BIND 10 jreed-doxygen, updated. 6c44ca2eaa94224d60ceac2602ee9c6846fabf18 [master] minor and trivial fix to a SCOPED_TRACE message. directly pushing.
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Jul 8 19:38:34 UTC 2011
The branch, jreed-doxygen has been updated
via 6c44ca2eaa94224d60ceac2602ee9c6846fabf18 (commit)
via b18409a1d6515152d107cd965e25ef58835f9f22 (commit)
via 616537c54eb13b434294342e1a0df06375134ec0 (commit)
via b5bbfb2f868f8f7401018debe275c39fc65a5139 (commit)
via 710095383c263973fffe58b050a4924d5053bd7f (commit)
via dd356abbe83f7c1275eba42ac855977499e71e44 (commit)
via c05781723ac006e4d193d9181bf46ccec998a5b0 (commit)
via 7203d4203cddbb6bf930586e2f3fba183ca12140 (commit)
via b250ca760fe74c901845861fbc2e7292b4349724 (commit)
via 3ac41521c2a1cbc43e3b6e0979eee46b6c45fa63 (commit)
via 7c7238ca556654cd2a0483dab5e7478fa7956a88 (commit)
via e12a932eadf0b33e26979cfbf387eb6788b97cad (commit)
via d77fde1d390e921740df739699dc03b48777f81a (commit)
via 78763269e5388263ad29b02049fa61c62829dbe8 (commit)
via 025f40af1c7877ac0ab84e0ee159806a57285c3b (commit)
via 27c6b8cef44f5daaa149ec72e3b7052e516ebc26 (commit)
via 846493567722add45db6e7296d570f8ecf99837e (commit)
via d1f68168ca58af66f09e51ced1a35334fb5fb825 (commit)
via 9336279b31c1a5dd9e50fa37d8178c790c4fdef0 (commit)
via db143fb8a98a13414f997892449ca2fbb07a0629 (commit)
via 4c0d2595196da373ca70a52663b7ec13842c940d (commit)
via 242235d6e7bb4e1893c0ebfc58e7a757dae771f8 (commit)
via baf3d8783ad1bc05bbe4db507325e9bfcd8d9be9 (commit)
via f9045d39a58a9b9287f3ece1022391a3b07e88d3 (commit)
via 0042b37bdc4e3929faf3d2b7862dd79979d60aa0 (commit)
via e39dbc26a1aaecdff6809be620a91d4771e5af9b (commit)
via 88c4d5e241d0dd670eca6f9a4981439a08924704 (commit)
via 2e8473390d5dd2274aedd59ba3934c597f94b04a (commit)
via 05164f9d61006869233b498d248486b4307ea8b6 (commit)
via bfcd0225fe669dde479dde1146612f7c067a817f (commit)
via 2c936393a16d79fa3d4bbbdacc66884f7d8d3cb9 (commit)
via 3383b56081364d68de8c29fb34698a7651c50e05 (commit)
via b60e7347e2b97d913b386b82b682c8c7ae2e3d4e (commit)
via e3d273a6048b48b7b39d55087d3a6b7ca0a623eb (commit)
via ea2a4f906dc3b5bae939a0348a5f82fa690bbec5 (commit)
via 376fd546007d1bf592e391f11b5fdf08993914c2 (commit)
via c0442d5d6e70643e10e639efe1162b64c44cce45 (commit)
via 95e0a157b2ae5a4027c06b7bb1aec04f9eb883fd (commit)
via cdadcd5d6a6bd594fdbcb9efe628067879623df6 (commit)
via 2ab154b25ceb6264f87ba6a3ca139ec44c7db275 (commit)
via faa2bca6751f7a8837e8c593ae723ea81fd40b69 (commit)
via 2e29bef63a7fa200af54b9b0fc69e5cf2573b467 (commit)
via 258663014324e165ea95d581498268915d176141 (commit)
via 4d81613695a03b3d39adb5b54822dc1a07a37af0 (commit)
via af1dbc5024d5b3289841868ee49929ba4f4d3f50 (commit)
via 03d707719016e3d3a6d98b3fb9eb786c90df69ec (commit)
via 803a215c662c5a692c3b057fc7c0bae6c91b3587 (commit)
via 18dcf1d0ec44f4ddf701d5872f6d5e493d3c4fdb (commit)
via ac06e0bbad2fd39f8cc77fac06fc397be14f92c2 (commit)
via a6de7efe8fbf314c5182744d462699283464d9f0 (commit)
via 3bd81bcaed4c9c2ca6c6ed5fab00f350be5c2eef (commit)
via 756a7d1a0816670445219db901364074b79f158a (commit)
via 1a915fef55d9902cb4e0c5d077e9c602101419dc (commit)
via dbe54369eb40d9ba95b8fd77859a243f076b5966 (commit)
via ec45081d781ae19de834b11e000acc35415a8f30 (commit)
via 46e6d4b1702e5c30c8bcd33e7fc73733872bc620 (commit)
via 612a96ab3eea34e232fd97e834599745401b73eb (commit)
via ee8c5f5bee062c8943e955184146d839c05bd2da (commit)
via 9ef0e6eeb1ec8477b1f6867d118d4c599f41c0ae (commit)
via b9755c94e619471f8d9769c7c0d230c1e40b9584 (commit)
via 05c064b4bd3ea51553a34e37099aa1053c141060 (commit)
via 1eb10de8b47aaab24b48cb0e109cf2a3bbc22860 (commit)
via 690a04dcd63dce08a69e648223320e922f82b3d6 (commit)
via 1b11baa7c10783eb9d53c24c7f1deb1c0a424105 (commit)
via e3ef2529a0582f0b146ea7326cf2d52312149cf9 (commit)
via d2dc7a3ef911a5ab83527753f351bc99440d60fe (commit)
via f0445f392c1e2c99acfe9117ad36eef0811bd68b (commit)
via aedaf51b32a4b31d697b18ecb914af3889d13c2c (commit)
via ae5aa5618516d4f894bdf5d2aefed76742069644 (commit)
via d191035ad012bc481ce0a4545f9b6819b897a04e (commit)
via dadf18e8c050ad6a5977fa32d563f31de99d3ac7 (commit)
via 3caca9f8debed45019acb731b5ef2f55a3479ee4 (commit)
via 3bbdd1e7d3f89b3a281900c75ceb0830d0cfd7d3 (commit)
via f5ae2264a57664aa6ab307865db72f1f740b80c7 (commit)
via 0a836aa297d08b3c375d245f50971cf4cf2760e7 (commit)
via f7af58ec51254d0586ee20ebfae4bd0f8977ed48 (commit)
via 22f3ad26d4bb70a03858d42122b7a648211911c7 (commit)
via 534acaf92fd8ba43488be7057d7a35623dcab0a9 (commit)
via ebe5d465d2995899aa3f95c944e0d32d09ec2034 (commit)
via 3f599b883384e9f180f12b06d704ef098e948c8e (commit)
via 348387b8fa68c25873b4ee50881738c9c0e83670 (commit)
via cae4ced00386d042535ec9b53b20e9bbc2cdaa20 (commit)
via f8ef5bcb1e8ebc747b32192348faae9fd32fdba9 (commit)
via 666d6e49e1cd46fd293b3fdce239e34588666ed6 (commit)
via 8b21629d234228ff9fbb7a3c5ad5ebeca4b981c1 (commit)
via 4191945aad5aaf0873b15727716d0a988b1c978d (commit)
via ebc5206327363f747822e7344037d9c2b76b8cd9 (commit)
via 9e72b16bcbfcdc819cbdc437feb10f73b1694107 (commit)
via 4355e75c9f82ea797d9353e82fd4d7c445c9e5c2 (commit)
via dfd7a01376d7b871cf7dfe631f5c96b4b2b7767b (commit)
via ae4e7f10136bd182db6d4801ace410e72574abf2 (commit)
via ca03a1f5156b0a68a2179e287d9af444c64aee91 (commit)
via 0caa1d89d3e60a80ab7517d3691a149093e32be6 (commit)
via e3b0557e225ad3e7a6b7d192b8820666d7b81d0a (commit)
via 27bb28f8bbde1dfc79030b0129a1c0405a8ffc38 (commit)
via f5cc3a37a155b140b4187a98028c1b8a5f79f9b9 (commit)
via 31bde63b8fae57143dd02d9db4798aa254494c77 (commit)
via 7e41b9c3e2e1ca809ed4ea6de67c843a1a0d7680 (commit)
via 4b83a53a37e3fa53a01ca0a6b4c9f7846a64bc5e (commit)
via 839d23249ae0d1722b51d87195b92cad40e6d78c (commit)
via 4dc1b4bf31b25256bac76baca6e8af71e11cc83a (commit)
via af6b421d426357550e818d6fee79dd559382ae46 (commit)
via b9ad6d4babd3e10f1c13140e53d60181681a5def (commit)
via 9c846eb19825f00e540c6bdbbe47c596f82b1a27 (commit)
via d2be8b73ba2b954ffcdb98d6ce0ee5f01fa161c8 (commit)
via 298f5f2ec683b78ddd7d3b57d04add89314a53d1 (commit)
via 0a4318b47384909ac7d922c065f38f46790db640 (commit)
via e0388c4bf5ca94c72132138edd81ff272148e2de (commit)
via a56ecdca722639891e04e89d39dcb5296f85d762 (commit)
via d7675f390dbfe0dd69bda3971e0fa5e4cd007b97 (commit)
via 4a590df96a1b1d373e87f1f56edaceccb95f267d (commit)
via 2f02427767832268aef89bbfed9b458a481829fe (commit)
via 9f6d580f95c2674864bd1fbc1955b9900d58ffb8 (commit)
via b9da1875519a8912f689a695bcb65b8d2f94f0a0 (commit)
via 44087e600d4d6163a2540c47c7ab0f4bdd7f0328 (commit)
via 8730224ec6ab50b04360caeb4ea72529574911fd (commit)
via acf0cc6205835bcee35ee276b5cbff285486a763 (commit)
via d7c2d4ab317636ed434265d885a433c8e669db10 (commit)
via 92e916b4fe00c6a544121616a401d0d5ea125686 (commit)
via a91e1274bc7cd044b9e6c254a100a0aff73dcc2b (commit)
via 3c3ef0445aefddccf5cebab6edb347536e3adc71 (commit)
via a88805ec3b7b2c0e3e42b91e0ffbd494b6c7457d (commit)
via 4ad7f97c789c2e2747506b7220bc279898aadeb4 (commit)
via e0744372924442ec75809d3964e917680c57a2ce (commit)
via ebf9a139d91ee05950e045fcbf2bf7acffe1755b (commit)
via 499ca0aa022f72674c29a2c66050a5cf1ee9f192 (commit)
via 376df1c9ac466742afad6681ff2e431ebfd75b63 (commit)
via b3a9a4228b95713636958a12f625434ef3cc3277 (commit)
via fb993ba8c52dca4a3a261e319ed095e5af8db15a (commit)
via cac876d0c8ab31aa9007411aacb5ef6ecda398a0 (commit)
via 1cbcbc84256489b664fe90553d3bb3579a33946c (commit)
via f9245031dcdecba55204916535555ea20374878a (commit)
via f9b5323ae8c8ffd7d4d2b69c360dc497b935d6de (commit)
via 3bb777140c5bc99775d5b8e0ea55711e227f0012 (commit)
via 8ec67a677e0ee2ecab48d112c3c5f5a5c5753543 (commit)
via 91bab51181c8ebc89e0ee2e735cc6e851eb6b93d (commit)
via 041c3ec8a0768c513131f47467652ab2aa75a07a (commit)
via ab31e2fbf10950084d9cda73c0b4fc7d36296817 (commit)
via 55c3df5bf10aac9f15735589142b197e4616e0ae (commit)
via d6cddde949ca816c12c182066afd5057eb726b86 (commit)
via 95707bad7adcc6963baebc1b7e3b005d1b8e316b (commit)
via 8081aaf24a0fb181f2847887ba6347d7c97969e5 (commit)
via 68b13c93af35910580e0ab42ad2081b212e8d5eb (commit)
via 0c9a11c009aec896872bf4914bd5f01e40a5e0ae (commit)
via 373bed0a95cbe38e67282e7ccca8cdd8fc2372f0 (commit)
via a01f7fc667d5fe05428231479d8e934673b40407 (commit)
via 8266c873258135c402a7cae57e409742a2cc1ace (commit)
via 5355f8f14648fddf13cda7240530e7b4216da671 (commit)
via 876951776077fecc59acb33574bd155fc9259dbc (commit)
via dcdabc780fce8d02c9263f8e98f03b29bb4e5210 (commit)
via 0fad7d4a8557741f953eda9fed1d351a3d9dc5ef (commit)
via 36c080d7a211dd69cc3ca37a8221f096828a2f4b (commit)
via 3e861eb6aec036b3c5a2f6a71c6ff3adbdc9a55a (commit)
via 502100d7b9cd9d2300e78826a3bddd024ef38a74 (commit)
via 6154d0aa83098b5b05f2c658a13bb1862139d618 (commit)
via b477df5d4dbce5b72ebd183b83555f62aa3fcec5 (commit)
via 701c0d6d7c484c2f46951d23fba47c760363b7e4 (commit)
via 5d5173ef0cc48d206464b39f696d03bae9daecea (commit)
via 7cc074aacd5159778111fa4cbdbe1c89e6a4e51b (commit)
via dc087934c1a1946cfdcf63b49a70aa0fefe6b282 (commit)
via 6f7998f9a209e9dd7b3ac80793098dfd81b489b9 (commit)
via feed2b3537a4e57e4cb55232242c6622d1fcc654 (commit)
via 8454952f81b704bdf5f28d1e105ed32de7797d83 (commit)
via 21a333f512f2a11ce0c770b7d72aacfb623d0c14 (commit)
via fb032e397153a63e4f1bd3b9b7fc1a89c01e7d6f (commit)
via 9395f12c95a2519803a0dc15b56424c22df88c84 (commit)
via d57f30ffe93b7f45aa6492ea1fba5d594adc01df (commit)
via 690dafd743f765f04b21d3ce15ec0a63da6a53bd (commit)
via 251a32a1fd1e7be23d59790e57a4b40fbcdceae3 (commit)
via 6bc6c57d5761ccd2ef65291e81bbfd995b4758a9 (commit)
via d137040ad98f7203cd440ca8b449a84f048af6fd (commit)
via 440524d524bde6ea17ec64b427e259f3bd08757a (commit)
via cb737e68ceac8238844fe2b8b9bc7feea23b4004 (commit)
via 6b48fd891428674ecf5eaaa083bcf5b843deabc5 (commit)
via a98a90d8c392ed3bd0ab51d644568cf560574112 (commit)
via 421d19914af7d7a6f886c1bce084324ce9407b99 (commit)
via 0f1b7a45520517a40b7b85d57d461e20e81b7aa9 (commit)
via 885a4ecf9c87b8e3a028b6488b0e6b853365edc8 (commit)
via 8d5a5b95c85af1f15654fe164f306fe21065ea73 (commit)
via 77367a5d67709b65afd8689159e5192416326cb7 (commit)
via 935bd760ed4f39213f8db8eab730bf41dc217da9 (commit)
via 52adf933c0bed4753a06632b25a46055d23eb655 (commit)
via 4fe29ae03d1ff8f6d721b42f4bb356702110c4e0 (commit)
via e3fa282a59eea69c50dcb9354e568a8503510511 (commit)
via 58df861a260fdf06b17194e224fb8c1bd03f0392 (commit)
via 77e3f8cf3f3fe79c7dd5f92f30d70c47b515f4cd (commit)
via 4a88c75d4d1decc3b3d5518bd12d592c118a7fd5 (commit)
via ea1b177b5503687f974252d185a9543066af20ed (commit)
via 6535d4fbe623226171b27730f60161436d0433e4 (commit)
via 0f4c693c3399bd9ecf2d2a5682fda8ed1eb8158f (commit)
via 877e89713ad2398b6637b843a22c3b12607fe5bb (commit)
via 33e08ca107c127d5c158882e7f2e86770a48c572 (commit)
via 32fb3ad97a7ccc65ef391b84c8f488d4ea71e963 (commit)
via 04e7fe3f480462d288c17bd121a368b74292cfd3 (commit)
via 354fcf46bf93f1e2e317043f2998a8b17f22fe04 (commit)
via 21acce853a4269f0db76dc2768bb7c5107b1b7d4 (commit)
via c021505a1a0d6ecb15a8fd1592b94baff6d115f4 (commit)
via 02aa9813c1f6829bb9089400c5397f3faba7d9e0 (commit)
via 3017593b63f34c4bc69494be8c80327eaad5d922 (commit)
via 62bc6cce6fe7343c4ef06c7e690939fd0aa20148 (commit)
via 77c17d3f03de64646da89de238288a22c49e3eb5 (commit)
via 6f8383136ae83eb439c71a70c4bde83524b72c5e (commit)
via a16c7925f9a00f44680e2ca984def99d6bb3cecf (commit)
via 12c37af78f65301858be28679695a9e818270947 (commit)
via 83f58ed580b457a424d4279d1deb8e698a0fcc9d (commit)
via 34698cb3c81a724da952e6d0059572c4536e3a0b (commit)
via 0494c88638d05f2c2004cf897f6360b06400b6c1 (commit)
via c58fa9e4c5aa486bb270681a45a4f0f7e04b4139 (commit)
via 89324744df3f73de1beaefb9420aeab5f9ff7824 (commit)
via f9070aee950581a47c0916cb1f3b48cd4bfcb7f4 (commit)
via 0c1589a0842cefe0793b538c53c1cb102080b570 (commit)
via 166b4747ffadbc6b3a94647f1470ba776aeb8c51 (commit)
via 181283a52982eaf9f8637bd09a2e1dfaef5ce302 (commit)
via a46b0d4ab62e16c4096bcb8790659bee93205470 (commit)
via aba816f40efe336b20ae56871a531c87117ad24c (commit)
via 217c09751aab2dc84f49e7942b2c081a0381945f (commit)
via ea15d26afc9ced4a11aea6733ea3df0969c5618b (commit)
via f685e5c06c382180eb1775bce714ea60154b08f2 (commit)
via 5a19ee14367d9bb796c8e43c034ee9f327052c86 (commit)
via f92d30bb55decf9ed4d7cdf10231dfe2913ca11a (commit)
via 461a9d0a1e896e0a1b676c6873f74404d5ab95c1 (commit)
via bc81810505f7263aedb8654d139510058c251626 (commit)
via b57c51e6ddfc6770d5c66eab5aeb1a5238e5a7ea (commit)
via ddb1b2241fc03a1d08dea42907ee8f859d3b2f46 (commit)
via 0b838ba0d3c60203a52d1a918333846116e607cb (commit)
via f77021d7838e33e1662b42776ccc49be4435b1f2 (commit)
via 632cd6151b871e060d09a79f6b8a283cc0ab469c (commit)
via 7358d4af5775ee1bfa6099f63443d2ad27347f0d (commit)
via 81a2df84a879ca5cbaaa61dffce5c413d920011d (commit)
via 59b380d3682bb9fca26cae2c70c6c49934823f01 (commit)
via 8b2247a6ae88fbf16bfd65852feb0216a4ea4dac (commit)
via 1b01a9d09e5ecf21ff8bd9cce1c20372846a775c (commit)
via 735f817c7f66813135b4ef576c117aa424a5bdad (commit)
via fef88019d325474471a353304499e7919023912e (commit)
via 99522dd887762e71cbf4d895486f0e2f915eabda (commit)
via 999736efa5e3aaf06949675c4f77e1ef9cd0d71b (commit)
via 9c862cc45629b24d0a704926d339796926c692e5 (commit)
via 85d5708e2c44e04b1a148610434de2c040d7142b (commit)
via e6b3d50483fb739da2ca83e493a1c30043ba0464 (commit)
via 4f82e3bdef9612a632da55d3ddfd02326d358160 (commit)
via fc29e92af2bd2cfe8fa77dd311b9382680fd6324 (commit)
via 78cffeb00933814658da0867ada0209403946b51 (commit)
via 6661b327d26dc4e090772b0dd263ae8a7c79d3b8 (commit)
via 868d5912e25819663d9f1f7690bf8a33dacd9b51 (commit)
via 1764a1eb64926211f77067ce98372aa4c748bb8d (commit)
via 9129a474d3289157a4d8eb761383352dbfc2586e (commit)
via 417893fc06dcd5339e2cd0278a6badbbe847d6c4 (commit)
via dd4c4405f56ebbcb74d8f792ad528daf9b2bc79a (commit)
via 8e715d5202d79361622e89ef11a0d433558768f8 (commit)
via 5ac59583a36f1d83d35ba8d159f87bb281d3edc5 (commit)
via 1368c87b932734919bc0f392b351651cc6dd03d7 (commit)
via 3f15151252dd734210582a2ae8923dada661231f (commit)
via 79ec6c320ec5c24036856fd6b589ba8bf8b26ffc (commit)
via e53411af37a32d0c9b14515bd90c1e701c69f6e9 (commit)
via edea2125fa0791f920e3dd9e45c8aa0c9bfc6eb5 (commit)
via 8f5fafa643f2d908b9e97b6d08aeb55c4b96addf (commit)
via 01f9c1c0adfb37d11133c87056161f1edfba2672 (commit)
via ac7aaa887d827f8bdf1c2881d245cc655c6847b7 (commit)
via ebb6493b8ff763d42fe99438c8befe48c381b4aa (commit)
via c786a61641a965545c2e304b1c946afdedc6dc1a (commit)
via 1efa5d9d7f699cc3ee636d4e1b50b3fb3a863180 (commit)
via e5251c4886f626e6ef9f6ba82771c0e949e0071f (commit)
via aaad42c52aed2c3890378511ecb2f97a3731d23a (commit)
via 4beebf47805d0c3f80872e8f690f09c1658ae4e2 (commit)
via 792c8b202cffc8fed726f10b3514523b1fc92469 (commit)
via 8c624c6644563ed9c4fecec8b0b5f5dd115fe7ef (commit)
via d1c7f98e910bd19d21a649386f1a8066e4f41677 (commit)
via a90c8a06056300e0f9f5ffdae72b8a2ba26346fc (commit)
via 30570ab2d917dc6adec02ba272ee50c17124b688 (commit)
via 59908b70a929baf829202197d6e7ab5a3557da32 (commit)
via 585d1c63d6d0126607f424571e38a4a60683cf4b (commit)
via d335ae50bb855b7b302dab852005385c0227dcfb (commit)
via 8034dbfe87c45eaa2c0aef0e715b86fa79a7c4e3 (commit)
via 0ddf0f5fa4d9d18599a1642b9f87caaa1f463c5e (commit)
via 5a75094dfdd5f2307c4a1669e05db70355b08682 (commit)
via df5bad72ac8dac07a038f29823a1938bc9bbe72c (commit)
via 24a865aeec3048620dea967cba9bb1df28cfd052 (commit)
via 6756ff6dacd40b74676b4243bc12ea02a43f3ae9 (commit)
via 4e025223cedb89d5dff5c250ab3cab42bfeb195e (commit)
via d4ef4b9a0cc72eb9d85c6fef4aeb4b2f90b2b590 (commit)
via e560672e5119540d6e6860c177a9b969e5a71fb1 (commit)
via ebc9aa4dc554ec8aced4413b47a0668f3f5f1da1 (commit)
via ddd40bde5412d11fb4d320958f26572797442b74 (commit)
via 4565ca4899e702da0c515e11d614cddb3f483a7d (commit)
via ca924dafc7902bbc2a22660fb00f70c0d34c6471 (commit)
via 679d8390f4fb1253ac26a86a47a9279f3d88174e (commit)
via 250100ecf6468ad2cbc47663d1f6e83f1fe10f9a (commit)
via 5aabfb971d4338d3e488d05f8c06a9db973ede5d (commit)
via e9a1e75b3d83fe811d9a4e32d6d9a21f446a37d3 (commit)
via 6722a4d52b519ed768fa70b31cdd10da868cacbc (commit)
via 9f2167d3b5878a5709fd9f1ba2cd200f29f057c8 (commit)
via ca9e1e2997420dea3e3b14fea010ab0af3d75f32 (commit)
via 5fe9e2f204d67a9ed65ab8fc2a1feb09f6700b5b (commit)
via 13eb9e8620e67d7c617423ac1992a720ecdfbf7d (commit)
via bf41f8dc2265ca0cd9ffb8b8c11047291e69ca3c (commit)
via 926a65fa08617be677a93e9e388df0f229b01067 (commit)
via 1c7930a7ba19706d388e4f8dcf2a55a886b74cd2 (commit)
via 61b01087195d5d1f875f01c5fd2eac5dc61d012d (commit)
via 84fcd68d77cc4aba23721e234622c33666e96c49 (commit)
via 96f093b960839b26ce37d9ff470933eed9c2b135 (commit)
via e828a215cc73946fa3681fcd88c3ef76b68272bd (commit)
via 0bea88f134736d6fd2872f77feaf309aca6c1bc6 (commit)
via 406cb1fd4af84fcbdf8339cf1afdae2cfb3b7946 (commit)
via 55689c559b3ac60765940d64a5b51007f94bddf7 (commit)
via 925ac83b98b02abec3f7f2a70b7c83170f851e29 (commit)
via 3f47015eab1abd9c7193a9e740f794c6a718c9f7 (commit)
via 4064b389d13d2861083499517f51d89492156099 (commit)
via 926985d03e3486f1a83615dc2794d310cb2cb520 (commit)
via 189f58f73fe02cf2729ab26d6ce8ab6469e82a1c (commit)
via 1ab0f2e8448a20674bfb8d12d463e5b3fec3ac6e (commit)
via dcb32f7928972c3ebe66f13a08560a1e19c62866 (commit)
via e25099da714a10dd3bc24be0002f9174fb9610c9 (commit)
via 2d39d007d30f65589cfe4b671dc91cdab70ed107 (commit)
via e9798fc8931856a7eaeee37155600146d7dc7c57 (commit)
via 59e01319e369a7c8e4f9a326d603dee7e3924c6b (commit)
via a6e68091deaf13986355b8763c7348b2da71d7d5 (commit)
via ad5a633a9e77e561675aca5263853db8161e82fa (commit)
via 362d429a32fecd1b59f309466e098935242f9054 (commit)
via 6c5a9b252b7bc062ed807aff342d0314811b5bde (commit)
via 8320629b004d5fc8194afb5d277a0d9e01299121 (commit)
via 0eb494bd7c49f0559c870d8a687ad0552f2feeb7 (commit)
via f79a424a36d3a5896c43c5cae5d88d690ecbe90e (commit)
via 75bda54b2b5cdf06f334e72cd554b616a887d1cf (commit)
via 2b97fc4f4f30bff13b94ad9b25766b4a6b2f6655 (commit)
via a1301a0545acc48bf2f94731cb26577806e3c383 (commit)
via 8bb79638bc658d8e57b15ae1b16d28a08ec06a69 (commit)
via 40cf6abada7f06648643b14b9b7db21d0fde3b27 (commit)
via 81b49bb4d72fdfb5db8d7ad5f9b086c489acdb86 (commit)
via c4430b49b30dcd74226d272fa3da4812afc2c6f7 (commit)
via eeebde9d81c4bbc4e5388db5cd6148ca3589b91e (commit)
via 4fae538655882db7c085dab798b4fb29c4a9d8f1 (commit)
via 8c5e6268927737a472348d1ff8ecb2201c76b98a (commit)
via cda19a7cbc56ddd67c7d19ec7d072a64477d254b (commit)
via c65177c8ea0dfba3aaa84ea1bf2583b2d818d23d (commit)
via b5cfd5e541d4bbb7f13ad93392018711e19ba0e5 (commit)
via 430f3e516852ee9a8af655626bbab16b03e4cf72 (commit)
via 1b47d1cf3e0bc8e3c6166d049070dda2f298c7bf (commit)
via d86526508726fc2941a7d35730013b75f49ab4a5 (commit)
via a2158e5b2c17043f0f3aa194009408aa73bd62ce (commit)
via 85b06e8c212c9733cc77e71d8a72c72161dc34f2 (commit)
via 4f87326ae6c17e26769b4ae276001b49d5bb3561 (commit)
via 06c9c2a763326d4b30ff9448f726928538fba94c (commit)
via 8d07954792d35120580d9d94fedd642d4797cd53 (commit)
via bc8bb2e13305ae879b31a6accbd3f5f1855bf327 (commit)
via 112aa5ce69ec2440db83d89196144b782f064564 (commit)
via 70af8c7c72300e1afe1974de22c117ff5566487d (commit)
via 03e690228b6f5184d67a4ff3de56a861fcac9a23 (commit)
via 3665d7750795ee247864c5619301bd1638c31175 (commit)
via bb22c63e7bf7544d5a765140cc29b87fd1e2410c (commit)
via d749aee2ec681e0304dd53c63f276af98edeaf31 (commit)
via f9a545ca4c82e51fc7c47793fec34eb5deb19e46 (commit)
via a4f1f8de765810aecff1194c74a108682e3de28e (commit)
via 287edb431de6ae5d7106dd4e593a193908b9ba9f (commit)
via 2f0a32d552d19ac9224dda91975fb128487714a6 (commit)
via 99d7c21284686ba3d021a6d09938b82ea56de783 (commit)
via dae1d2e24f993e1eef9ab429326652f40a006dfb (commit)
via ed6ec070b25a8995bccb3cec1a63cb111e06a6fb (commit)
via 97131f9739d60c41a530a52c5f2a2861ba68637e (commit)
via 8cad081427cd8326318ef1a0dc81c1eefaf73d29 (commit)
via 309b24ff461b623770e950d6ff12654241bdd39b (commit)
via 4fb4173e7538a13246af1ae75afa9d783630da55 (commit)
via 15556181365ba9b65c91c136ce00c74df06da3dd (commit)
via 0bfea71bfc68c4598cfc0ad936a0507e6423e014 (commit)
via 0dc7060246ec79dfb0de03aca4c5fe8ded3fbffa (commit)
via 52d165984d1a7784a1a6e0a3b845b19559698203 (commit)
via a948a71497441782d409e98f4068bd04cadf581a (commit)
via 69da5b8b1b3ed9f6c6e21cae8a8a86c8fbf83a76 (commit)
via 8f95286e55b815323110f79d92f338878930509f (commit)
via c4c85ce1694bd421912d1902f2d614c15bebbea1 (commit)
via f78cf6ebc22712c470da4af720915b09ae8e8ebe (commit)
via e31eaa10fc2fc2730311596b7ee4ac16050efe62 (commit)
via 4c250f85ed6ad7f697c42137f1e67aadacf73dac (commit)
via 941eceae0a54d023dce0c43757b0104b8adbcc9c (commit)
via 79143dc457f23670d860a2fa134b13eb62db490b (commit)
via 80dd433545aecf82aa178365dbc6e0650e12907b (commit)
via d867ca0fabdb5398d6a964aa393fadf678af2bbf (commit)
via 3eb58c78cacf7686435e963d423c6c035a737bc0 (commit)
via e90d2063e0bd98767fdcd38962ad5be6f2eda68e (commit)
via 0699e756aead6ec1b3a80f5e044d8c3cb35e3280 (commit)
via 384501f85cc9e66a686a96e349241442af29a56b (commit)
via 707e700d4861b2c47235183ce6e98d985819dd2d (commit)
via 67a88d3fd748cc42730e142cbfa79d0b7fb7a813 (commit)
via 42177c3c2dabc2b46adb7133374a4c2da46b1f9e (commit)
via 910caee23c188c5b9575d87bf479d9caa3ab8d07 (commit)
via e1bf2fce994c07e02466016d94007e509f2fe478 (commit)
via 179a2105dc0a9c9341d4347b711b62c2bbb9ccfb (commit)
via f7a8e38c733ab5695bda72b7b69a803c8d98c80b (commit)
via c1ebc31d07e2c04c0158fbd3e7289db650b41c1d (commit)
via ad3f4a5e40390f14762648986dae8430760202c2 (commit)
via 62318bb5e15a45164d9cba4a03c9e5583e0b6aa3 (commit)
via a9d549be7404552a13a95db041e7e1da64729341 (commit)
via dd3c0d1df47590362b21e7d582df513a98942a54 (commit)
via 3eb8c8e08c993b1458a6d79f434e0305936bcd14 (commit)
via e580e10deec55a34efd3fc2825bd80143af67d4a (commit)
via de063908e81bac570c2c485a9d91d496835fbbee (commit)
via 9723ecc53f8afd751d66d4e2db24f8fd05ffb467 (commit)
via 6fe98e3c2a669c9dc779980426a81fbe1ddcfff3 (commit)
via 576f337f42e5c72aec48d8ea1d36ab5059588301 (commit)
via d3a000535b506ae8af54119af3dde7d14509654e (commit)
via 2048643593b3e9c8f34af40bbd00342c2c0c1318 (commit)
via f3813c74f58f13c2b93859f0b4e07788af631960 (commit)
via e45768f4b916faff9ace1f1da25e423eba2ab430 (commit)
via 9652c9fd3f69836a928626ca9fc940e80e725279 (commit)
via a5572545c3b6106052898e016757398efd475dd8 (commit)
via b37a785ae2a03a7c84b316b67e50f77a827a81e8 (commit)
via 0b99dc120fc89081901be0b884f77e02530b8588 (commit)
via fde3b8cb6fc790249bfb918971a33a0cce7a4cf8 (commit)
via 3f6fb6a54d85ed33bb23aa3988934166b17261c9 (commit)
via 356ffa38fc98b8587499aa004eff6af103cfaa78 (commit)
via edfe1b966d53caf3ed9e17cd525b0d94beff0aaf (commit)
via bfd50c768ccf03b2e4f3d3ecbeb5fb344ff79129 (commit)
via fe244439fca78ce558073b856dac69290baf67c5 (commit)
via a6c21ad6d670896283a941b5bfb233ff5987c50b (commit)
via 30a20a04aae6b14749980d575cc69180998ca0c9 (commit)
via ae21ebb0f609f8a2aa8ffc3d4b84c465111ec2c3 (commit)
via 7cf66b7e44e389205ae4344764fbf136550854ce (commit)
via fd9334b7d856c4f748919d035b2a4ad3c85b545b (commit)
via 0c3b69c6e170bee7dd775090af2bdd1cae900080 (commit)
via 0fdc040591f07f5f876ff2a16ea363e9026346ae (commit)
via f5edd310465966137f0cd4e2109d90f7e5d5965f (commit)
via 46f82ccf0e1984c8b195a5492618f5d065682c3e (commit)
via d324bf65587ab92ffc8a5a6f94cefc2fc78a52f5 (commit)
via 49816e5010448c5c966ba3964934adf68a972400 (commit)
via 50f16fdb5e03054e470dca9aba6408062c198fd7 (commit)
via e59c215e14b5718f62699ec32514453b983ff603 (commit)
via 73ac6b09eeeebcdb03965076d4aa8a8a7a361ebe (commit)
via 463a593e465643f157fe806b3fe826b6ed593750 (commit)
via 6c92dafaffbb0c1ff83ffc8a1004cc40cd500310 (commit)
via 2826df63af5ce079b537fb8beaa09b0138b7c308 (commit)
via 3cf7f4bf3f7c6947bab97f269172236dea5b9d52 (commit)
via f28bffcf3e0369b89631df178865a2de00145f97 (commit)
via 2a81a3ad5a7fa790d2b03845b81380f395fb5ac8 (commit)
via c55ce18325b71ece62d213ef4ab60e6c95c07da8 (commit)
via 86a307f08882d02ad443e848e096a30ca14ec918 (commit)
via 9c2b48c4baccba83732e2586eff2b7ee64c63ac9 (commit)
via c13544e653c5ad96bcdc4ffb4fa13316fd4ac9b3 (commit)
via 1d4cd68ac7b1b5dbab042a1d5b7b82710d0a7769 (commit)
via 261450e93af0b0406178e9ef121f81e721e0855c (commit)
via 0bb032a0b6ce7ca30fb250a647b81394faeeb730 (commit)
via 99785047d78bc833643292c1b795ea24e7916641 (commit)
via 6135be8219f25c87c5a551354b889e8c4e55fbfa (commit)
via 49ae23f163b8e4ad45f7146f95235253691a0f4b (commit)
via 1ff545f50926224d095e3809636c642219ea9078 (commit)
via e47b0a39b7849489e6d9a167117ebb3be5eea4d4 (commit)
via 3e1fae0b23d4df32f239e5aa1350e40557d8ada4 (commit)
via 2636154f904f6123de434503ff72e65e4a27cbae (commit)
via be1bc7ac71892b98cf5449b9df388ba7de462e10 (commit)
via 4f714bac4547d0a025afd314c309ca5cb603e212 (commit)
via 6ff5cf12901e9322c4df72e5484b2ed9cdfe9a4f (commit)
via d4aeefff3273af7249ca459eb7e6bc4d358cecd5 (commit)
via e95a07de2e9eb88d461efd236664a7d12f204e74 (commit)
via 1302962baac0113c3a9ff10f1317271ca060a1af (commit)
via ac65c96b59824fabc738a300ad2a36332e4ff01d (commit)
via 3255c92714737bb461fb67012376788530f16e40 (commit)
via 01e08ad7fd14a1683b8eb21f4aeb5e8ca8e54a03 (commit)
via a8414d057fa2ea8406a8911492bf91fb4e6d8166 (commit)
via 91920024ba2897735eb1228a17a2cd2bd0f82c61 (commit)
via 5e0454234b2e1975d7ecd79f8e40a43e6782f968 (commit)
via 4ab86cf2cd1a3133f8a5e1dd529104cb641b2ac8 (commit)
via 9fa2a95177265905408c51d13c96e752b14a0824 (commit)
via cc5c0e24296cd58e1d6b04198da3e99af2c3071d (commit)
via 8e105c57dd7c082a36d4710e5ddf27bda118adb5 (commit)
via 679661cb33158ce088dc3b7a3b5cf2fc9a7dad29 (commit)
via 31a30f5485859fd3df2839fc309d836e3206546e (commit)
via ec6aba3f5e5ea3f3e9e5b9d70b162fe6e0f33df2 (commit)
via e2e2a6756e459bb49446c22b89efa07306c35f21 (commit)
via 18cebf0c6b77fb88486574e1fcfcc2b89d52b279 (commit)
via ef5f41fe04fc405c0db1aa7871fc90fa5110acf2 (commit)
via 9611300c4a297500c0130d6b372be3bd409c6aaa (commit)
via a739faa43b38c39ae49950f3abc79d66ac0ab545 (commit)
via 078162ae556edceb66899017b9a3e613f2886475 (commit)
via c589e8b9c1f1d3b4fd4959bae8bfae5be04ae8fa (commit)
via 6997749029cc8d634eba96d8fe8d222cb35ed41a (commit)
via 2e386bd4b400b73103900bcccb8b5166258448a6 (commit)
via dd46abf659db56f51b981ff675ef02ea92d70ed5 (commit)
via cbe4f685dbfcef07a17c6cef56d35abf12fd677b (commit)
via a4b89470c575878fbd0d3e1bddd45ed2a3289e6b (commit)
via 0c8063199fe37278da7fe03adb5723deb4263f82 (commit)
via e08c212ea82bf00c90eec566b1058862b82b78bf (commit)
via 4fd41636ae9c11a96b3893cbc939fd4854b64b56 (commit)
via a149f9e04c33ce6012a4a3e837c98db6d3074a44 (commit)
via 4e18854902a306ea6ad390eb90a9f43f49c8222b (commit)
via e77d93ac97216b2b67124a605649487a767e8d09 (commit)
via 63f62558f6490de53d57504ac48076165f18b9e8 (commit)
via 631651ce7025329af4264f8c576ef77ec3339288 (commit)
via 754c4ab6e60afe6f90ce7afda9670efe3debe13d (commit)
via 461fc3cb6ebabc9f3fa5213749956467a14ebfd4 (commit)
via 52cc6e6c2a7d61f93e17525b161b7c33ea4ade98 (commit)
via 88119987e9f33fa12243eacee75bd9ec4c99f936 (commit)
via 25cb92eaa2253745fd27c957e8d576cd90b7b244 (commit)
via 73487153601d7ad71f04e6762cabd3b3712c860a (commit)
via ec4d7f29e1fade106ce33ebbbe0147330584a784 (commit)
via c9abaf3a9e3e78a875e9722649615339dfb75047 (commit)
via 8c6a400b68d28b6c9b37b186a1313eece27b8222 (commit)
via bf384859fca46c07f2e43c376ccaf9111bd3b1b9 (commit)
via 83e33ec80c0c6485d8b116b13045b3488071770f (commit)
via 542a15d604018ea73a5a28f26b30b92fb668e399 (commit)
via e4543dee376bae0e459f9008a2061f2a2529dcee (commit)
via acf47441057d4ce66bcb84b67038e1e99e822c72 (commit)
via 70d184ad5dac430b1591166893689ecb5b774777 (commit)
via a215fa328b934f3d140e433725b2d7a540b826f5 (commit)
via 795329663bef70bf69fddd14267daff57abfadd4 (commit)
via 8a83c13c2037e69e64474131916b6c53ececef34 (commit)
via cd4ffa3493b2e1f3c53e83b60dd4f7d03dbb518a (commit)
via 717534f5a10d288081a0d8b40644794cc1b38861 (commit)
via a9d0e238a2ddfc35c772e2a72a3c73ced70538c4 (commit)
via a86e015d7c5fd0b0d8286523aa5d7e3b036f8589 (commit)
via 92113f50b12c221ca1db5dbccd51d762be5d4f6e (commit)
via 16717b1380deba97032b3ec698c051cbcb032263 (commit)
via 364d07e849b394da94b715450814ebf559ca9cb5 (commit)
via 5638c66218f33efb7fbf7e3700d49ff7b6b0f090 (commit)
via 1ef6e04be1902dfad65d7df155756ab5a73aa843 (commit)
via db8fc3e3a28dc8681dabafc868f2b38763876e7f (commit)
via 5705a62ebb133531c81ff820edae5f92774dfa43 (commit)
via 7855203ec77dd6165607297f90d0be20734fe692 (commit)
via e006dc3e92ac9a8bc8792bdf9c26fb18f064f4f8 (commit)
via bddb6473013e4cc91d4a76f182702d0bd1856f2c (commit)
via 2e5251ee15359eebf7d081c265948713222ef850 (commit)
via 3b2040e2af2f8139c1c319a2cbc429035d93f217 (commit)
via c9ec49aaf765eef5ba3450ad0b60ae031fc571c6 (commit)
via 77ac9b0e168fd353a8d4826e4aa46fcf98b377cf (commit)
via 7f8b0c9ad5ad478b0ed0df4a8a9138449b6f63b4 (commit)
via 2d1592a9e5aa5d212336af266e07ed20e8b56b06 (commit)
via 0a6fabd5e27753d3cb3827f05c3d279ba31cb1c2 (commit)
via 7385c5e065fe7ab4dfec519d97eea2788306b00c (commit)
via 0ac3a688424c0f5da2e0bd7086dfacf846c47970 (commit)
via 78fa7f965cb3bdb319a88028960e69fbf8773977 (commit)
via e245b3b40b9508a4579e0c9506df0cb3f8bc208a (commit)
via e4d3b7c6dc04c8dc443610bc45bafef86519c92c (commit)
via 182328dfa329c085df1f334a32d68ad925dab476 (commit)
via 5f20e1ed0833a7dc5816cee885235c42652cb517 (commit)
via 21b0e9d23d1c72940fb01f3dc9ad6c7975abaf5d (commit)
via 824f8da33c64169ea6e768f899ba7501a3536823 (commit)
via ff69cf49ecc375485209da7caabea778b5ff1ca2 (commit)
via 38b3546867425bd64dbc5920111a843a3330646b (commit)
via ec4ebeb8ecfae6b0e3cc6fedabb7e2f84509c930 (commit)
via 48e10c2530fe52c9bde6197db07674a851aa0f5d (commit)
via c4a9bf2722c9650c1c0f4290670ff33c0b7af87c (commit)
via 479cab63fda13cde6707a71dd16d9bb4c1f93b4a (commit)
via 17494c7a2f58e91cdcaa8b6e28bb275d49f25437 (commit)
via 7c4d8ab93f853f7abee812976340008554f25fb6 (commit)
via 684a395b40dd02ef04d3a914b7f90ea96e808f5c (commit)
via 4e058c0c696584bac4b299f241ca5e74d7121b06 (commit)
via d22ff00401ed90df31342dcce12bb4ceb493f232 (commit)
via a61bc91e4109168327481710f17affe728af80d5 (commit)
via c58ee77ef68b2cf49db6af85ede886580ad2b6bc (commit)
via 601d7f0170b1f9f929acadd3c37d60c5876ca7be (commit)
via ef8b3f326fe9ab9aab0050071c6f8975b8ecd354 (commit)
via 51f6bda14d754aa6aea474300c8c51f15f32f0be (commit)
via 711a621387d48993712031be9164510de7b27054 (commit)
via 4948469dafe0a5ff130706ff2fb13676c417a538 (commit)
via 2626464bbbb35e29f01a7e5a532d06da8feef837 (commit)
via 83a82bdac7711760eed682b91f6be4435606a0dd (commit)
via 616019706c0101316835f85843c59439e20a1c8e (commit)
via cb6be8450ae82e705dc4c65ca4415b1ed77abd6b (commit)
via a705f09ca7cf67fdf8947716d27e49f1f9c58366 (commit)
via 9f8b63669172d090e653b68b18b162539428663a (commit)
via ddcbd508353cff40985a2e40334a82d91bf95341 (commit)
via 560e7210fa3006c98c711e96cdbac6f9a0b391a0 (commit)
via 9500689fabae67565bee456d33953ae7b139a209 (commit)
via 505839e902948071f5e7876d274db4345b28b49d (commit)
via 78def66fe5de6f50a555b24d3134d5ec0dc32021 (commit)
via db96ce78efca85a1953fcb14d4ba24b7989044cc (commit)
via e2f1f9c450d7cd9e46bd23cef80be7db829d44c5 (commit)
via 56e8727ef471d54b56a88f340d19e2ee05d898bf (commit)
via 137e70ef17439961ac3ce8fa288675502cd26178 (commit)
via a9a606dd7a35d81932f970e00cdf38b466eee2d4 (commit)
via e848d7f24f16319fa07ded7a76af27187a8348ee (commit)
via db21e8ab55f132828542f1a34642d98bbaf6d8ee (commit)
via 31d3f525dc01638aecae460cb4bc2040c9e4df10 (commit)
via 489ef1ad9701548d0987c45cab34d33d8dcb73fa (commit)
via 95f7b6ea18efc84f160053bec169ab8a736cbb1a (commit)
via c24dd6f877460227e6eed02d0cca0956366748cb (commit)
via 2f9c3c1f2f692d16e55022f90333abbf7e3f13cf (commit)
via 4d4d0ee24097af2ec942ca78025c95908063a4b3 (commit)
via 14153d9340c8220b4cc4f0088a14f6260b3af548 (commit)
via e402f8d476894b34d7bce97fbc961123e4775b4f (commit)
via 9b93c0778662b587f0a526583fb011ebdf9788d2 (commit)
via e219a9fb24d6efe15066526ded67093f1a59c8ad (commit)
via 99a107f4ab33c0791df351408c76c07f5820cde5 (commit)
via f843346fdfc748cb5b67d079958202c8d4e32e0c (commit)
via 54c201f10077610d7e09a7787d89d17264761f5d (commit)
via 5637b1961aafcdc6950d22aa7a3637221a57e99a (commit)
via 39238e406f2e0cdd63a71f4b4b8700988e1dac9e (commit)
via 1a32f61cafb9f735815bf8f4abb0d8e1019269c3 (commit)
via f3395938a1ead66ef30ab1d261e3a38fea16faa4 (commit)
via 36e95aa1164d24b5b2c46d64958c709a9a02fd51 (commit)
via 0692e63e77ba5f6e1dd24619f215c06a53a48845 (commit)
via c9d69c549110808b6314bac44b357b4f0fb2b699 (commit)
via fd99c6922384541b484e5e3ac4422d8472011b42 (commit)
via 432a1a86cea043993348f31d14628548e267e3b8 (commit)
via fe5e7003544e4e8f18efa7b466a65f336d8c8e4d (commit)
via 475624c79f83adc6befd4d631eeaf83d83fe89f9 (commit)
via 3d05a83e9466e6075dec6e4b7a1502989d9205e9 (commit)
via acfbd3b209c50aeff46fc09f9d792a457f51722d (commit)
via 9e48d735ae41dd79b6ff856ea4e5ba79112f440c (commit)
via cfa31a99243065c40dc3f5a6393bb8ec923d2bd2 (commit)
via 5178a891fb3713609ade921d010bed453498f355 (commit)
via 67727b623a4889ee0e38bd0ad9aaba710e722e4f (commit)
via b31501e6a7d8cb5c1b3714f289d9c55753447d83 (commit)
via e9975a7383a299789fd18b184880a00dfb0e15ed (commit)
via c8cf64492fbb3329d10b15ff660c9f262e46ae1f (commit)
via 1ccec996459f9cea1bc32b6d736692fd087aae0e (commit)
via 4f06531cf0b40476bbfd7d6a4312bacb1d958ffe (commit)
via c1d2bae691446b1fb44d3281092a7529f2f4f648 (commit)
via 5e6de42e41584afd09a579b08d25902c9db88927 (commit)
via b39ac098a37803588a3a801d91b85170ddaa7137 (commit)
via a5dd2311e2083f2c32fc55b5339b1e064e8b7de3 (commit)
via e57fcdbaee0fe7562168a363c1f3273a92fa595a (commit)
via dc44fdda4e0705993f44275d22b2daa45d4bae6f (commit)
via 160519b6e3c5da340011b454e034a188529a4cc8 (commit)
via 5538d85d751405c573cfc0491b1e663bb7ce19f5 (commit)
via e3a81c18ff3f55727c1308bb894a8eb03e8f48b1 (commit)
via d6b120403a24c9c233fa70ff8fca4a5da39f209e (commit)
via e156ec53472f22bbb890ebc76a1acdd9a9eda70c (commit)
via f80bba65ac947cdc2f99253623e976c815b63140 (commit)
via d694e5ded809ef2842eb5101e4d03e6e657e4671 (commit)
via 5d33e8c43d18b6aef9461086c8fbd5ee493b55df (commit)
via fae2d0d5854141a9af7c1177c663a0910ab18ad0 (commit)
via 14a1dcf8c6d299b7b7d91b27c4bd73b06e1fd61d (commit)
via ce11fa4222196986805f54ab5b67b77dd5fe8e47 (commit)
via 74402ae4cf3274fb20bc3183941c4099dc672b89 (commit)
via c4412dfaa0bb8c6e95cb4476e80b648cfa9288f2 (commit)
via b2c418d1d6aed9e2eb599941218e1f8c0d13a445 (commit)
via 48fab55d59089444ac9dbd81ea3c6978f55f9474 (commit)
via efb25130e7c98335f72741cb62c9b3cf4ab076b3 (commit)
via e37672ef8e814456d53772220d885c91941db7bd (commit)
via a3edee3da65d09f2b00256277ed8d8a608c9c627 (commit)
via b8855cafa94a12af7e087a4866367f2336e10bdb (commit)
via 682108d6e371706f340cfe8575d8e00d1dab09cc (commit)
via 6ea285d4f7cf6a9fd18bd9a3811944fc02d0e34c (commit)
via 1aa773d84cd6431aa1483eb34a7f4204949a610f (commit)
via 5519b6cb34e655c96dca4ec8be2a3eabda941f3c (commit)
via ec6446047b5007290ca4d6d31e67239c424f33bb (commit)
via de552586600ecd4123b904e65d09c3ef0717fc2a (commit)
via 58ec390c5c70c0c7625e3f52caf4c4f20b3bffa3 (commit)
via 34ebe17e3d731f19c6c44a8777b994241b5de7ee (commit)
via 67c6c4489b2daeb6001cf2f462cc189f2c841b5a (commit)
via 41139644452bb11a162254b442ab611644ceb603 (commit)
via c84b3ecae4d2d0b532c64c958857f8104dbae923 (commit)
via 27ba6b2117e3ab88aedaa904292707871e131393 (commit)
via 76ae47d1f6061a09f4a8e20852a9874ea28a4e19 (commit)
via bfaafb81eac3c4ea98cb63e2c0bdb8fc02105b6c (commit)
via 6a9f0125c633e6203e5fda37e6220ea862038df6 (commit)
via 990a0cff9891fce08b3e163720dfa08fffdede5f (commit)
via 54f86d7796c55289518befaefdcdd7d84ebefa88 (commit)
via e66cc9c2e0eca69c62a3a61133fc4ba220274bc7 (commit)
via c37ebedf94c5dbbed3c685272a0cdc4bea67fb04 (commit)
via 24fd003fcc458c673803b953ca857a96bfa5183c (commit)
via a9d323bf4df6aeb6333b057514b748d05febc1a9 (commit)
via e1df41b8ba21d04560ad4b65837bf7bb32fbdf34 (commit)
via 2af8dc8ec9301a935228568f80f8ba18531a3ffc (commit)
via b0dfec1a5000ad22ef4dfa9c21c89e90b0e68673 (commit)
via 862e13c5c852b8ea55c1b53a67803105d473a342 (commit)
via ee0e702fa85ebecb95ac89aeba5959b111ba5d57 (commit)
via 0dcde0e52b99cade8f584751b2d2756c20e78625 (commit)
via f1213a3d4df71a4009a3f9a09a9aab002b42ce35 (commit)
via f39c8aea4a0f9dc4b910c003b336e124149ab88b (commit)
via 5dcd9589aff3bc2a91ad2dbef98cd9012ed9b637 (commit)
via 857abf9ee880aecc9039b993e73683f1a6019cf6 (commit)
via 5db2d64ddcd9f4eec0db6bbb6a160c95ac81cc6e (commit)
via 37ded0b31a0a0dcadb93da7bd85248b90d9af57f (commit)
via 50dd99f81efde9267569def411a470e082ac312b (commit)
via 496a4378cfcc8430dd1155bb3ae5448f27ebfdfa (commit)
via 6e6eda44e9b1b7236e3baccc25e008eb674440aa (commit)
via 27e122bfdd9edcc04e39cbe8a033d89ef36207d3 (commit)
via 9cc499cb83497dcf89dcaac8c7a59e84a0b4a0e1 (commit)
via d04505cee4d9df2cb9cf9a2909a948589be4ff09 (commit)
via 2273e3e71f5879cdf51f956c19a153f4280d9531 (commit)
via 77d792c9d7c1a3f95d3e6a8b721ac79002cd7db1 (commit)
via 82ea1075a4ae1ef8f6c0ba3cca5d6a1968a77f3f (commit)
via c926e1e8bf4eedc6c999c6ad1a1f0fff96783154 (commit)
via 358e2303c565cb387aca660c2e55967b683792ff (commit)
via 2ddb97d633c1721d74615b84d2c6ebd360b9bda3 (commit)
via 74567fe1e92c8f10da3a639d235cdbc4c4d5cc9d (commit)
via 192aefd97c825f7636e0885eecef9a5834e53d05 (commit)
via cc3ccc1f474407507118ae0c53af307d0dd7bdf2 (commit)
via 41521402ab646288ff1a4a12c5110d52dddeb4b1 (commit)
via 5cb80378e05cc0ec208adfbe09e15efd04431267 (commit)
via 0c7670c04135ba2ade5ffe241b66094aeb891f16 (commit)
via f0645fdb5d7be3c7d7090159847853f5a06ed7e4 (commit)
via df354bdb4031c1f67c03d71527105da4e35d1a6e (commit)
via b30c6c0843ae18a275f65e7ad5ab86bd78c82678 (commit)
via 30d350e8cae28631a083c04a9ccf8d5bcaf69adb (commit)
via 6c86102687c491bdac96577cabf98a0640ad8a60 (commit)
via 37035a073be57c9f6d00f2558f74ce69b7067295 (commit)
via 765bfaf3ccf1a96e981210c429d5beacb229b4ce (commit)
via 938eb7ec8ac6bc670fcdc95a2189f4b13390e9cb (commit)
via 50c678a05ecc181f4a63fd930bd345bce72dfc68 (commit)
via b5dc1d86b2a763f98d8414589a545fcbda09a36c (commit)
via 67d9442a23eb4fab4dff3f4d1ab142948e21e880 (commit)
via 5f74b5f0820d7bf4aa50d0341c9316e6a915738e (commit)
via bf4d941c69ed13ec03a2e179009780eb1ad11359 (commit)
via 71ff1ab09693f1a90e46ba74cd5bbadf449161ea (commit)
via 030c77386b5018d30282cda2fb6aabe620a0f15b (commit)
via a206dbd5432240440b6bbc63ff3165ad579d5efb (commit)
via 6342eda010ae512cb972e70f3824ca0de638c293 (commit)
via ff008c094a577e61f619cc39df2eab858dee0fe5 (commit)
via d2ee006cd5d5f3cf5ff56594fbf3d8955685d8a1 (commit)
via 74a60943da1619dc598bbf4b440b78914ebe98a9 (commit)
via 71d57323f77270278f75a57f63499c82f8f4ddd0 (commit)
via 4fcd0d3125dd7d8441f33c3ebf8b63e5b2093a68 (commit)
via 6f8d74e83a1c8073dada7288be92c2976c638e27 (commit)
via 0034051f94a5e0e91e2574ec07f9b5374d39d6cd (commit)
via 0346449357648e45f5b68f792cbf6dc914b16d9c (commit)
via 89e3ffaa1fb56bcc76f626a64afcc25e506d8b54 (commit)
via fc97ca7de4ecd731aa6e470a7c68f5b26cbda7b0 (commit)
via 78502c021478d97672232015b7df06a7d52e531b (commit)
via 71eb80242fd144bddc06c98b3bdaa91341a65f26 (commit)
via 0555ab65d0e43d03b2d40c95d833dd050eea6c23 (commit)
via 23d63caf0cc09f19a01794458bf2457a67caac05 (commit)
via 031eda633fe45496f47fad9d4b656a0100144f75 (commit)
via 77def12e8d0d957d9fe587816b27b6df4f88ce90 (commit)
via 080db7e6245ebf63f0b3104e92b99142c87fe291 (commit)
via b50e03318eeb6464f7143eb524eb7b8ed2a5d02a (commit)
via 88504d121c5e08fff947b92e698a54d24d14c375 (commit)
via ede93598806f8ddb21d193eb002fad8218416e40 (commit)
via 35ac625edfbc78dde6ccf78f8d577f3d8edadbce (commit)
via 4bd053d4e1a4165c7b4b5d91a6f674e40250301a (commit)
via a2936cc155f8b5ce2afaa82820fa377a037f2be3 (commit)
via 94cfeebfec6574350fb4980c2b0cc6a7d84ba4f7 (commit)
via 47e5578077fa332f8476a13aa4ad6ba29e003a1a (commit)
via 830c0ba6c96b009d1c9c4fa31dea7cf4f6de4e37 (commit)
via a8307030f7af9fc88e3e66b6eefcc89f6b6e15c5 (commit)
via 07a778c5592002042269785ec41bf4d93fc7db91 (commit)
via 9cc0c06cac86d3460ee1f5b5e2c8669d9709663e (commit)
via 9e84764e4911a4ecf9b82df3beb6cd289eb68ada (commit)
via b8da54961c78a690c6bf02618d4c28fb9d320177 (commit)
via 28465ec39050a2779fe98503c690ed4df2711e98 (commit)
via bcea2b3da4bca69194a2154b92f9f734edbe8322 (commit)
via a251c4f239e7b42856314412142cd9777f91dbf1 (commit)
via 2360fc72c24eb39d32b0afd6a389cfe8f10cb2f9 (commit)
via fc9c42d22de8c2c5555573a1a3e29b2d30146a29 (commit)
via 3303478b6f9943fd5514268bb1c0c42a638d107a (commit)
via d158640b970e5d8f0e5d4f8c6c278f03ee0e47e7 (commit)
via d098ac2ee97872ebcd8366cb700d7d2a5e668b8c (commit)
via d11b5e89203a5340d4e5ca51c4c02db17c33dc1f (commit)
via 7a1c4cc8e1baaf51a2058edd0a4179bd586345f3 (commit)
via de0694d2f4c7dba909eb922e3fa1a1269d3cbc78 (commit)
via bc0688b0e7846ec9bb38e4e014ed23be84876948 (commit)
via b8a7bf58c974c9ba4518fd894963cd66a19baf7e (commit)
via 3a54f64afc94e9394a527872b957609d14002ff1 (commit)
via af788f1fef2cbdb63b05f14106f907dff87fd6bf (commit)
via 7c390c1149baab7bd33e741afcdefb827275d5cd (commit)
via a94c70e333e3082a55f8a82ff026aae9b35964ad (commit)
via b6982ea32af206b6ef661b492eea7b274af97bd2 (commit)
via b5673b0b0860921b516d9a0e514891e78e444578 (commit)
via 4cea30b55073337c32d03bbd9dc813eb92795c2e (commit)
via f0c1ac2d7613802bbbc9545684ba0a9346bfdc0f (commit)
via 5d422dbf6640bae02dc13ad7c9406fe13f42a1ef (commit)
via 072ba32545ec255923699c0c181345910f86a208 (commit)
via 62168918aceee04418765089cdc5fdfb34bf66a3 (commit)
via 929a9cae2b351e67ac1953514a63b0c54095361c (commit)
via 04ea273e2988cc405ae3ee7555a0f028258c17a5 (commit)
via 58a5aabf65705e4107cc59ad25c76bbbcedc52bf (commit)
via 43f5888605c770c012421a420766b01e1ad8a96b (commit)
via d1b5154a7c17058ff49aa67f389f52496228e4b6 (commit)
via 5b495e880e559c413a80d0dcc741a5076f3f7eb4 (commit)
via 141bf465a7b13f1e4c92a76ca2df208c8d375385 (commit)
via 7a87432e627715d9062367e2321427a4510d5822 (commit)
via ac2e283bd92dcc5af494938d6cfed82e4074abde (commit)
via 03fd43339b3ffd2537a1621d628d504cee13c9d5 (commit)
via 6ed8870ee7ac32e786030403de4423b8d7647679 (commit)
via 7a6f36fc9073def2a531a4090b97d223d5a5c69d (commit)
via 7c576f2e3d986b0f58883776822323ff57535a3c (commit)
via 1f91575ae48846e3a97a0c3921dd808470ba4427 (commit)
via 523ab3a9278385abc26e1438803499a66e260f43 (commit)
via 8e4fcdf22e1b5d14f740276e3a6c34a90bf3117c (commit)
via 201bae76d11706e9fe9c09491ed216c225a02e9d (commit)
via be4334aa0b66fd01f9b3f859c309545459dfab00 (commit)
via b02c482b25d489b723927f45c4cfe3fedd89f5b7 (commit)
via 99240dbca4848d3aa76b34f5ff0d6da4d6bf911c (commit)
via 1440e71559ad3565dff46e9cac31f31e6748d8f2 (commit)
via a407cb3d58c78b93afe294be13e7b360b11fd542 (commit)
via 1cccbc6845d795f8d8e2b0c7ba637fb28e179e71 (commit)
via 0c81e90348e53e673a05e92e150cec0f598a2d4e (commit)
via 882cc4d6b2b9f391e72fdc0bf8eb82bdb846ca61 (commit)
via 081891b38f05f9a186814ab7d1cd5c572b8f777f (commit)
via 4472e3c7cbfb5fd33ce575bd2666036d363b2ce2 (commit)
via 04c0fbf9fa780b4a281c5982ec33e5632852cfa1 (commit)
via cd79c2fae07f7b1a8d2e2f501488de7a2d11eac5 (commit)
via 76039741c19fa58e404879b334475b9ae01cd8dd (commit)
via dc19181b46dec42bc5db83861731656a5b45b899 (commit)
via 224c7b9aeb3000e11790a2c667f0ee45c9481f17 (commit)
via 94d43a69237b5d2bf671e384ff8b2b9a5ce445b4 (commit)
via c3e1e45419104bdb01dd385b22eae85bb8799611 (commit)
via 946b527467c19236814cae6e35191ce19db3284a (commit)
via 02e2f3a3c2ae669824d595bc9b42f37d9624b22c (commit)
via 54210ba456b4e0822c5e333fd1f996bb35c6bee9 (commit)
via 7a52a9a3618fd19ea9779eb0cef1a3e4f1c3d444 (commit)
via fd08a0dc40846f58aaa8a7df7726ac83e5e4c038 (commit)
via 98f5e0e604e60fead409c28645b064510b8fc8de (commit)
via 46629b17783d3ddf624002893af955d525f453a8 (commit)
via a1a58a7382e82256f3f6785b7bebfa4643cced67 (commit)
via 928a439496c6040392cc03615c38aa3de45bfe87 (commit)
via 8e28187a582820857ef2dae9b13637a3881f13ba (commit)
via 4b3e621a4497ac99978d40b316b11581cf80d088 (commit)
via 75340e4d2906762ecd088180087c9229a253e4ed (commit)
via 1ce058332d84de9bc2f37344d0b49c24d3c30551 (commit)
via 4e0adcf35398ec9f5c3a7a4b14e9a1aedcf9f66a (commit)
via 87d5f96fe04dab6de30c733230669703a2429701 (commit)
via 324cf344f3e0cd9e35e50076911fe7801a7d4690 (commit)
via d3da4f1f4f00d13962e6f23ff1272269569be5d4 (commit)
via 71315bc901882a8bbb35a95c19781528560fcb82 (commit)
via 3f27e2dc3c0ce961a95e4791604e3cb12fd43dfa (commit)
via 50ca3176a95f7ca760c0749d7a92634e2526369d (commit)
via 08878f6bfb271301564ad307339d2599bc6d951c (commit)
via 3336f575aee4072d1d88b05f7db79712384ef2d8 (commit)
via f6e9a1186d00e023021f5853e5a7a1d7c3f838cf (commit)
via 3bb68553bb39502b749265542c5b6d36ad80e32d (commit)
via ade0b1a890f1fe21c075d4fef332e3e35407f086 (commit)
via ff20711233b8bd6a5df5a896c0d2855222291a9e (commit)
via 3e8ef1595dd2c7095540b22cca77e69991ad8ee1 (commit)
via c6827475447b07375cfdd2902c08519cd1cc9dde (commit)
via f15bdf8420351828159c0d7847d4f653388bf53c (commit)
via a234b20dc6617392deb8a1e00eb0eed0ff353c0a (commit)
via 97a0938cfafb96358649b9a538b00fbb3ee42ea0 (commit)
via 023527c23812b2f999c938ff1ee5b30434209297 (commit)
via 86632c12308c3ed099d75eb828f740c526dd7ec0 (commit)
via dafe7a6038499cc0432f4dd6f968b7fb1ffb119b (commit)
via e93cfe3f6812d8a1fef103e34d9d1a6ee88dd8d7 (commit)
via a1420dabc6ace739cb21044184654717a32604a5 (commit)
via cd689c463a59fa5fee72d3f977835e0369eb3650 (commit)
via d1fdfd86c9b8e0120e557e2c7baaab542f9c2719 (commit)
via 744fe91ac965c576cbe916ca39a0bef54afdcd3f (commit)
via c3f769ce6f0e3be367f7e0079a97a11e3f344761 (commit)
via ff36bea0314636d978f61923cc7292364c1808fa (commit)
via d686e00a5dd1efae29891e8056af324ea983d187 (commit)
via da57d4a08a2e14e5e6589138692674894f667f54 (commit)
via fd9855894957b318876a9cb9a0dbe2b4cbbdd4b6 (commit)
via 5aaa6c0f628ed7c2093ecdbac93a2c8cf6c94349 (commit)
via 345b5986ff26015475b5e17506b3ca6cafaea8bd (commit)
via 7b25152b5bbbaff6840f88ad7ff2684f8b8062a6 (commit)
via 0f07b7c15231aa778e693e0f2b36d32e1023c431 (commit)
via 76a81d33b95b12341515cc6ca5718d72a242ab59 (commit)
via 8339141a4a64043e82685f0e6793b592b7ac820a (commit)
via a28db69a6e8e3548e8e41f62999fff18dbd33bec (commit)
via e3ab39868457157166a8b7b2f1753555409426b9 (commit)
via 25c5c37094f9845be826f91bf5901068415891f6 (commit)
via 9df5e254832c5c65ddf4a0895c09256e8386820f (commit)
via cba6db1c8d894a7f30ae820a49aea3d0ff8c18be (commit)
via 36af50328be059026859a09d402ff2d591789dfd (commit)
via f8c763ef2c9eb8343ead481093e2d0024e3f1cd2 (commit)
via f5278f2ac4becb33b93dc4c2fa7e6558a7daad8f (commit)
via c47f23e502e998d7885d4d3d679c1eacb1dab790 (commit)
via d4a2c864bf1952f1bcccead59159462519c58e10 (commit)
via f80cdaf115a8ad80184240b283b243ac07bb9739 (commit)
via ab5868a8dc4c6859f772219daddb7775848d3dc6 (commit)
via c2693bb84ea22e3b44617d729d8d2e8dbd99c51c (commit)
via 2b521f9674a0d9aea7fb06d71b008e64c40922aa (commit)
via 2f25f64f9f7a1021b67b9c37cfca41e86f5ae032 (commit)
via 14f8767d89d62e0d573a1e6b31fbeea9272e7011 (commit)
via 32872f1e745f45d1c0e84943cf57ad985a925f3e (commit)
via a82432e09cc7f3b22920d9baecba1f8e89332f37 (commit)
via fd51a1883a332c305fa4015a6210971d3956fc12 (commit)
via 04611df9f5e34b2a8d6949b5b00d25a065c7c920 (commit)
via 9eddc07cd32918f3b8e9ebd114d9c8f8f39a359b (commit)
via 143b2c6769c64eb55d2f34305ad8e2b7ce681aa6 (commit)
via 4569c1b87f5d04f663e1c8e2813d090a78dd9e40 (commit)
via 76a1d7e547e9eecdc4aecfa3cf6cc4ba940ad725 (commit)
via ee53746391bcfdaa75bba0db87add3dc7becb84d (commit)
via 92c0c95057cd98a5b8cf8099367a122684cf329b (commit)
via 296ec6be00c208aa9ab8cbaa20376749e8b6ab49 (commit)
via 82ead9ea724a5482c44e8a6235bcd4634eacce2c (commit)
via 7deb4955ef8816885376e558e7f0eaf65e22b4d4 (commit)
via 4a78ffbf8a2b9d1171415d7f0af1b7adc4e53481 (commit)
via b916d130e0799f457634cfba2b06fc1250db54a0 (commit)
via 1ea71dc16928433c1243375bd9210d5fceb28fe6 (commit)
via e7a49bee8af14cf5d4e16e14cb5bdd3ba83385b9 (commit)
via ed62a7f4dde0264cd60e4b739c41be9c98bbe3e4 (commit)
via 38932d7e76c4e35aca7382640e9e86360a389545 (commit)
via 55b3152ebb03fbe946986a52732f79a68d926163 (commit)
via 29d36377ec206cbe52274ee9a5a6e88ef27921d5 (commit)
via 25b33e62902b5bb181405e2177d2fe935cfe6c75 (commit)
via a892818fb13a1839c82104523cb6cb359c970e88 (commit)
via df1ab797a9d1a2a188060c70071bb8fa7d6f1a01 (commit)
via 6fc6ecda47f8556fc0a4daabc042b0b1a2d7d592 (commit)
via b653f950b906ecf4194d2a2cd07fa92ae47dd546 (commit)
via ceef68cd1223ae14d8412adbe18af2812ade8c2d (commit)
via 879d981650dff5ee577712aa45aa9b363b039a04 (commit)
via e595402348d7766d7e3f7fd8d9f32bd8b144f747 (commit)
via 33cd7d9232078f70aa380309a3615a1a9d743988 (commit)
via fb26d933e7af0cc07fe686ef88ef95b28cd10d2b (commit)
via 48d2369e3a7c0e55f5c94973ab0191ddfbaa02c1 (commit)
via fe59cd140491dbc685932bd22440e28c703c1053 (commit)
via afd75a89d7aeba622b53c5b37e3f76572ef68c3a (commit)
via 8d9debda3a88cb20d19d23dbdfe106b3132a4ead (commit)
via 6eba2b45ef54d671c674d7d840dc99208eb5d00c (commit)
via 4787c281a351b04b9570b8bb2d863db706a9d9c9 (commit)
via 9bd9fa819f30aff560da9412ffa14d87e557ab69 (commit)
via d53bbaf239de6059dd0d8872f84d1a0db6ccb90c (commit)
via 7b0d9b1dc03d9343f3ccfed2fb5876d7d9f048d5 (commit)
via d65686925bbd04cbbfa3ca2705f54696b76beb45 (commit)
via 4f6b51bc00d3f37b8938951217cd1f3564bedcd8 (commit)
via b395258c708b49a5da8d0cffcb48d83294354ba3 (commit)
via 569b2b6e5d84277316bce84c0e118ffd116ffe5e (commit)
via e5687a75c06966ab68ad453093fb6c7b9d8fe761 (commit)
via 119678f768f09b13d6ecb715ad731d1dca8f05b0 (commit)
via 4903410e45670b30d7283f5d69dc28c2069237d6 (commit)
via 27a4c936d87d31952f6a8377e04cae1fb90edffd (commit)
via 9eb7b41d5253c44e59987170a74e345b651c96bc (commit)
via 7f642b767c8274a6f3344e4a5e1141e9e7f38924 (commit)
via 7efc144e0eacc123544507d635630b7949052993 (commit)
via 43db6eac145a6a9a59f6d892a9b9fd2384a12d51 (commit)
via ad6d052794693ec22f430109de624b8fd7ea212d (commit)
via a4f08f816c968fd9dc614f171b8925b5b7998da7 (commit)
via a7a3c9f8cc85f04267a5606f4413afcde7af1864 (commit)
via ea37ca8caa7c56fea957fc7aac066d5708675f28 (commit)
via 4ff5a9f61d9ad1c96468556771562f1dbad7ec98 (commit)
via e93f054a2d0cf1c21d00112ccaa93d5d2432a677 (commit)
via c16b3ab523860ccca5e8e9648b58801c3eefae29 (commit)
via b734c55b73941125af327595ba39252add032791 (commit)
via daab0dcd59baee4b1e56eab640e29c9e8e947e80 (commit)
via 00a15b4b06f9de757b30cd320fd56431980f8458 (commit)
via 8bfccfb1fead0448e959126e07dad1a800880575 (commit)
via a09ced080782388e389ee6c669b9d36da65f9dc5 (commit)
via b21c74c08fbdd350bcd01b083fc3cf29a98e5a3a (commit)
via cabd3b127cf5ab2b916c5717992338316496de8b (commit)
via 9a8870c515be763e8be63cdc28231883601867d4 (commit)
via fb1d629419b6c58b9c9f410e6c027c8769e0c9a3 (commit)
via 0709f8b56df68cfe0c79ed98bfe7e9dd25ce0e1e (commit)
via 40e06e655ce78197f0b52d8c5dc9c7516795362f (commit)
via 80c0a2c3eec3ac5d3ac862f9b4c5926f0eece6ed (commit)
via 11439c030741ce402c52ad9d3cd1a407d45a443e (commit)
via e564debcb683454296d3e3cf478748daa0b08cbc (commit)
via fba4ec1d7269284b48042b1a7ea084b531bf8d9c (commit)
via cccc20cac1d91951bd9b707c3dfb532e9ad8686b (commit)
via 68c9545bb7d57933e514ef55779abfd06c38145b (commit)
via 2cf390147f147dfab2c8c63c470177189799ed20 (commit)
via 287ee379ad62f9043699c936dc1204b6a906f52c (commit)
via b5646b50e23e6049f233f755d7e143a09d4fb19c (commit)
via d4d910b28b3226d1330b7da1df170c406771b939 (commit)
via 7bb5dc265a830d8993c9cb1da55194631e657587 (commit)
via 1e3c154cd21cede83a8bbd7a559c20752b58ce24 (commit)
via 9c4b079aca67cffe9385e54671f8eb9ed232e1e5 (commit)
via b1b486e59d86633e7b5e17aeedf0f78688ecef05 (commit)
via 45fb91884dc0741384a14e7f52633b0566779dff (commit)
via 434719dc9b7eeee0d0901fa6f5e1847bc40691c2 (commit)
via 0ecb807011196eac01f281d40bc7c9d44565b364 (commit)
via fa321eca22c746101dd9cf67c7bc9da6de30483a (commit)
via a64f778b468ff7bd95461d1228aec66565602d57 (commit)
via eef87432f746406d467c647dd7302fc4cac8d53f (commit)
via ee2664ecf4fda60a78e97d39c4f69b4f22d5aa24 (commit)
via fb283c1a0e6f8cdacc9c326f00caef141b058f1f (commit)
via 4c178871eba133fbbf56c2dac1dde1a28ad20477 (commit)
via ffa2f0672084c1f16e5784cdcdd55822f119feaa (commit)
via 7c40e60eeaad7f2c1ea79f92e866dee08eafd5ab (commit)
via fcc1b5e13946c5e58e2bd0bf287f551e161f0544 (commit)
via b6d140b6169c130b06896d2b40df58752991a47d (commit)
via eab5008d28b04fdd7c0f4c93051fd3b1c1a416f7 (commit)
via 313464eed2b54281b8b2357002cd7dfb9231ddf6 (commit)
via 121d3e844ad6d8ed8aa21dd48c53095e2f770117 (commit)
via c3dad479414b01979693ba71c832aedd681f5044 (commit)
via a23491a635d7f74132fc0b91eac832275f8f1f87 (commit)
via 9a2ca8e5de8a23018ebb0ac7fee819c065542ff1 (commit)
via 284c8b457d8a8940265da1e4d0c7b1050806afbb (commit)
via 7c54055c0e47c7a0e36fcfab4b47ff180c0ca8c8 (commit)
via c166c0c96a476eea511fe363b90df3d78fae7506 (commit)
via 5c497dfce7d23487c2aefe9907f942c39c4c5846 (commit)
via 83717978e3941e4eb5d7dc90d26a88256efd3175 (commit)
via df0c3599c1f3ad957df20eb8cc04c33d1c958e4a (commit)
via 40870d717a8017379cdb68db9ed15ca71faddd5f (commit)
via d846851699d5c76937533adf9ff9d948dfd593ca (commit)
via 372a2f033edf044d3f515f3b5009d0c8fce3ce16 (commit)
via 89fcb0f002447e2cc3148e212f1ecd4f6d1bd821 (commit)
via a437d4e26b81bb07181ff35a625c540703eee845 (commit)
via 0338dd8c3ef74395235bc37533269fb252142263 (commit)
via 28c50de2e9ef07250b54a3d99850ee353f511ffe (commit)
via 297955d6928e3202c25d0f1fa17708c032c46a40 (commit)
via 2979d921553eaaf59cfbe6b78bd726e09006ca9e (commit)
via db0ca2fbc64b390f261b5e36938c736321b171f9 (commit)
via 7f0a7a5ff8d2452c4b48575a725f97e5981aff66 (commit)
via 2aca19e705f02400a6b213ef84fa81c86dff0375 (commit)
via cf46f370333b15487f6c02f85749d90c2e0bd710 (commit)
via 4fff42d6c9ae24effcae3f6eaa73a8a4eda5a38f (commit)
via 25bc145bb597683411ece6ad7cb72fc0fabedeae (commit)
via 0e1eaada6f38f23e7de9404a94b2cd5dd2794879 (commit)
via df8ef3fffa7a4186d7ade60b514bb78acbb9b33c (commit)
via 79b087eab39177799e38611661fd86125d9b8a67 (commit)
via efdf126e6e0fe75afa26d4d7cdcce663892a840c (commit)
via 35a7c63a23646994af69a1a6d325ad11b4bc8354 (commit)
via a91fc737dd4c982194392b467b68c6c8383cec55 (commit)
via 34c2d5bbe08e41b0802eb6ca58a8084595064803 (commit)
via 4a2c0e7a8852496e9b8f9a56f5058cb1f55d3f55 (commit)
via c83d4bea0e9abc94b84315eb42c8ed7a879c8899 (commit)
via 7e9ea719c8fda75f346eb87daae58cf06c8070e0 (commit)
via e4f2823d8b564c5b2c2f7029e5eb81be6b058d3c (commit)
via 25301e86fb8aa4cb8d98933a0c3b7dcf46f8c4be (commit)
via 1b967a5870088c3f4c7b5a3b8202e1b2e9572690 (commit)
via 50ebc839b1c8389eb1b7b70f02758a9034138552 (commit)
via bb568b0fbb855dcf79681de5024c09b91251ccda (commit)
via 04c515e17a08b41c9dbf241e3983148596353d2c (commit)
via 45417c48a7d32fb6e3f7fdccc70ec2faa8441135 (commit)
via 16f90854f8957d12e2861bc77303f186402a8805 (commit)
via f1dfe74d6b9b2b8c4dc85dd39a43084dc2b73aa9 (commit)
via 6daf339b7a83843a12d4938424063d1b96929924 (commit)
via 9c3195a542df8d7e747e28d49d7abdc707781632 (commit)
via 3ce5e0a2b96c84ae003ea372bb84ebb19ca355e8 (commit)
via 9e8b5ec4233217fba5c7e9b17afd6c01e4fc7b79 (commit)
via cca71e70d0667d066ba5ab63c65b21d83cbb896c (commit)
via 2d514a764119f29d3e2fbb04366d880df1866928 (commit)
via e22700fc2021180a0dab3f312df5fdabe9bc47e2 (commit)
via 98d147ea68fc1e62b6e7e3bc74c80e9457b5249f (commit)
via 96d1613a474602b833af953a364fb045fcfee776 (commit)
via 6f8490fe9ce1d6dbef326d75f9eab79c09e83462 (commit)
via ac7ff7f2226bdba60809b70caa82659b77c6e2e6 (commit)
via 677cebce1708ba85e46a0712d939176dc7ab7a94 (commit)
via f0fe344b15c3e1be2604201000e6c9fbe6a910bc (commit)
via 5a4d69114ef60bbb86c4fc2190b78761ac4ccc3f (commit)
via 776f5ebd79615b0c05f2009c09bf61c44c59343a (commit)
via 314e299226288d66efe5e65d8e00fc041b688f35 (commit)
via 0e3b55edc75e91ea11df51ecffae0ae4ff07fdac (commit)
via 0261bbec3a5cd8770aee567aaebab2a4f93c2303 (commit)
via e997ff18a6bf1eeb23391e0957db0defdecd0213 (commit)
via 9e183cb0529aeb9f0674b5727affa223d8fc9db5 (commit)
via 31a6f34026c83594bd9f205df18eb290114d9ea1 (commit)
via e34dd62b8eca7abfbfd4851140d663efc43041f1 (commit)
via 8320ec6eb230fe761ae4769367f10383e967655c (commit)
via 38cddfdc23f6d2aa31be24173f9d7c1df2e3c4bc (commit)
via d033a50a7e32664848c360b160e9170cdcd79886 (commit)
via e2e13d0bd0856496a3571ba6b7f35f41e6534859 (commit)
via c8b7abc5751b171621be9edd5156dd2691ff60d1 (commit)
via b8422db8e0120d3998b15581dddcbfe4efe91a68 (commit)
via da16cc776443e1414f779d7402996c6949934765 (commit)
via d6db622c5649e745fb3c64218e36c41545c47aed (commit)
via cce60befee1e0c25e20449b9242112cc0a777089 (commit)
via 89dd4548179485bdf76a84d289ab88ba65bd07bc (commit)
via 364be72653933235d362a57c9342bae1c6e17b3a (commit)
via 43f9ce0c56cd14c0349a8f3c3c9a5f4dc092f65a (commit)
via ebe0c4b1e66d359227bdd1bd47395fee7b957f14 (commit)
via d9b00f6ded6d0cfb41a0939cb613c1e7bd703a67 (commit)
via 2c60d79949a79fc8f093f3447c949e7c63f83137 (commit)
via 0ede9e8f8c0e1d89a01cdb1eccb5256363bda0cf (commit)
via 64cee1a7ad8341b83954e022487744060b59d04a (commit)
via 177b356c926307e94a95e6e2de7a3f0ee7124021 (commit)
via 1867180c5e2263baf319514b5b765b8eb4049c08 (commit)
via 6c554c8b4e37daeaa4182d05af69a40a0b78cea2 (commit)
via 6eb7a93e81027bcf67435188c1ff7788965253cb (commit)
via cacab413e886e8c90691428e1e3f78b77814945e (commit)
via ea1f2d6e96715f0821df5d096be46b2454e6073a (commit)
via 04f7331a44e0bfd48f52d06f6fd463c6652dd38f (commit)
via 5b4feea19e2cc1bdf18ce52cf0062828ad9067c7 (commit)
via ec1ad752576f7f3541b1b57f906256c6a5ae0dc6 (commit)
via 1ce2874170c85de79a896b1cc3077d9f3032d9e0 (commit)
via 1151e49bcec0ac69fe01d084e10f106481337dea (commit)
via b92cca798d702273d63d56d9197782edeb927c8c (commit)
via abdf624576745fa0a4f37319e3c7dbd76047e2d2 (commit)
via 28143beb0e57bbd4d62432b81edc8e179b95959f (commit)
via 76705c8fc54809ece24609deda2d156a844d2bf5 (commit)
via 47dc61713d6bcfc16410fdb657c41dbbef85fb2c (commit)
via a42b39784b65894e7dc164f1b7f2f887715f0c45 (commit)
via 9713a50748616587ca32f17e9931c752aa1e5d3d (commit)
via fe141c14e201993b5e0efe848c2a47e0a4b553bc (commit)
via 0310b8b77e6adb8671d1f9ebfe826e394e95f53f (commit)
via 29170e28d19f35a874d84b88f7c0873c51e28f89 (commit)
via 53d9f46923a22920c04d5bc4fc706ec202686b1b (commit)
via 2842dda9275c1a471d70d3a571d810c8d715bf3a (commit)
via 78bd2b77782510d02748254a8c79249e493b6024 (commit)
via daf48e860413d4c3229ce309253b29d1ad974c7a (commit)
via daa24430a497d145849a687f51252bfed16a2caf (commit)
via 6d03a35ac6dec0508d30cd778fed48257fc6c0e1 (commit)
via 87e4b9874ac16ccc5d236a2fcb8221942713e086 (commit)
via d9433ad4bd9aec1028b9b05aa55e9b083e35a4d4 (commit)
via 9df42279a47eb617f586144dce8cce680598558a (commit)
via 0640f80c7a62cf9c3e289f8290c5a3b44f4385f5 (commit)
via 05c6223cf7f145ec6e96cffed0561a03387b7367 (commit)
via cc788af2c45043fbb98365d5fb85c1d16307dda2 (commit)
via fd45c40964a9557c0a6534c6d0cbe5c377d05066 (commit)
via 475e26bae3b3c4c8823c4931638d0a7af0ac66cd (commit)
via f4ad75548592b2f3eeae22de5685cacbf5c82bae (commit)
via dabf205231ff9e720d8693800e311a89ace18488 (commit)
via ed5025a1322aec3c6aa2855ec807bdcb8e013f1d (commit)
via e4034daa010c24286b969cd9ad6d75c30895cd52 (commit)
via 4903de33e8f5d16ed99b6ad6b22fac023e653f71 (commit)
via 2bb4420b73b5485cbd05a221a414a40f9e7674b0 (commit)
via 0660b35c79dce9ff27361fee48da8c174bf05cd5 (commit)
via 3e37899f95fe561fee0a73fb5d85071065ef3c7d (commit)
via 088bb8cbca563a17b97166bcbb799fd50bbbca4f (commit)
via f099c11348b7947c64f7467677e9afa58ca7023c (commit)
via ad134794bd3fdbb058fe5d50d8e92c71d03d47cc (commit)
via 309bc10ee624106cf75165efd4251312deed3212 (commit)
via 5cd716bd9844e154e6ca58a981787614a3a6fe6c (commit)
via adfd1014c34da20e272f058c9cce43130c637071 (commit)
via 65874313065ddb02756d5ef06c8802c7540c17c7 (commit)
via 98eab34d80219120219d064f6b3b9f0afce3e59a (commit)
via 00e36b253bd643b007f89a8ccb3addac1a723eba (commit)
via 7be4a7e7f859881a3e5a3f4268dbdbc3468b7214 (commit)
via abdb0ff5e8cdd781d7c4960bb51d8538f4e3b44e (commit)
via d4ab440d337cb9ef3f1301c3b655ed0a913cf257 (commit)
via 33eb030ea0a6f980573c9a7ffff41c99535df98e (commit)
via 6c274fd95d3c2bcc99113108f5a68aa0364f924c (commit)
via 241c11e7b06b233585b52df53a9273ab8e0ad405 (commit)
via 3427f489904a83ed1adb8e740aaaf1f6ba80267e (commit)
via dfddb7ba4637530a542b833ee14083421eeba826 (commit)
via 1cbd51919237a6e65983be46e4f5a63d1877b1d3 (commit)
via 81b2d1ceaf680d740f66250b848eea1db05c4a3b (commit)
via d743478f2b76e0f37381f64d5f4e90417260d67c (commit)
via bc436f1f6715aabcc62c468fa6cfd7d3bf694f29 (commit)
via 0194e3a376aa9c1ec2c232b0f2fd9a6cf02e448d (commit)
via 74472396fc8f91daedcd6a286b34b5cae8a5ab57 (commit)
via e5cae846407b0f627ea67ba687132c141c4de57b (commit)
via 5542432e850b29dcd316842a61fcbc2903acbaab (commit)
via b963854734ff8ba78ccb14f2e5a481c9426881c6 (commit)
via ea46adf9f6bbdd78a06ae748d979986a75b9b8d2 (commit)
via ffbe8a099bece23efb94e3adbe8fdfff8cfb379e (commit)
via 3c194549a61e68ed38bbcd90e69f1f954ff286f4 (commit)
via 5a6f9f8c21674d8f70fe33de2b7d7e23c105e888 (commit)
via 5573b0bc953d1a9589d2e0f06b8da58dadb64bc3 (commit)
via b1cb4c4a1421bb512d093690c95d27425b33e1e5 (commit)
via 521c140da800fdb7a7c87e53e01270e02ae4bce2 (commit)
via 583203a8ca082541bc2982752b20294bb972bdcc (commit)
via c32718be9f5409b6f72d98ddcd0b1ccd4c5c2293 (commit)
via 2ec061bcb5fbdd5893377a233219af11a6309cf4 (commit)
via 1096417815a2e2241aa059a24fada61108b2ca90 (commit)
via 2463b96680bb3e9a76e50c38a4d7f1d38d810643 (commit)
via 912b79387bbbe16f5a97513dce140d31917e091e (commit)
via a1703e5ae5fc9458c066fb4aab7666bf4e5fdb8a (commit)
via 823fa0556364ad5bdcd26cb7a42999d502f3af02 (commit)
via e37bfa349bd0b7866a57519bf7f86dbb18c3a104 (commit)
via 145a6173191e6cd39be3d495a6e5d0511dfe867a (commit)
via fb7877b06ba873d4fb222409dd92b4701ae11ffb (commit)
via 21385dbdb72fe02275c859a88674a9d2166a0ed0 (commit)
via 158af09a0032d47503a9e52102bc495b8d5bba46 (commit)
via e9f3843c953ce9a3e05c3dcc4b8df2a67a9d9e6d (commit)
via 9f729b786ad2d12fde55cbaad5de72918fbe209e (commit)
via 20336851f494ff1d9c8937400a99ed4d0203a340 (commit)
via 246e56b3bb7792789b3aa891e21e580976d9e7be (commit)
via 168bed01625dd25fc2c40bd6b402763849e840ec (commit)
via e831ffa2129fc5e013a0217f004896f6f40f258e (commit)
via 4eda7bbc802b24730eb8ef9e5716fc47f19d5a00 (commit)
via fb4a0a36ba0d90409684e8042916341ce35c5f21 (commit)
via 7b1677584cd0cc94825741792f9ed66f16029938 (commit)
via fe8babb0a2953fbe1755b79db136c0cae25682e2 (commit)
via 9140fab9bab5f6502bd15d391fd51ac078b0b89b (commit)
via 1241ddcffa16285d0a7bb01d6a8526e19fbb70cb (commit)
via fafa0c06f358f9cc32860535bef1c56d25f20775 (commit)
via bf1cf162e37ff391874569c82c6bef7e8b7e4f18 (commit)
via 122d477847f80c7d77bec087a849b9e6743c17d3 (commit)
via e74f6d5e8f9abb081bde606bc46319488c7e546b (commit)
via f137e0fa3a387fcc13898ea6da6b61dcd80dc8ef (commit)
via 457ef26144222d0eac25e93751ad8f0c8b12b8e5 (commit)
via 572ac2cf62e18f7eb69d670b890e2a3443bfd6e7 (commit)
via e87e34cd12ac1e4b70bd55ea2fcae1bfd9ccfeeb (commit)
via 9ce716d340a89f0212302af25ae75ba68bf69b75 (commit)
via 4b5b7b0c63830075ce2b063c23a5efa5fd2b9cc7 (commit)
via 0e2f20e8a4f5c57c8c9ed8601edeacf52c4a4ab9 (commit)
via ec02d3d35a79cf4acfe35dc7382ed69666d03f74 (commit)
via 29025aa582a5d8771619e4de08c3c3fe4f0d0fd5 (commit)
via 2a27eff879ad941873586006fc2b944fd7c9471d (commit)
via 270059b1e812a686d706525a40873da3e5233611 (commit)
via 8038f6efe5645a6100b9021b145f7b414efb6d86 (commit)
via 084d1285d038d31067f8cdbb058d626acf03566d (commit)
via 712d1db4df1cb12a3ade2f96d653535b77ea2718 (commit)
via cf456aa1546145ac403e75535e555d773b39ff20 (commit)
via 006b2ea9dabd869bc05682b73b083ea815d520db (commit)
via c34d8eb763ceb840f65de77a22eb67c8b1ab6392 (commit)
via 2aa47d167c58b6718706f8e037927b824ff0cf00 (commit)
via 7ed21ab7e857037f18944e63fa5deac02ecf017a (commit)
via 81c81d70342c7aacfb2689d86aa239e231c310cd (commit)
via 9b16d3b4c04d35914b9eeba56a168d5039d7342b (commit)
via 50eff6d6d5788193d055c038a5772bce2534628f (commit)
via e16c86b6229017d8b79f8bbe342976ca2b938c3c (commit)
via 981abdeb847bfcee7c03d6c252d0859c1ed7a2b0 (commit)
via 1781090510f9f7ad378497d65feaf94f5c32b262 (commit)
via 5651434ffff461b28616ffec595ff8a941c6df04 (commit)
via 27dc0ba25fb06503a6e9f5bfaa293b1db1cd5e78 (commit)
via ec9b5b21e964c7fb5eeeedb17af5a4f833289667 (commit)
via 597a1d9a5adaee9c31156b0f8f07aac30dbf4e67 (commit)
via fe0c557fa06274df7610c0d07a6c6bb827a5cd3f (commit)
via 604e90d1e0c928cc8d94146beea6e24fe2252eec (commit)
via dcf58b7836e854107438368905576de7d29e0e3b (commit)
via ea064b169fe62c3f71aaec4b5c6d677255f3621a (commit)
via 5a906f923ec419895fc398ba3dcdf64842164d14 (commit)
via f4f11a9b1c09adb07e2d6b99a0bd342d5a75ea3f (commit)
via 74e6966d7f8f8216dad871f1648cab977fb9fea2 (commit)
via f65cb09eef192590929e246311b33aa964c5a1a6 (commit)
via cf05a54b3c950aef906e577be39d792a17e53796 (commit)
via 95689208a5f10f7f839abc57d7db9d73e0eca200 (commit)
via 9303e9a05b5a48f80ea32531ae493724b04aa2be (commit)
via 8ac28e2101ab399e885c178294635cacfe6573e1 (commit)
via 0334c1798ea43061494a530d8bc274283f1f1fc7 (commit)
via dcd582cc0a7d02d69c3c7c28916980f71034f9b2 (commit)
via c6947d22cd80d5934ad53c91b74c640c8a64e57c (commit)
via 40f4efbbd9f5685147cd1abf19d12c336bc1b8d9 (commit)
via a57662d6db020dd0b334472b5bb7ea88aefb6a7d (commit)
via 122d9611bd552c3113eca8aa5e91d2b8a3018210 (commit)
via 6ffefafd958806769b56e1e5972eab62b87a1135 (commit)
via 36b32903369f9a3afda93e2537d369d2402a3bfc (commit)
via bb5efb65cb0114b5bf985fd1588e8b645d4c45aa (commit)
via 54cc81d718e60a3e095fecefe502cdca9dad1776 (commit)
via 1b666838b6c0fe265522b30971e878d9f0d21fde (commit)
via 7ae7577211c77f31a9f30be122d245add72d893d (commit)
via 0eea320309f86b8f1f95bed053078bdcf4c82071 (commit)
via 20607bda887846e2745bac1d275c51874885f917 (commit)
via 17106327efdf114e15b21cf8079f9191ab073648 (commit)
via 8eb1d7a04312f7fa70ee3c986e66b6444a1dfde4 (commit)
via 99c44efcd64b8262614354faaef2b5abb6fb644e (commit)
via af9f4f7fdaf88b906022bb808f44c6e586e702fc (commit)
via 1ea4834d960f3d5f14ff8cee312448baab72e075 (commit)
via 921ca1236dbf64fa1f73a03a6903b5a2fa82f84d (commit)
via f1ac03b8147047bee020fbc019d4969f16c75064 (commit)
via 231b29911894b0f5ddbd0d1444d9dfdf9cb5abb3 (commit)
via 06c4fa64bb59638af953e9b3961d23e173eab49f (commit)
via 4e8ed21d6a2e80e81393e64d808bb07655f42cbf (commit)
via 3b42967a83f4e15e1b1d07a159a600cb67628039 (commit)
via 5012fabe00f1ebcbd2d67564df325fc69a529b5c (commit)
via 262ac6c6fc61224d54705ed4c700dadb606fcb1c (commit)
via 8eb32c279693c55b444cbacaa835c9d04ee3f242 (commit)
via 160b06212023cfce40c1ff6f13c5307c4d4f6e50 (commit)
via 8541053e6fffaf84127fddd2c0a25d0dcd5c635b (commit)
via 5e89e6afd429bf91d66f2f70435342fa1de388a4 (commit)
via a37c25cdb88ceb49b518d27a324ad7523f936fb2 (commit)
via b1610a09098d7019c6701daf77c2b8b98eb03b75 (commit)
via 04330565df2cc183a92f196772e03b05482046eb (commit)
via 44f4b82933783502be198c0ab0a81020fb3fe510 (commit)
via 3437b4fa8efb83ce5395ec159e85b38951e23504 (commit)
via 4b65b6bdb511b88979d0b1c6f44161c223e1dc9d (commit)
via 3f6620c5ba918a2a411aca9430fd8ac347e1aeca (commit)
via 2018011dab54aaff10385ae4905c1a5706997062 (commit)
via b751b7e0cc08dca691029169909a54a0bd1e6b45 (commit)
via d7cf894e555a8aa8508f695fabac2061822d454d (commit)
via 41361697fe474af2b511e922c18dc0b211f1344c (commit)
via 0dc2e2582d63c42847400701a7c3c6b0fb2d6d9b (commit)
via 4d55533790df1992aee5c088d4f91593d055c080 (commit)
via 66c4f9e1df9c4c00d117323d016cca1c16c13e01 (commit)
via 1eb73e619aae14a927a7970cc4cdd2a7fb1d362a (commit)
via c374a5c5a329a307346be02d5b884ab57e1cb8c1 (commit)
via cd3ce64ac0ac3e82ba905f3eea77f1040b7e69b8 (commit)
via 3c348815b0ddc70deed6e5ffcdb3b6828edae961 (commit)
via 660d7d782c81bc55b8cc009836c35d9ca3948063 (commit)
via 316da90b500c8cb176bcecca78eb7e711022fdaf (commit)
via ecb22c0c225d8da3710f4f912648acf401183cfa (commit)
via 75080dc8f887cad817ed9b1d20db97461704d1cf (commit)
via a9f80cc455747b38115c5279bafde024e78b5f30 (commit)
via def088d4e8387e8d04fc0fc0e56a9db39e3ccefa (commit)
via 2c8303354db6a7d41b4c2e90181b5fcd5db40aee (commit)
via 96b5b4c186fffb2a52891209bd8c95e92e45573e (commit)
via 6f91ee90cd428b0e7eecd4cb99eefc0201f4360e (commit)
via 24aa99548a4164d2c57adcf3829cc50f1fd34de5 (commit)
via f19053d9083026ad651b574f276313f5f2bd8dc6 (commit)
via bc88971bdb67728a22d029504bf18e778af26591 (commit)
via 6e2078ecc6e3f91676631b3b3f9c4be7b5584853 (commit)
via 3384859c8e15572519887abeb44d3ff295dbd178 (commit)
via a140e9b97e208ce30d0c3fb51f14dd248f307b1d (commit)
via f8a702716291db0b289aa211e5adac6fb2790cb7 (commit)
via 390af5c81d043a9f4cc8e908365e7f0734bddb8f (commit)
via 2df69541deceaa8a3bbbcc04d2087e94aa063d11 (commit)
via ed3c8e2f7a386b05b696d3ff79fa285ae7a050e9 (commit)
via 047cdc39aa335b778d4ba9ecdacbd859d612807c (commit)
via f5bcf535de7b23fd48061b9ee37580e2ac761f16 (commit)
via 1f16df990ddee676893ae701b8553fd42d6992a7 (commit)
via aa0ddfbb4f0aa61cfee383f5459c2183b353674b (commit)
via 7bc6ee513da806b25cab12a32465122f33232128 (commit)
via a3e7ad9b4fb05fbed4cbeb6d6bbcfc8cc0443df3 (commit)
via 87c6ef9942c9edcb37aa811e1c20357e6bd6bc4b (commit)
via 95e81e5a713c145307034724994bd64bf35455ff (commit)
via 4f1d8d0f2ce99880306f7926a23b28f1aa8e4fd9 (commit)
via 3999bb246ea782310611471d3ff28cdd85bf4a0d (commit)
via 10b62db1ae170eed2fb897335037d50c399b37e9 (commit)
via dbe0eeebf72c420712555c522be6d25ea857f7c1 (commit)
via ff81e1bf773d5d18656f75ed144f278cc0734a88 (commit)
via f155c3edec68515cccb5f5c4da611af08770ae4e (commit)
via ecfc86a4afb1d26fcd48054b0406e153daf571ee (commit)
via 4e9bf13057eba0ccff7052f1859ab49b0a27c777 (commit)
via 36dd3d4b0bdd8c40695ac186955196bd268db586 (commit)
via a7c4e0bc10a5430008d12081f4cb30d1d0d26723 (commit)
via 3987357f56e0fc2feca75001b963a11f1b7120ff (commit)
via 6cece2fc1d8a086b65421a583cf35b40f6ee1751 (commit)
via c20ca2440575e5cfc9d4a3dd582c9f56776452d6 (commit)
via e23c4b1e90ba1127a5b7eaef4add6f3b68fbc6fa (commit)
via a83e29a881af14dc61b366024e25f5ca68244220 (commit)
via bf0a9b3576da038ee53bd49360fe3bd8b01189fd (commit)
via 4e47d5f6b692c63c907af6681a75024450884a88 (commit)
via 400d3b61a32c0fc47c5f41a93ea649fbde6a5445 (commit)
via fac1bf01a628b3b922af67cdd94f05257b6eef9a (commit)
via 34ba5055b70c9de09d406b3e51ad2e02b98f6439 (commit)
via 0eacaf1a394d3674ca73c6531aca60dd952e0e3b (commit)
via 238dca82160452df0eb732ce014166777380767e (commit)
via f6aa7d59569e492326499898a0e3f183c8748970 (commit)
via 253d063204f1e0656c89650999c76f942b4840f6 (commit)
via ad04d070072126a8f221f67244e9a54e68b53955 (commit)
via 817462f1ea1f1e9b4e05b2da90b8d311a7b59843 (commit)
via 30ab4cfbd7f0744653248efc5635c68930359eea (commit)
via 97feb3a88bae42f4051cd67384a4dc7dcd018c9a (commit)
via b78eefc13bc53a301327674a10d88cb917d6f28d (commit)
via 34d2f102aedbdb6b818f8eb8deb1e00fe431dae4 (commit)
via 6eab8b3fc5922c7b9c6403f4510b929544eafe30 (commit)
via e29c4f167f9d466eb9310989e836a0be428e5ace (commit)
via fb99e325e667a39ef88b6c8fab128a8df862a786 (commit)
via 31b96198427b2c140556afcd61ff3809c43266f6 (commit)
via 4578fa114fea13eae53713b5282f5e906a71fbf6 (commit)
via e58e69f00b57fbb7bdddfbd43e8b958ea50fd146 (commit)
via 0a2a239a12af8c0ee54d62cb34d170c6cfdd33f9 (commit)
via 0e519f4da5ef99475c3bf5941ed1ea553a7c9a6e (commit)
via 1e3b465d617d7e7dc9c9ac2427e37a1b837369d9 (commit)
via 429ade576746b5c5e1d9113ae57ee863276809b1 (commit)
via 25fe349f4fb12f444c06fdc2117a4f62f1dda2f7 (commit)
via b91c7c259c85f29413e05898e8b74ca011835aa6 (commit)
via fcc5a1f450a3a0125e999a8bc9e1d26f207a8153 (commit)
via 71d23aa4207048dc78531ce48e35e6a83a6b62ab (commit)
via bcc0502a221ec06331c2825c5e27874626b32cf1 (commit)
via 6555863f88c4e9865baebe0b55db1f7b00b6214c (commit)
via d3dbda9445dcb699e48457e89b90c3be933ec2b9 (commit)
via 992f3702aad9c83836af20d32e96313ff22cde3b (commit)
via 111334856c3beb8cfad23ff60264604746dd1492 (commit)
via 6ce4dde0aeb2a0aa3457f0037be5d549b3e9b966 (commit)
via cdf229b32e2138c7d451eead64d7fdc2d69e94b7 (commit)
via c3a18648a3d7f96325a529f87ed5824183fa0ce5 (commit)
via 2e11f6cdbae2c29e12bbce2c9d14f1851439155f (commit)
via 30872fc580a3cb357bdd34765eaecc1d61820b16 (commit)
via f2de716ada3aa2c0862f3d7d21ef10d28db90219 (commit)
via 95b1c90380f6b12bf29939b7164fce8d5516d443 (commit)
via ca4c3ad8a436b2c5660959d266c82b692271c158 (commit)
via 8fc7cdd216bead2a98f1c98a6b6dc7a8a3bea4f4 (commit)
via a2d07257bc12397054a57d4190cc61419e572278 (commit)
via 051d8bf74c500b8e1f1d2eabb9bb384d01104d10 (commit)
via 2e92b2f8a977a376fa38e3a51c9646ea9e83c4b8 (commit)
via ce281e646be9f0f273229d94ccd75bf7e08d17cf (commit)
via ca268cc4ca8f7505b996fa3076959b5ef3934658 (commit)
via c4d150ae4528f15fe8f05dbc85a1025abc92e78a (commit)
via ad3dfb04a335ed4c65722063bd351be51dae33b4 (commit)
via 5861ae7cb08e698c7a6c6d053b644f04106ed7a3 (commit)
via 778e7b5aa3c55ffc7947454459fd02263725303d (commit)
via bef9fc00a6c4e6c45922848d7f220ed262e83b1f (commit)
via d622e35a3d7b378f79dc4d587156cbfec3befed9 (commit)
via 433ef14c7c54f255b302ff74213a24bdc298f2a0 (commit)
via 5eeba2034481f043369f9a7f3f86ce606653387e (commit)
via 86ab4d3fabd2e551f90603d81ff2bce2368ea7af (commit)
via 865d62e9e58a3acf270f163a75ba713c134d82d2 (commit)
via 8fdbc3e46da7c31b4a533bbfe5778bf7d68e1bbe (commit)
via aac391dda57b88507b8b10ac4bd1b068d3701b2d (commit)
via 7e561fe2f8d44b9224cf107287f79df14584512b (commit)
via 3b6ddc0b213a61abf826d87351d97de9145a3fc4 (commit)
via 28b01ad5bf72472c824a7b8fc4a8dc394e22e462 (commit)
via 745b08afcdce71ca9b50b2d5a7ffe5d4ffa66091 (commit)
via b509cbb77d31e388df68dfe52709d6edef93df3f (commit)
via d12560d1714fb3a07e56b6903141d82d6081148d (commit)
via 150914707089969bde30fbc6276abc00f3211ba3 (commit)
via ba6927dc78e991f8a5c608beee4c9807895914cd (commit)
via 21718d6ffdc3f782ffa1b5960088786da9b77e4e (commit)
via 20f12e8672c45540cd72bb5df44cf21ff3b6732f (commit)
via 583d7973d8f49c061dee13a14c89cf0846f84721 (commit)
via 7b2536d7a654ecb24d98c3c0814c7384230e81df (commit)
via a3f3377467d511fb0e789957a4b7a9105e4fe0d2 (commit)
via e7d0dbd6ba745bab334aef6e747056c33f5edaa8 (commit)
via 7c1e4d5e1e28e556b1d10a8df8d9486971a3f052 (commit)
via 4929dc565823e49c94b1efea1543ba23d9745c9f (commit)
via 34571fc51116f3e476e84c20e7fb7ba59393dab8 (commit)
via c7bb38c99c9afa049c7336ea3f827aa41a1b2ea0 (commit)
via 1c269cbdc76f5dc2baeb43387c4d7ccc6dc863d2 (commit)
via a125177e363f66375aca5af53649b8e369af58e6 (commit)
via 98e93921ae905aefef6646fc8839e3f4a30a37e1 (commit)
via 1ec7e1ccd24b25501902e0fc29fddf400f2ebdb1 (commit)
via 47b51ddc032a458b42454c3ed774bf26ef4254c1 (commit)
via c139d4bf6b30d7ad9325c7a621911c9455826f6b (commit)
via d8c55247d888cd9f5e2e474e49f8bd97b96623cf (commit)
via ba876505740d4782b58169848605af49325b11ed (commit)
via 4d505b0cef54209575173d52e4f9a3e6e244dab3 (commit)
via 6c611b5c04a5484df7269a603be4f00c4f7fa7f2 (commit)
via 0355bddc92f6df66ef50b920edd6ec3b27920d61 (commit)
via efea92bd3f50b23ed5d551cd7f140abe47959bfd (commit)
via 69e7fd6b5782434c809f90d8060e148318ba3c7e (commit)
via 56b5bef538df18c95ad5df3aaf7ebff4464de7c6 (commit)
via 62a61edffac3ebdd91fec693fe2c1a94785ddb25 (commit)
via 68b9571be6f2370798951b05968d07688e96d56d (commit)
via dec73bbd6dedb0068efe90ee8d77c020778b38db (commit)
via 6ff2a83cb7a6c04a258e84818257fe6eda9634a6 (commit)
via 682ea37cfb7e20366521fd48fc98826a21344942 (commit)
via e1e592789ebc8806b9a8767af95c41c1ec2d5a14 (commit)
via 4f42307f123f7ed147b537430e0f2e9a1c665e8e (commit)
via afa5624c2c3503df1f152f82278f1b9dba19b533 (commit)
via 342926bcc0d317101321ff9afe6cf61967472a6a (commit)
via 88c0d241fe05e5ea91b10f046f307177cc2f5bc5 (commit)
via 3ceeab28d48c23ac561a2ed75a57a8f4aa153858 (commit)
via 888bcbc103826ce8de73b29c3e8bf48d5925fe27 (commit)
via ea679d6d1fc5105027de1f0243817c66a32fa8ac (commit)
via 510924ebc57def8085cc0e5413deda990b2abeee (commit)
via 1d6b96dd3f682a4433a02f2f156fd118aac6f0e0 (commit)
via 8d3f576ca98cdd6e821cf76464cd342d42d941a3 (commit)
via 13668e95ca6bbb07128babb14f1772bfefcb09a8 (commit)
via eddf905784249ab6949e6186f2676f9d627b089c (commit)
via b4111d2495797a6a9738df6dda3b9db696c5388b (commit)
via ece21d1bcb8b6ef6015622427cdc784e951b7396 (commit)
via 90946cbcad43d13bd6dec1a2a83eb75d019dd511 (commit)
via e28f5c6b01d30e48e8de45cb0312d360a44b7ca3 (commit)
via d151653e759da7ad45c28914a67988057e8cbd7f (commit)
via 9eb28db1b6b3c0b6cd76379416726b9931c729d2 (commit)
via 4a484575725500dd766516377eda41daaa17f402 (commit)
via ddfc6892916bc041e53b79a690abbed10ff99056 (commit)
via af0d35e501aab417b9b032069ae1cb8841debab4 (commit)
via cf5b24f110a927685226e8ef41378a847b002357 (commit)
via 5a7c1928eb5b70c2a9739650961bd4e32b6b26ed (commit)
via 238735e15837b8dcb878bbdb627ea4b61682db60 (commit)
via 782a56fbd225e31f6b1341857ad9a5af20df4901 (commit)
via f9a697ab99914d58e7e135ca069b3d5a06c00511 (commit)
via 3399d44c2d83f81b92a30bec360b3f207029bc3e (commit)
via 1516f8a92c2366f34a7c880ba6e6becad841e981 (commit)
via f6092c3b8c16a7155157ecedd3d5d9b4cd8dcfee (commit)
via 78986db7192134b31155c85e075e809acba2984c (commit)
via c77af8b7ddef2d9198b1a092e2a93aa6bc61b8d2 (commit)
via 2a4f9f2f6d0831520ddba84030873b70b9577c0c (commit)
via db4993d8478cad992ebfe8787edda08a6dd0d347 (commit)
via e099dc5e644156de47b9ad45e79a1732e2ba4314 (commit)
via ad723802c83996a3286fc5de23ef97c88ace004c (commit)
via 118edb29441d7299ef16cb752518680b4cbec503 (commit)
via 7b089ac66b9d15be53a0f874dfe4f2e47cb70c07 (commit)
via b5f4af26e1fffd82d4e9a9a2ba31ce9cd44f820e (commit)
via 1b02466ceefb1190fafd35c717dafc6285a557cd (commit)
via 012f9e78dc611c72ea213f9bd6743172e1a2ca20 (commit)
via c34be1b6a604ffb0a6ef34abfcf9c2fa42451d8c (commit)
via f2256e0e6f8260cf8addd1511fe49eaacf22d2bf (commit)
via d6c0273c617498a8bee3a813e013837e7c16b7e6 (commit)
via 681b1839bca185f19eca8e0fe567bb0012743a79 (commit)
via 99fdcbeaa5ea2a5432fc47f39c5ed40f4850b843 (commit)
via a263e7d8bc90224bb6acf8b096d6ce8c87c8647a (commit)
via b5c567f94b74e671986d68ae6e7549e829f72fa9 (commit)
via 320bafb306878cd9f13ba9703a0d4573fb645341 (commit)
via 1960b5becbba05570b9c7adf5129e64338659f07 (commit)
via 87f24fca3b55591ede24de9f856c807ca65966b1 (commit)
via f0b271b93312a990a566380815d70b758293934e (commit)
via 1e4827d9858483f0cb35ff90d3a665c08f9527d1 (commit)
via 3811dac0257939f28cefa621226019e6d1c75239 (commit)
via 8d638e07f2347d5e713493ffed9659ea5048e872 (commit)
via 0b53561c3b937c5d2585a3c62876b600800815bb (commit)
via e111d142cba584e410c5e385c10b714715f55dc4 (commit)
via 32d47188a5034c0ed3973099ebfe28ffe21ff1ce (commit)
via 61e7f493f8f44c7ccb8f2d9ef0186dd15e499097 (commit)
via 0c730e6357cf058473dc389820145d64cd5d946d (commit)
via e909ff99007b9b9b26af58848d3fccdb25d4135d (commit)
via 4dfa50d97a293f4ee50600805b903d8be761c9c3 (commit)
via ae9f367fb26b0fc24fdd5b3d43619fe2190201f8 (commit)
via 6a75877d0cd151127a80dece3ccbef19e6c672ee (commit)
via 31b0982e2b3a7c2f4a3cd31712a996aa3519944e (commit)
via 2754c99e705b7d45551aafe014fc2c11e6e4e6e3 (commit)
via 788d9505e0bec5369ccecc6f51af957874063fd8 (commit)
via eab1ff6c4b905655feb4aa76966e48e6112e3f63 (commit)
via cd1a6e393c28200adc827298ab4776f37e077af7 (commit)
via 32e4aaa9bdc0dc4e618960e0f6aa6c25105547c3 (commit)
via 451bbb67c2b5d544db2f7deca4315165245d2b3b (commit)
via 72de47d4fe6a2a65ac41efa998fe1b78a1616f1d (commit)
via 18ea57dd5bdcdd3e4e0dffef3f656597e4f06713 (commit)
via 6a1a198d0db14c0eceed4ce9ddb437f53cf74fbd (commit)
via 1165c559d56305469df3148f0a652bb1e2dcc9a1 (commit)
via ac453e66538072059954b8ca3e3d53bfc7c8cef3 (commit)
via dabcc74a7a5aa10a983d2e8bac2084e4ec5dadcb (commit)
via daf3c3de7e1ec602131dfb0d71df140b9b43093f (commit)
via b61a322c649b3766e45661551ab578275eec53cd (commit)
via e0122c1e3219488a152350666165369a0e9fbe32 (commit)
via f1f8de8ad231b05e58abbc8644d5c7ae5bfd9feb (commit)
via 6a34b5430a7e16c10cc08a957fedb46cd45b5bbf (commit)
via 21b1f9f943664a24ec0772c3ec7f07c63fa0c5fa (commit)
via 33d7934b9fda58090d00dd3994ca62eb280a5319 (commit)
via 6070acd1c5b2f7a61574eda4035b93b40aab3e2b (commit)
via fe7a9d0719b8ed30956d78f5564d8bd69ae3f9e3 (commit)
via 6672a4b36293b3959afbbd46accda352402d9436 (commit)
via 8da646ef1ffc59ac14234dc87882a8cf87b0e8dc (commit)
via 8190099591a5d3f4442f4235fee65a4a0800083a (commit)
via c416ba80303b3f6938dd93399ba988600e37c675 (commit)
via c65637dd41c8d94399bd3e3cee965b694b633339 (commit)
via 8609d278b5efdee7b218429063df1f6872ab2305 (commit)
via 2b0db8fee277900fdaf55cdf94c7365ead9f6f49 (commit)
via b677c094340db6ba6c37bba7b3e6b177830116f6 (commit)
via ddb07c03439bba577baf4f0c1462cbb8749d2d0e (commit)
via 34e8602a378948478f3f7275743db47b6857fb5a (commit)
via 8361a1340142b5465288fd7f126c92a4d04354b7 (commit)
via 47620f164b9541ce3c62437ff31efd1c134f3f05 (commit)
via ab4d484d2d17a0a2e3c4fc99453f7e314aee3b7f (commit)
via 34dbbd16060884ef96b20d28c110c5682db388ac (commit)
via 82f0bc8ecd6b3ad6c1419b4339ed3584351aa636 (commit)
via a32e082284a9afc697ac57ce02f76023d9402d1c (commit)
via d48aaf1aba81578c0e91609d97f83b83df93435e (commit)
via 2bb953ee477f97c9ed8b4f4dc3857ed0718f1aec (commit)
via 8251cd8f3a007756ad570dd7983660dfc353d6e2 (commit)
via 13a3fcfa3f8f256f5185faffededd36bcd03e5b5 (commit)
via f1bb311e77e15b5e43e2acd11e257bca11db4a7b (commit)
via 759761fd78c941bc3fa5332a8b4171ea3dad478a (commit)
via aba4c4067da0dc63c97c6356dc3137651755ffce (commit)
via 963acb307871f945870c61699cffee72de1c7b65 (commit)
via 3692ad59eab42084765ef68acea3eb24a9e327b1 (commit)
via 3cc446d3ed8a1b6c899ee19faeda2c41a4b5bdb2 (commit)
via 050c451f53da820f9b5ef47dcc469261a595d014 (commit)
via e79153fe56e7d514bd2ddf70450058e863c72d51 (commit)
via 5514dd78f2d61a222f3069fc94723ca33fb3200b (commit)
via cd620bf3e4315d693582f25538b3bb71941a42e5 (commit)
via 662e99ef050d98e86614c4443326568a0b5be437 (commit)
via 3b05e142949e5bee23b809625cb524e7e5ea66f8 (commit)
via 061e18bd967f3a456ec53b531590cffd12d0698e (commit)
via df2502bc7f9600f03dc410f01b1e6e060ea427ff (commit)
via 729bbdeb813e99a5f8323f29593f2aaadc95ce3f (commit)
via b923cbf809b74f03d3f13a147ac098dc376b45a4 (commit)
via f096d86833c3ec49b349d1dc0df91e84a8121891 (commit)
via 7a3dc628e96dd7b5201a8a1d3851992ea8d325ce (commit)
via ebba59e8684bf93f5239461d6c0724ac291243b5 (commit)
via 629023f290c290441129c96b11ece7de299fb8a6 (commit)
via 79e6083d6c946d9734337bd4ebb7f3a984fd6d05 (commit)
via 1e67f2cbd82e555241a366d5b93a7a6ec52c7921 (commit)
via 14d7b2bc8d2d3cbfbaf78d1d9a0492c4705aedd0 (commit)
via 8af77178bac36566bc64b0df7fe22a2b4494ea42 (commit)
via a5c11800c36942b37a3b69f48513ed3fcb31fb46 (commit)
via da6a8d0c07e20f0b258d774de436b8d363c81bd8 (commit)
via a92158cc1d674e0dce9e75acbb40a1a9bd807f0e (commit)
via 3a4e135e22cb4e986ca4c1902d34172ccc9aeae1 (commit)
via 314f9f7615f42a76fe8e741aac266cca8b988807 (commit)
via b7d0dbbcb1f1cc69efe9630afe07037bdafe1d09 (commit)
via 674efef18dbc1a215af65b31b4b38e33f53a6387 (commit)
via 66e6ecea1445b3fb151adda962db171ee65b0501 (commit)
via f1f8b24ea7bd7d408fd009e858458722508db0e7 (commit)
via af4b7eba43ad36c0510c77710d975a9beeb08f89 (commit)
via 659d03c759ca8d350c6c8b7bb6583b09dafa0f54 (commit)
via dd4e05070a8868a7f42eab9096f0b4d506bbc027 (commit)
via 170485efd4eb6cb6ba6af78d62be71b2354befd8 (commit)
via 5874ea28a5e9a3af38fc15c53bcf9d0690680e6c (commit)
via 4fd1b4c0f03f01b2fb085dae50455f6746b435c9 (commit)
via 691a4a2f4e82d52d313e67ddd9b1e5480226fefe (commit)
via 52247fe2ac1e071d95b3333d3c0cea73a0429399 (commit)
via 065327bbba5925b882af5230e5d312750e619a91 (commit)
via a72483929eb91cf20d41ca5c40bd6ba8b6f31e60 (commit)
via 3e6c2a9c09722a48fe3e6c8a1ee35724a9397360 (commit)
via 7a7299fb09d2a02023041d3fe7a92e22d17ab549 (commit)
via 0f225ec3b92e320020b731720e8ebe4d5d1fadf8 (commit)
via de3c708f962db8fe15287c9a5704d3aa8b257b03 (commit)
via 9b90408108142c710c2adb403e61af374fe1b24c (commit)
via 00cb2ff77044b185fe80dcf3f577a9b4793638b0 (commit)
via 00b37501ae500c81657b1727dfff0105c2202f07 (commit)
via 7b794844b6afc77cf05bb93828477fe5cc48408d (commit)
via 75b29aaa4aabf843b0d80400eb2cf8c39c4dd8f0 (commit)
via dcaab4f4a892d1ab0e9e2c246282066891b2ffcb (commit)
via acbd9518519498fcf9d476b5417bcbbb2b9141be (commit)
via 02d45b17f160bd3662ee765147debe770c6d3faa (commit)
via fc26c7396d98fa84a8f057cc409303f911792365 (commit)
via cf2b8ace9b9c295e46fdd5373bd70a13e7e3fbee (commit)
via cbc69359f3d9841a217ee55f0e30937a86cb0097 (commit)
via 1b24dfdd078a3eddf6198d9561ae8bb93803103d (commit)
via 45b76ba773367f8a490bbf19f0d74c02293c387b (commit)
via f009a637580c569a49a74b87a384a242c5a5f30a (commit)
via 9dcb53261abe3c16324155fbc4c3436100b9e2ee (commit)
via e33f9a6d1009753b2f02b43e43da1014b0df1964 (commit)
via 07c01a7a90476fd6b90659bf809e7135ce26cff2 (commit)
via a1d7f70e7c660e847fb388c6f17d6709c93fe8e2 (commit)
via 96e55aef179874427ea28641316dd145fbd98175 (commit)
via ccdce3bab3f04bade1e92e66e3929ffd0db97f0b (commit)
via 79155a51eb422813900332ad1582ed5964d70f5c (commit)
via d27da40718b797aca37ad5bab7215592a089627a (commit)
via 9d42d008eb9ab1a9f271c7c20754ba02785772f0 (commit)
via 447c40bcee5edbd71ee1b3812fa71727b2fdc7a6 (commit)
via f1a6ba985d60a31bb5cffe0571c827c3f7b25ce3 (commit)
via f1396eba9c5c5b1291ba24e372a0b536e3deb1c0 (commit)
via a73a3ef00b42b0fc5aa5ecf832cb713db74288a1 (commit)
via 610400fa75aa44d15fdc95dc8b69e02b45698765 (commit)
via cf020fa451a20df9625253b3b2ccbc00e8144c1c (commit)
via be4c4ffc719d10735bfaea5a7cd5bdea7e5f06c7 (commit)
via 715fd606c4d77f1947c8c26142a5d516d54a00da (commit)
via d686e3ada2141094413e756d601d2e727fb6f760 (commit)
via d0dcd91d39a438d5a18e0726251e1a93212143ca (commit)
via b32ea06f28de94a8ec779385d5e86374e81c023d (commit)
via f73c46eb406d7234dd7519a1d330db67813b31ab (commit)
via 1faf02bda80053ddb2f815466d0372a2eeb6c08c (commit)
via 9e782794969ab034eb92dca6a8d5ee8f518ccc95 (commit)
via f664bc1482edb6da965169317f04f2d7b6458fbd (commit)
via e241028cc0993fb87306bd097a45f99a5ea3a50d (commit)
via 7362cbdfc230a2f8deee2933b78e23bdb4892e98 (commit)
via 48846bf7b70cc48e317aabc554a4dacef89b2d46 (commit)
via 501462d7b9850ddca6a3d0a2c7043e3fa0923759 (commit)
via 044c080dd3fc084f3ee7dcdcc70c8dc328bee9df (commit)
via 1cd8ba5e307c77ae9bc2563569366c43623bbc75 (commit)
via 8599f0c0c2ebe7043dd65458a2302837f7c43895 (commit)
via 902005dceae646aefc03c2005e1ff2896efb909c (commit)
via 22e21e72190a88d50dcca4c7ebf0394074d0adc3 (commit)
via cc259f459b92c5e5f5f374c99c7f1cde9900544b (commit)
via ba422c4d7e15abf64bc7e466d470243d3259e675 (commit)
via ff5a80a31953f5aa6f746e6c4e919519926da717 (commit)
via e04950d346768779e32c83268b6b68e0838680eb (commit)
via fa743a8d8303433152e7c93bfc8aca1a2aa9cf0e (commit)
via 4434d7bef7a127ad8fb7e9dc6a43c9bd9e6601d1 (commit)
via c7a0fffa73e28d0d28b3706a9bc6b6e1ed9d2a7b (commit)
via 344410fec60755f6b5cb2348ec9e7b67068f31ae (commit)
via e88330e1dbfed90776b634513372f71caa96ac5f (commit)
via 6f9b0595718f8f7fd5324a6252207d29c40836b2 (commit)
via d11a387719a4ea69946801e4c865323f4b9016a3 (commit)
via 88d4219ba95fa853b07c3397aeb4c2e6567e50c0 (commit)
via 8da3cbf0ec4fcd563b8e779038a38811a77dece8 (commit)
via 66bb0f76db68b8f96fc4ac819f9c07c7f8a404e6 (commit)
via 26b876a067e285be526793254f9422067a89e071 (commit)
via be4ec3ec23084ca94e72a60ddb1a2d4250ade5cd (commit)
via d58451bac9dd51a562a24032d013e1eacc5a3fde (commit)
via 6b09fa3b6e19b73f82940664802158baaca0297d (commit)
via 1f7ffbad521f059f611876c27d8373f355ca8c40 (commit)
via 0add6c7b1047219eda3f1d7fa13340da61cd96b5 (commit)
via 3b1fef1e85522e061114beb6bd9013a51da16c08 (commit)
via 3772748aa192829099877ac1c1e43e43b691df16 (commit)
via 7dab055fe9854655b43338485ec8a960dea94080 (commit)
via 78533f09dbf43b91578392dc8cee6700b3db7d2b (commit)
via 5d8d976fe843af90f06dc9ff48408e96a51a0316 (commit)
from ce374384070155e16216b2624bd89f184993df0d (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 -----------------------------------------------------------------
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 872 ++-
INSTALL | 4 +-
Makefile.am | 453 +-
README | 10 +-
src/bin/stats/tests/isc/utils/__init__.py => TODO | 0
configure.ac | 215 +-
doc/Doxyfile | 10 +-
doc/guide/Makefile.am | 18 +-
doc/guide/bind10-guide.html | 128 +-
doc/guide/bind10-guide.xml | 194 +-
doc/guide/bind10-messages.html | 841 +++
doc/guide/bind10-messages.xml | 2018 ++++++
ext/asio/README | 9 +-
ext/asio/asio.hpp | 2 +-
ext/asio/asio/basic_datagram_socket.hpp | 13 +-
ext/asio/asio/basic_deadline_timer.hpp | 13 +-
ext/asio/asio/basic_io_object.hpp | 9 +-
ext/asio/asio/basic_raw_socket.hpp | 15 +-
ext/asio/asio/basic_serial_port.hpp | 20 +-
ext/asio/asio/basic_socket.hpp | 13 +-
ext/asio/asio/basic_socket_acceptor.hpp | 9 +-
ext/asio/asio/basic_socket_iostream.hpp | 17 +-
ext/asio/asio/basic_socket_streambuf.hpp | 19 +-
ext/asio/asio/basic_stream_socket.hpp | 13 +-
ext/asio/asio/basic_streambuf.hpp | 38 +-
ext/asio/asio/basic_streambuf_fwd.hpp | 33 +
ext/asio/asio/buffer.hpp | 22 +-
ext/asio/asio/buffered_read_stream.hpp | 15 +-
ext/asio/asio/buffered_read_stream_fwd.hpp | 6 +-
ext/asio/asio/buffered_stream.hpp | 13 +-
ext/asio/asio/buffered_stream_fwd.hpp | 6 +-
ext/asio/asio/buffered_write_stream.hpp | 17 +-
ext/asio/asio/buffered_write_stream_fwd.hpp | 6 +-
ext/asio/asio/buffers_iterator.hpp | 20 +-
ext/asio/asio/completion_condition.hpp | 8 +-
ext/asio/asio/datagram_socket_service.hpp | 12 +-
ext/asio/asio/deadline_timer.hpp | 10 +-
ext/asio/asio/deadline_timer_service.hpp | 14 +-
ext/asio/asio/detail/array_fwd.hpp | 25 +
ext/asio/asio/detail/base_from_completion_cond.hpp | 17 +-
ext/asio/asio/detail/bind_handler.hpp | 31 +-
ext/asio/asio/detail/buffer_resize_guard.hpp | 12 +-
ext/asio/asio/detail/buffer_sequence_adapter.hpp | 20 +-
ext/asio/asio/detail/buffered_stream_storage.hpp | 14 +-
ext/asio/asio/detail/call_stack.hpp | 11 +-
ext/asio/asio/detail/completion_handler.hpp | 34 +-
ext/asio/asio/detail/config.hpp | 205 +
ext/asio/asio/detail/consuming_buffers.hpp | 20 +-
ext/asio/asio/detail/deadline_timer_service.hpp | 76 +-
ext/asio/asio/detail/descriptor_ops.hpp | 171 +-
ext/asio/asio/detail/descriptor_read_op.hpp | 114 +
ext/asio/asio/detail/descriptor_write_op.hpp | 114 +
ext/asio/asio/detail/dev_poll_reactor.hpp | 366 +-
ext/asio/asio/detail/dev_poll_reactor_fwd.hpp | 19 +-
ext/asio/asio/detail/epoll_reactor.hpp | 426 +--
ext/asio/asio/detail/epoll_reactor_fwd.hpp | 26 +-
ext/asio/asio/detail/event.hpp | 14 +-
.../asio/detail/eventfd_select_interrupter.hpp | 120 +-
ext/asio/asio/detail/fd_set_adapter.hpp | 15 +-
ext/asio/asio/detail/fenced_block.hpp | 34 +-
ext/asio/asio/detail/gcc_arm_fenced_block.hpp | 76 +
ext/asio/asio/detail/gcc_hppa_fenced_block.hpp | 58 +
ext/asio/asio/detail/gcc_sync_fenced_block.hpp | 61 +
ext/asio/asio/detail/gcc_x86_fenced_block.hpp | 18 +-
ext/asio/asio/detail/handler_alloc_helpers.hpp | 238 +-
ext/asio/asio/detail/handler_invoke_helpers.hpp | 14 +-
ext/asio/asio/detail/hash_map.hpp | 35 +-
ext/asio/asio/detail/impl/descriptor_ops.ipp | 382 ++
ext/asio/asio/detail/impl/dev_poll_reactor.hpp | 77 +
ext/asio/asio/detail/impl/dev_poll_reactor.ipp | 338 +
ext/asio/asio/detail/impl/epoll_reactor.hpp | 75 +
ext/asio/asio/detail/impl/epoll_reactor.ipp | 390 ++
.../detail/impl/eventfd_select_interrupter.ipp | 125 +
ext/asio/asio/detail/impl/kqueue_reactor.hpp | 79 +
ext/asio/asio/detail/impl/kqueue_reactor.ipp | 385 ++
.../asio/detail/impl/pipe_select_interrupter.ipp | 96 +
ext/asio/asio/detail/impl/posix_event.ipp | 46 +
ext/asio/asio/detail/impl/posix_mutex.ipp | 46 +
ext/asio/asio/detail/impl/posix_thread.ipp | 74 +
ext/asio/asio/detail/impl/posix_tss_ptr.ipp | 46 +
.../detail/impl/reactive_descriptor_service.ipp | 136 +
.../detail/impl/reactive_serial_port_service.ipp | 151 +
.../detail/impl/reactive_socket_service_base.ipp | 212 +
.../asio/detail/impl/resolver_service_base.ipp | 106 +
ext/asio/asio/detail/impl/select_reactor.hpp | 84 +
ext/asio/asio/detail/impl/select_reactor.ipp | 273 +
ext/asio/asio/detail/impl/service_registry.hpp | 70 +
ext/asio/asio/detail/impl/service_registry.ipp | 164 +
ext/asio/asio/detail/impl/socket_ops.ipp | 2917 +++++++++
.../asio/detail/impl/socket_select_interrupter.ipp | 151 +
ext/asio/asio/detail/impl/strand_service.hpp | 140 +
ext/asio/asio/detail/impl/strand_service.ipp | 106 +
ext/asio/asio/detail/impl/task_io_service.hpp | 60 +
ext/asio/asio/detail/impl/task_io_service.ipp | 354 +
ext/asio/asio/detail/impl/throw_error.ipp | 47 +
ext/asio/asio/detail/impl/timer_queue.ipp | 85 +
ext/asio/asio/detail/impl/timer_queue_set.ipp | 101 +
ext/asio/asio/detail/impl/win_event.ipp | 50 +
.../asio/detail/impl/win_iocp_handle_service.ipp | 452 ++
ext/asio/asio/detail/impl/win_iocp_io_service.hpp | 115 +
ext/asio/asio/detail/impl/win_iocp_io_service.ipp | 496 ++
.../detail/impl/win_iocp_serial_port_service.ipp | 180 +
.../detail/impl/win_iocp_socket_service_base.ipp | 575 ++
ext/asio/asio/detail/impl/win_mutex.ipp | 78 +
ext/asio/asio/detail/impl/win_thread.ipp | 138 +
ext/asio/asio/detail/impl/win_tss_ptr.ipp | 57 +
ext/asio/asio/detail/impl/winsock_init.ipp | 69 +
ext/asio/asio/detail/io_control.hpp | 14 +-
ext/asio/asio/detail/kqueue_reactor.hpp | 406 +-
ext/asio/asio/detail/kqueue_reactor_fwd.hpp | 23 +-
ext/asio/asio/detail/local_free_on_block_exit.hpp | 18 +-
ext/asio/asio/detail/macos_fenced_block.hpp | 20 +-
ext/asio/asio/detail/mutex.hpp | 14 +-
ext/asio/asio/detail/noncopyable.hpp | 14 +-
ext/asio/asio/detail/null_event.hpp | 18 +-
ext/asio/asio/detail/null_fenced_block.hpp | 6 +-
ext/asio/asio/detail/null_mutex.hpp | 18 +-
ext/asio/asio/detail/null_signal_blocker.hpp | 28 +-
ext/asio/asio/detail/null_thread.hpp | 31 +-
ext/asio/asio/detail/null_tss_ptr.hpp | 18 +-
ext/asio/asio/detail/object_pool.hpp | 146 +
ext/asio/asio/detail/old_win_sdk_compat.hpp | 18 +-
ext/asio/asio/detail/op_queue.hpp | 10 +-
ext/asio/asio/detail/operation.hpp | 15 +-
ext/asio/asio/detail/pipe_select_interrupter.hpp | 84 +-
ext/asio/asio/detail/pop_options.hpp | 16 +-
ext/asio/asio/detail/posix_event.hpp | 44 +-
ext/asio/asio/detail/posix_fd_set_adapter.hpp | 19 +-
ext/asio/asio/detail/posix_mutex.hpp | 43 +-
ext/asio/asio/detail/posix_signal_blocker.hpp | 23 +-
ext/asio/asio/detail/posix_thread.hpp | 80 +-
ext/asio/asio/detail/posix_tss_ptr.hpp | 44 +-
ext/asio/asio/detail/push_options.hpp | 18 +-
.../asio/detail/reactive_descriptor_service.hpp | 549 +--
ext/asio/asio/detail/reactive_null_buffers_op.hpp | 83 +
.../asio/detail/reactive_serial_port_service.hpp | 186 +-
ext/asio/asio/detail/reactive_socket_accept_op.hpp | 131 +
.../asio/detail/reactive_socket_connect_op.hpp | 101 +
ext/asio/asio/detail/reactive_socket_recv_op.hpp | 118 +
.../asio/detail/reactive_socket_recvfrom_op.hpp | 128 +
ext/asio/asio/detail/reactive_socket_send_op.hpp | 115 +
ext/asio/asio/detail/reactive_socket_sendto_op.hpp | 118 +
ext/asio/asio/detail/reactive_socket_service.hpp | 1576 +-----
.../asio/detail/reactive_socket_service_base.hpp | 298 +
ext/asio/asio/detail/reactor.hpp | 10 +-
ext/asio/asio/detail/reactor_fwd.hpp | 30 +-
ext/asio/asio/detail/reactor_op.hpp | 11 +-
ext/asio/asio/detail/reactor_op_queue.hpp | 13 +-
ext/asio/asio/detail/regex_fwd.hpp | 31 +
ext/asio/asio/detail/resolve_endpoint_op.hpp | 116 +
ext/asio/asio/detail/resolve_op.hpp | 126 +
ext/asio/asio/detail/resolver_service.hpp | 401 +--
ext/asio/asio/detail/resolver_service_base.hpp | 123 +
ext/asio/asio/detail/scoped_lock.hpp | 10 +-
ext/asio/asio/detail/select_interrupter.hpp | 21 +-
ext/asio/asio/detail/select_reactor.hpp | 308 +-
ext/asio/asio/detail/select_reactor_fwd.hpp | 11 +-
ext/asio/asio/detail/service_registry.hpp | 197 +-
ext/asio/asio/detail/service_registry_fwd.hpp | 10 +-
ext/asio/asio/detail/shared_ptr.hpp | 38 +
ext/asio/asio/detail/signal_blocker.hpp | 24 +-
ext/asio/asio/detail/signal_init.hpp | 20 +-
ext/asio/asio/detail/socket_holder.hpp | 17 +-
ext/asio/asio/detail/socket_ops.hpp | 2051 +------
ext/asio/asio/detail/socket_option.hpp | 14 +-
ext/asio/asio/detail/socket_select_interrupter.hpp | 156 +-
ext/asio/asio/detail/socket_types.hpp | 76 +-
ext/asio/asio/detail/solaris_fenced_block.hpp | 20 +-
ext/asio/asio/detail/strand_service.hpp | 204 +-
ext/asio/asio/detail/task_io_service.hpp | 381 +-
ext/asio/asio/detail/task_io_service_fwd.hpp | 11 +-
ext/asio/asio/detail/task_io_service_operation.hpp | 18 +-
ext/asio/asio/detail/thread.hpp | 14 +-
ext/asio/asio/detail/throw_error.hpp | 35 +-
ext/asio/asio/detail/timer_op.hpp | 11 +-
ext/asio/asio/detail/timer_queue.hpp | 289 +-
ext/asio/asio/detail/timer_queue_base.hpp | 11 +-
ext/asio/asio/detail/timer_queue_fwd.hpp | 10 +-
ext/asio/asio/detail/timer_queue_set.hpp | 85 +-
ext/asio/asio/detail/timer_scheduler.hpp | 11 +-
ext/asio/asio/detail/timer_scheduler_fwd.hpp | 28 +-
ext/asio/asio/detail/tss_ptr.hpp | 14 +-
ext/asio/asio/detail/wait_handler.hpp | 76 +
ext/asio/asio/detail/weak_ptr.hpp | 39 +
ext/asio/asio/detail/win_event.hpp | 40 +-
ext/asio/asio/detail/win_fd_set_adapter.hpp | 16 +-
ext/asio/asio/detail/win_fenced_block.hpp | 36 +-
ext/asio/asio/detail/win_iocp_handle_read_op.hpp | 101 +
ext/asio/asio/detail/win_iocp_handle_service.hpp | 573 +--
ext/asio/asio/detail/win_iocp_handle_write_op.hpp | 97 +
ext/asio/asio/detail/win_iocp_io_service.hpp | 599 +--
ext/asio/asio/detail/win_iocp_io_service_fwd.hpp | 30 +-
ext/asio/asio/detail/win_iocp_null_buffers_op.hpp | 112 +
ext/asio/asio/detail/win_iocp_operation.hpp | 19 +-
ext/asio/asio/detail/win_iocp_overlapped_op.hpp | 84 +
ext/asio/asio/detail/win_iocp_overlapped_ptr.hpp | 72 +-
.../asio/detail/win_iocp_serial_port_service.hpp | 214 +-
ext/asio/asio/detail/win_iocp_socket_accept_op.hpp | 158 +
ext/asio/asio/detail/win_iocp_socket_recv_op.hpp | 108 +
.../asio/detail/win_iocp_socket_recvfrom_op.hpp | 116 +
ext/asio/asio/detail/win_iocp_socket_send_op.hpp | 102 +
ext/asio/asio/detail/win_iocp_socket_service.hpp | 1806 +-----
.../asio/detail/win_iocp_socket_service_base.hpp | 385 ++
ext/asio/asio/detail/win_mutex.hpp | 69 +-
ext/asio/asio/detail/win_thread.hpp | 153 +-
ext/asio/asio/detail/win_tss_ptr.hpp | 44 +-
ext/asio/asio/detail/wince_thread.hpp | 32 +-
ext/asio/asio/detail/winsock_init.hpp | 120 +-
ext/asio/asio/detail/wrapped_handler.hpp | 18 +-
ext/asio/asio/error.hpp | 44 +-
ext/asio/asio/error_code.hpp | 28 +-
ext/asio/asio/handler_alloc_hook.hpp | 8 +-
ext/asio/asio/handler_invoke_hook.hpp | 4 +-
ext/asio/asio/impl/error.ipp | 33 +
ext/asio/asio/impl/error_code.ipp | 97 +-
ext/asio/asio/impl/io_service.hpp | 132 +
ext/asio/asio/impl/io_service.ipp | 143 +-
ext/asio/asio/impl/read.hpp | 386 ++
ext/asio/asio/impl/read_at.hpp | 410 ++
ext/asio/asio/impl/read_until.hpp | 902 +++
ext/asio/asio/impl/serial_port_base.hpp | 59 +
ext/asio/asio/impl/serial_port_base.ipp | 106 +-
ext/asio/asio/impl/src.cpp | 25 +
ext/asio/asio/impl/src.hpp | 65 +
ext/asio/asio/impl/write.hpp | 396 ++
ext/asio/asio/impl/write_at.hpp | 417 ++
ext/asio/asio/io_service.hpp | 105 +-
ext/asio/asio/ip/address.hpp | 207 +-
ext/asio/asio/ip/address_v4.hpp | 175 +-
ext/asio/asio/ip/address_v6.hpp | 305 +-
ext/asio/asio/ip/basic_endpoint.hpp | 235 +-
ext/asio/asio/ip/basic_resolver.hpp | 13 +-
ext/asio/asio/ip/basic_resolver_entry.hpp | 11 +-
ext/asio/asio/ip/basic_resolver_iterator.hpp | 18 +-
ext/asio/asio/ip/basic_resolver_query.hpp | 15 +-
ext/asio/asio/ip/detail/endpoint.hpp | 140 +
ext/asio/asio/ip/detail/impl/endpoint.ipp | 202 +
ext/asio/asio/ip/detail/socket_option.hpp | 17 +-
ext/asio/asio/ip/host_name.hpp | 44 +-
ext/asio/asio/ip/icmp.hpp | 13 +-
ext/asio/asio/ip/impl/address.hpp | 53 +
ext/asio/asio/ip/impl/address.ipp | 203 +
ext/asio/asio/ip/impl/address_v4.hpp | 53 +
ext/asio/asio/ip/impl/address_v4.ipp | 178 +
ext/asio/asio/ip/impl/address_v6.hpp | 53 +
ext/asio/asio/ip/impl/address_v6.ipp | 313 +
ext/asio/asio/ip/impl/basic_endpoint.hpp | 55 +
ext/asio/asio/ip/impl/host_name.ipp | 56 +
ext/asio/asio/ip/multicast.hpp | 15 +-
ext/asio/asio/ip/resolver_query_base.hpp | 15 +-
ext/asio/asio/ip/resolver_service.hpp | 23 +-
ext/asio/asio/ip/tcp.hpp | 15 +-
ext/asio/asio/ip/udp.hpp | 13 +-
ext/asio/asio/ip/unicast.hpp | 15 +-
ext/asio/asio/ip/v6_only.hpp | 11 +-
ext/asio/asio/is_read_buffered.hpp | 11 +-
ext/asio/asio/is_write_buffered.hpp | 11 +-
ext/asio/asio/local/basic_endpoint.hpp | 137 +-
ext/asio/asio/local/connect_pair.hpp | 30 +-
ext/asio/asio/local/datagram_protocol.hpp | 20 +-
ext/asio/asio/local/detail/endpoint.hpp | 133 +
ext/asio/asio/local/detail/impl/endpoint.ipp | 138 +
ext/asio/asio/local/stream_protocol.hpp | 20 +-
ext/asio/asio/placeholders.hpp | 9 +-
ext/asio/asio/posix/basic_descriptor.hpp | 20 +-
ext/asio/asio/posix/basic_stream_descriptor.hpp | 24 +-
ext/asio/asio/posix/descriptor_base.hpp | 19 +-
ext/asio/asio/posix/stream_descriptor.hpp | 14 +-
ext/asio/asio/posix/stream_descriptor_service.hpp | 32 +-
ext/asio/asio/raw_socket_service.hpp | 12 +-
ext/asio/asio/read.hpp | 17 +-
ext/asio/asio/read_at.hpp | 17 +-
ext/asio/asio/read_until.hpp | 21 +-
ext/asio/asio/serial_port.hpp | 10 +-
ext/asio/asio/serial_port_base.hpp | 71 +-
ext/asio/asio/serial_port_service.hpp | 23 +-
ext/asio/asio/socket_acceptor_service.hpp | 8 +-
ext/asio/asio/socket_base.hpp | 11 +-
ext/asio/asio/ssl.hpp | 2 +-
ext/asio/asio/ssl/basic_context.hpp | 16 +-
ext/asio/asio/ssl/context.hpp | 11 +-
ext/asio/asio/ssl/context_base.hpp | 15 +-
ext/asio/asio/ssl/context_service.hpp | 15 +-
.../asio/ssl/detail/openssl_context_service.hpp | 15 +-
ext/asio/asio/ssl/detail/openssl_init.hpp | 15 +-
ext/asio/asio/ssl/detail/openssl_operation.hpp | 22 +-
.../asio/ssl/detail/openssl_stream_service.hpp | 26 +-
ext/asio/asio/ssl/detail/openssl_types.hpp | 16 +-
ext/asio/asio/ssl/stream.hpp | 17 +-
ext/asio/asio/ssl/stream_base.hpp | 11 +-
ext/asio/asio/ssl/stream_service.hpp | 18 +-
ext/asio/asio/strand.hpp | 9 +-
ext/asio/asio/stream_socket_service.hpp | 12 +-
ext/asio/asio/streambuf.hpp | 10 +-
ext/asio/asio/system_error.hpp | 11 +-
ext/asio/asio/thread.hpp | 7 +-
ext/asio/asio/time_traits.hpp | 6 +-
ext/asio/asio/version.hpp | 4 +-
ext/asio/asio/windows/basic_handle.hpp | 22 +-
.../asio/windows/basic_random_access_handle.hpp | 24 +-
ext/asio/asio/windows/basic_stream_handle.hpp | 22 +-
ext/asio/asio/windows/overlapped_ptr.hpp | 26 +-
ext/asio/asio/windows/random_access_handle.hpp | 14 +-
.../asio/windows/random_access_handle_service.hpp | 30 +-
ext/asio/asio/windows/stream_handle.hpp | 14 +-
ext/asio/asio/windows/stream_handle_service.hpp | 31 +-
ext/asio/asio/write.hpp | 17 +-
ext/asio/asio/write_at.hpp | 17 +-
src/bin/Makefile.am | 2 +-
src/bin/auth/Makefile.am | 21 +-
src/bin/auth/auth_config.cc | 361 +
src/bin/auth/{config.h => auth_config.h} | 0
src/bin/auth/auth_log.cc | 26 +
src/bin/auth/auth_log.h | 54 +
src/bin/auth/auth_messages.mes | 260 +
src/bin/auth/auth_srv.cc | 252 +-
src/bin/auth/auth_srv.h | 65 +-
src/bin/auth/benchmarks/Makefile.am | 7 +-
src/bin/auth/benchmarks/query_bench.cc | 11 +-
src/bin/auth/command.cc | 37 +-
src/bin/auth/common.cc | 36 +
src/bin/auth/common.h | 11 +-
src/bin/auth/config.cc | 347 -
src/bin/auth/main.cc | 103 +-
src/bin/auth/query.cc | 2 +
src/bin/auth/statistics.cc | 39 +-
src/bin/auth/statistics.h | 7 +-
src/bin/auth/tests/Makefile.am | 12 +-
src/bin/auth/tests/auth_srv_unittest.cc | 151 +-
src/bin/auth/tests/command_unittest.cc | 34 +-
src/bin/auth/tests/common_unittest.cc | 96 +
src/bin/auth/tests/config_unittest.cc | 12 +-
src/bin/auth/tests/query_unittest.cc | 12 +-
src/bin/auth/tests/run_unittests.cc | 5 +-
src/bin/auth/tests/statistics_unittest.cc | 3 +-
src/bin/bind10/Makefile.am | 13 +-
src/bin/bind10/bind10.8 | 41 +-
src/bin/bind10/bind10.py.in | 316 +-
src/bin/bind10/bind10.xml | 79 +-
src/bin/bind10/bind10_messages.mes | 157 +
src/bin/bind10/bob.spec | 15 +
src/bin/bind10/run_bind10.sh.in | 6 +-
src/bin/bind10/tests/Makefile.am | 12 +-
src/bin/bind10/tests/bind10_test.in | 32 -
src/bin/bind10/tests/bind10_test.py.in | 330 +-
src/bin/bindctl/Makefile.am | 5 +
src/bin/bindctl/bindcmd.py | 18 +-
src/bin/bindctl/bindctl.1 | 15 +-
src/bin/bindctl/run_bindctl.sh.in | 8 +
src/bin/bindctl/tests/Makefile.am | 8 +
src/bin/bindctl/tests/bindctl_test.py | 108 +-
src/bin/cfgmgr/Makefile.am | 7 +-
src/bin/cfgmgr/b10-cfgmgr.8 | 18 +-
src/bin/cfgmgr/b10-cfgmgr.py.in | 78 +-
src/bin/cfgmgr/b10-cfgmgr.xml | 48 +-
src/bin/cfgmgr/plugins/Makefile.am | 12 +
src/bin/cfgmgr/plugins/README | 34 +
src/bin/cfgmgr/plugins/b10logging.py | 109 +
src/bin/cfgmgr/plugins/logging.spec | 81 +
src/bin/cfgmgr/plugins/tests/Makefile.am | 27 +
src/bin/cfgmgr/plugins/tests/logging_test.py | 135 +
src/bin/cfgmgr/plugins/tests/tsig_keys_test.py | 103 +
src/bin/cfgmgr/plugins/tsig_keys.py | 50 +
src/bin/cfgmgr/plugins/tsig_keys.spec | 21 +
src/bin/cfgmgr/tests/Makefile.am | 18 +-
src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in | 100 +-
.../cfgmgr/tests/testdata/plugins/testplugin.py | 34 +
src/bin/cmdctl/Makefile.am | 20 +-
src/bin/cmdctl/cmdctl.py.in | 60 +-
src/bin/cmdctl/cmdctl_messages.mes | 81 +
src/bin/cmdctl/tests/Makefile.am | 8 +
src/bin/cmdctl/tests/cmdctl_test.py | 4 +-
src/bin/dhcp6/.gitignore | 9 +
src/bin/dhcp6/Makefile.am | 52 +
src/bin/dhcp6/b10-dhcp6.8 | 50 +
src/bin/dhcp6/dhcp6.h | 213 +
src/bin/dhcp6/dhcp6.spec | 14 +
src/bin/dhcp6/main.cc | 122 +
src/bin/dhcp6/spec_config.h.pre.in | 15 +
src/bin/dhcp6/tests/Makefile.am | 22 +
src/bin/dhcp6/tests/dhcp6_test.py | 65 +
src/bin/host/Makefile.am | 31 +-
src/bin/host/README | 16 +-
src/bin/host/b10-host.1 | 122 +
src/bin/host/b10-host.xml | 201 +
src/bin/host/host.cc | 77 +-
src/bin/loadzone/run_loadzone.sh.in | 8 +
src/bin/loadzone/tests/correct/Makefile.am | 9 +-
src/bin/loadzone/tests/error/Makefile.am | 9 +-
src/bin/msgq/Makefile.am | 5 +
src/bin/msgq/tests/Makefile.am | 8 +
src/bin/msgq/tests/msgq_test.py | 8 +-
src/bin/resolver/Makefile.am | 24 +-
src/bin/resolver/main.cc | 60 +-
src/bin/resolver/resolver.cc | 294 +-
src/bin/resolver/resolver.h | 77 +-
src/bin/resolver/resolver.spec.pre.in | 35 +
src/bin/resolver/resolver_log.cc | 19 +
src/bin/resolver/resolver_log.h | 49 +
src/bin/resolver/resolver_messages.mes | 223 +
src/bin/resolver/response_scrubber.cc | 26 +-
src/bin/resolver/response_scrubber.h | 4 +-
src/bin/resolver/tests/Makefile.am | 18 +-
src/bin/resolver/tests/resolver_config_unittest.cc | 234 +-
src/bin/resolver/tests/resolver_unittest.cc | 61 +-
.../resolver/tests/response_scrubber_unittest.cc | 10 +-
src/bin/resolver/tests/run_unittests.cc | 5 +-
src/bin/sockcreator/Makefile.am | 18 +
src/bin/sockcreator/README | 49 +
src/bin/sockcreator/main.cc | 26 +
src/bin/sockcreator/sockcreator.cc | 151 +
src/bin/sockcreator/sockcreator.h | 100 +
src/bin/sockcreator/tests/Makefile.am | 24 +
src/bin/sockcreator/tests/run_unittests.cc | 23 +
src/bin/sockcreator/tests/sockcreator_tests.cc | 273 +
src/bin/stats/Makefile.am | 35 +-
src/bin/stats/b10-stats-httpd.8 | 136 +
src/bin/stats/b10-stats-httpd.xml | 221 +
src/bin/stats/b10-stats.8 | 25 +-
src/bin/stats/b10-stats.xml | 22 +-
src/bin/stats/run_b10-stats.sh.in | 33 -
src/bin/stats/run_b10-stats_stub.sh.in | 33 -
src/bin/stats/stats-httpd-xml.tpl | 24 +
src/bin/stats/stats-httpd-xsd.tpl | 38 +
src/bin/stats/stats-httpd-xsl.tpl | 56 +
src/bin/stats/stats-httpd.spec | 54 +
src/bin/stats/stats-schema.spec | 87 +
src/bin/stats/stats.py.in | 36 +-
src/bin/stats/stats.spec | 61 +
src/bin/stats/stats.spec.pre.in | 140 -
src/bin/stats/stats_httpd.py.in | 492 ++
src/bin/stats/stats_stub.py.in | 154 -
src/bin/stats/tests/Makefile.am | 15 +-
src/bin/stats/tests/b10-stats-httpd_test.py | 444 ++
src/bin/stats/tests/b10-stats_stub_test.py | 115 -
src/bin/stats/tests/b10-stats_test.py | 36 +-
src/bin/stats/tests/fake_select.py | 43 +
src/bin/stats/tests/fake_socket.py | 70 +
src/bin/stats/tests/http/Makefile.am | 6 +
src/bin/stats/tests/{isc => http}/__init__.py | 0
src/bin/stats/tests/http/server.py | 96 +
src/bin/stats/tests/isc/Makefile.am | 5 +
src/bin/stats/tests/isc/cc/Makefile.am | 5 +
src/bin/stats/tests/isc/cc/session.py | 46 +-
src/bin/stats/tests/isc/config/Makefile.am | 5 +
src/bin/stats/tests/isc/config/ccsession.py | 59 +-
src/bin/stats/tests/isc/util/Makefile.am | 5 +
src/bin/stats/tests/isc/util/process.py | 5 +-
src/bin/stats/tests/isc/utils/Makefile.am | 2 -
src/bin/stats/tests/isc/utils/process.py | 18 -
src/bin/stats/tests/stats_test.in | 31 -
src/bin/tests/Makefile.am | 8 +
src/bin/tests/process_rename_test.py.in | 2 +-
src/bin/xfrin/Makefile.am | 16 +-
src/bin/xfrin/b10-xfrin.8 | 43 +-
src/bin/xfrin/b10-xfrin.xml | 46 +-
src/bin/xfrin/tests/Makefile.am | 4 +-
src/bin/xfrin/tests/xfrin_test.py | 607 ++-
src/bin/xfrin/xfrin.py.in | 467 +-
src/bin/xfrin/xfrin.spec | 45 +-
src/bin/xfrin/xfrin_messages.mes | 91 +
src/bin/xfrout/Makefile.am | 15 +-
src/bin/xfrout/tests/Makefile.am | 6 +-
src/bin/xfrout/tests/xfrout_test.py | 437 --
src/bin/xfrout/tests/xfrout_test.py.in | 673 ++
src/bin/xfrout/xfrout.py.in | 245 +-
src/bin/xfrout/xfrout.spec.pre.in | 12 +
src/bin/xfrout/xfrout_messages.mes | 140 +
src/bin/zonemgr/Makefile.am | 5 +
src/bin/zonemgr/b10-zonemgr.8 | 37 +-
src/bin/zonemgr/b10-zonemgr.xml | 66 +-
src/bin/zonemgr/tests/Makefile.am | 8 +
src/bin/zonemgr/tests/zonemgr_test.py | 369 +-
src/bin/zonemgr/zonemgr.py.in | 143 +-
src/bin/zonemgr/zonemgr.spec.pre.in | 38 +-
src/cppcheck-suppress.lst | 13 +-
src/lib/Makefile.am | 5 +-
src/lib/acl/Makefile.am | 27 +
src/lib/acl/acl.h | 140 +
src/lib/acl/check.h | 195 +
src/lib/acl/dns.cc | 121 +
src/lib/acl/dns.h | 140 +
src/lib/acl/ip_check.cc | 141 +
src/lib/acl/ip_check.h | 417 ++
src/lib/acl/loader.cc | 46 +
src/lib/acl/loader.h | 479 ++
src/lib/acl/logic_check.h | 286 +
src/lib/acl/tests/Makefile.am | 38 +
src/lib/acl/tests/acl_test.cc | 90 +
src/lib/acl/tests/check_test.cc | 70 +
src/lib/acl/tests/creators.h | 158 +
src/lib/acl/tests/dns_test.cc | 205 +
src/lib/acl/tests/ip_check_unittest.cc | 617 ++
src/lib/acl/tests/loader_test.cc | 383 ++
src/lib/acl/tests/logcheck.h | 94 +
src/lib/acl/tests/logic_check_test.cc | 291 +
src/lib/acl/tests/run_unittests.cc | 24 +
src/lib/acl/tests/sockaddr.h | 69 +
src/lib/asiodns/Makefile.am | 41 +
src/lib/asiodns/README | 157 +
src/lib/asiodns/asiodns.h | 23 +
src/lib/asiodns/asiodns_messages.mes | 56 +
src/lib/asiodns/dns_answer.h | 77 +
src/lib/asiodns/dns_lookup.h | 85 +
src/lib/asiodns/dns_server.h | 157 +
src/lib/asiodns/dns_service.cc | 201 +
src/lib/asiodns/dns_service.h | 114 +
src/lib/asiodns/io_fetch.cc | 429 ++
src/lib/asiodns/io_fetch.h | 228 +
src/lib/asiodns/tcp_server.cc | 244 +
src/lib/asiodns/tcp_server.h | 124 +
src/lib/asiodns/tests/Makefile.am | 50 +
src/lib/asiodns/tests/dns_server_unittest.cc | 503 ++
src/lib/asiodns/tests/io_fetch_unittest.cc | 731 +++
src/lib/asiodns/tests/io_service_unittest.cc | 118 +
src/lib/asiodns/tests/run_unittests.cc | 29 +
src/lib/asiodns/udp_server.cc | 325 +
src/lib/asiodns/udp_server.h | 108 +
src/lib/asiolink/Makefile.am | 13 -
src/lib/asiolink/README | 161 +-
src/lib/asiolink/asiodef.cc | 37 -
src/lib/asiolink/asiodef.h | 21 -
src/lib/asiolink/asiodef.msg | 56 -
src/lib/asiolink/asiolink.h | 9 -
src/lib/asiolink/asiolink_utilities.h | 61 -
src/lib/asiolink/dns_answer.h | 73 -
src/lib/asiolink/dns_lookup.h | 83 -
src/lib/asiolink/dns_server.h | 155 -
src/lib/asiolink/dns_service.cc | 200 -
src/lib/asiolink/dns_service.h | 112 -
src/lib/asiolink/dummy_io_cb.h | 2 +
src/lib/asiolink/interval_timer.cc | 67 +-
src/lib/asiolink/interval_timer.h | 16 +-
src/lib/asiolink/io_address.cc | 4 +-
src/lib/asiolink/io_address.h | 4 +-
src/lib/asiolink/io_asio_socket.h | 9 +-
src/lib/asiolink/io_endpoint.cc | 15 +
src/lib/asiolink/io_endpoint.h | 51 +-
src/lib/asiolink/io_error.h | 4 +-
src/lib/asiolink/io_fetch.cc | 355 -
src/lib/asiolink/io_fetch.h | 179 -
src/lib/asiolink/io_message.h | 4 +-
src/lib/asiolink/io_service.cc | 4 +-
src/lib/asiolink/io_service.h | 6 +-
src/lib/asiolink/io_socket.cc | 8 +-
src/lib/asiolink/io_socket.h | 2 +
src/lib/asiolink/qid_gen.cc | 54 -
src/lib/asiolink/qid_gen.h | 85 -
src/lib/asiolink/simple_callback.h | 4 +-
src/lib/asiolink/tcp_endpoint.h | 12 +-
src/lib/asiolink/tcp_server.cc | 239 -
src/lib/asiolink/tcp_server.h | 123 -
src/lib/asiolink/tcp_socket.h | 16 +-
src/lib/asiolink/tests/Makefile.am | 17 +-
.../asiolink/tests/asiolink_utilities_unittest.cc | 74 -
src/lib/asiolink/tests/interval_timer_unittest.cc | 28 +-
src/lib/asiolink/tests/io_address_unittest.cc | 2 +-
src/lib/asiolink/tests/io_endpoint_unittest.cc | 194 +-
src/lib/asiolink/tests/io_fetch_unittest.cc | 608 --
src/lib/asiolink/tests/io_service_unittest.cc | 116 -
src/lib/asiolink/tests/io_socket_unittest.cc | 2 +-
src/lib/asiolink/tests/qid_gen_unittest.cc | 59 -
src/lib/asiolink/tests/run_unittests.cc | 11 +-
src/lib/asiolink/tests/tcp_endpoint_unittest.cc | 2 +-
src/lib/asiolink/tests/tcp_socket_unittest.cc | 8 +-
src/lib/asiolink/tests/udp_endpoint_unittest.cc | 2 +-
src/lib/asiolink/tests/udp_socket_unittest.cc | 8 +-
src/lib/asiolink/udp_endpoint.h | 12 +-
src/lib/asiolink/udp_server.cc | 322 -
src/lib/asiolink/udp_server.h | 106 -
src/lib/asiolink/udp_socket.h | 6 +-
src/lib/bench/benchmark_util.cc | 3 +-
src/lib/bench/tests/Makefile.am | 5 +-
src/lib/bench/tests/loadquery_unittest.cc | 3 +-
src/lib/bench/tests/run_unittests.cc | 3 +-
src/lib/cache/Makefile.am | 12 +-
src/lib/cache/TODO | 5 +-
src/lib/cache/cache_messages.mes | 148 +
src/lib/cache/local_zone_data.cc | 4 +
src/lib/cache/logger.cc | 23 +
src/lib/cache/logger.h | 44 +
src/lib/cache/message_cache.cc | 43 +-
src/lib/cache/message_cache.h | 24 +-
src/lib/cache/message_entry.cc | 5 +-
src/lib/cache/message_entry.h | 2 +
src/lib/cache/resolver_cache.cc | 42 +-
src/lib/cache/resolver_cache.h | 20 +-
src/lib/cache/rrset_cache.cc | 45 +-
src/lib/cache/rrset_cache.h | 36 +-
src/lib/cache/tests/Makefile.am | 29 +-
src/lib/cache/tests/cache_test_messagefromfile.h | 4 +-
src/lib/cache/tests/cache_test_sectioncount.h | 2 +-
src/lib/cache/tests/message_cache_unittest.cc | 3 +-
src/lib/cache/tests/message_entry_unittest.cc | 2 +-
src/lib/cache/tests/run_unittests.cc | 7 +-
src/lib/cc/Makefile.am | 13 +-
src/lib/cc/cc_messages.mes | 108 +
src/lib/cc/data.cc | 11 +-
src/lib/cc/data.h | 2 +-
src/lib/cc/logger.cc | 23 +
src/lib/cc/logger.h | 47 +
src/lib/cc/session.cc | 32 +-
src/lib/cc/tests/Makefile.am | 3 +-
src/lib/cc/tests/data_unittests.cc | 15 +
src/lib/cc/tests/run_unittests.cc | 7 +-
src/lib/config/Makefile.am | 20 +-
src/lib/config/ccsession.cc | 299 +-
src/lib/config/ccsession.h | 145 +-
src/lib/config/config_data.cc | 136 +-
src/lib/config/config_data.h | 10 +
src/lib/config/config_log.cc | 26 +
src/lib/config/config_log.h | 38 +
src/lib/config/config_messages.mes | 59 +
src/lib/config/module_spec.cc | 19 +-
src/lib/config/tests/Makefile.am | 6 +-
src/lib/config/tests/ccsession_unittests.cc | 274 +-
src/lib/config/tests/config_data_unittests.cc | 20 +-
.../config/tests/data_def_unittests_config.h.in | 1 +
src/lib/config/tests/fake_session.cc | 30 +-
src/lib/config/tests/fake_session.h | 9 +
src/lib/config/tests/module_spec_unittests.cc | 4 +
src/lib/config/tests/run_unittests.cc | 5 +-
src/lib/config/tests/testdata/Makefile.am | 3 +
src/lib/config/tests/testdata/data22_10.data | 11 +
src/lib/config/tests/testdata/spec30.spec | 45 +
src/lib/config/tests/testdata/spec31.spec | 63 +
src/lib/cryptolink/Makefile.am | 14 +
src/lib/cryptolink/crypto_hmac.cc | 281 +
src/lib/cryptolink/crypto_hmac.h | 209 +
src/lib/cryptolink/cryptolink.cc | 69 +
src/lib/cryptolink/cryptolink.h | 208 +
src/lib/cryptolink/tests/Makefile.am | 27 +
src/lib/cryptolink/tests/crypto_unittests.cc | 610 ++
src/lib/cryptolink/tests/run_unittests.cc | 23 +
src/lib/datasrc/Makefile.am | 15 +-
src/lib/datasrc/cache.cc | 36 +-
src/lib/datasrc/data_source.cc | 163 +-
src/lib/datasrc/datasrc_messages.mes | 493 ++
src/lib/datasrc/logger.cc | 23 +
src/lib/datasrc/logger.h | 46 +
src/lib/datasrc/memory_datasrc.cc | 65 +-
src/lib/datasrc/query.cc | 2 +-
src/lib/datasrc/result.h | 1 -
src/lib/datasrc/sqlite3_datasrc.cc | 35 +-
src/lib/datasrc/static_datasrc.cc | 5 +
src/lib/datasrc/tests/Makefile.am | 13 +-
src/lib/datasrc/tests/datasrc_unittest.cc | 199 +-
src/lib/datasrc/tests/logger_unittest.cc | 31 +
src/lib/datasrc/tests/memory_datasrc_unittest.cc | 1 -
src/lib/datasrc/tests/query_unittest.cc | 2 +-
src/lib/datasrc/tests/rbtree_unittest.cc | 2 +-
src/lib/datasrc/tests/run_unittests.cc | 6 +-
src/lib/datasrc/tests/test_datasrc.cc | 21 +-
src/lib/datasrc/zone.h | 1 -
src/lib/dns/Makefile.am | 81 +-
src/lib/dns/benchmarks/Makefile.am | 17 +
src/lib/dns/benchmarks/README | 10 +
.../benchmarks/benchmarkdata/rdatarender_data_com | 32 +
.../benchmarkdata/rdatarender_data_nxdomain | 10 +
.../benchmarks/benchmarkdata/rdatarender_data_org | 22 +
src/lib/dns/benchmarks/rdatarender_bench.cc | 188 +
src/lib/dns/buffer.h | 431 --
src/lib/dns/dnssectime.cc | 211 -
src/lib/dns/dnssectime.h | 145 -
src/lib/dns/edns.cc | 30 +-
src/lib/dns/edns.h | 18 +-
src/lib/dns/gen-rdatacode.py.in | 15 +-
src/lib/dns/message.cc | 423 +-
src/lib/dns/message.h | 69 +-
src/lib/dns/messagerenderer.cc | 81 +-
src/lib/dns/messagerenderer.h | 225 +-
src/lib/dns/name.cc | 6 +-
src/lib/dns/name.h | 13 +-
src/lib/dns/python/Makefile.am | 19 +-
src/lib/dns/python/edns_python.cc | 18 +-
src/lib/dns/python/message_python.cc | 203 +-
src/lib/dns/python/messagerenderer_python.cc | 206 +-
src/lib/dns/python/messagerenderer_python.h | 52 +
src/lib/dns/python/name_python.cc | 530 +-
src/lib/dns/python/name_python.h | 84 +
src/lib/dns/python/pydnspp.cc | 56 +-
src/lib/dns/python/pydnspp_common.cc | 18 +-
src/lib/dns/python/pydnspp_common.h | 29 +-
src/lib/dns/python/pydnspp_towire.h | 127 +
src/lib/dns/python/question_python.cc | 6 +-
src/lib/dns/python/rcode_python.cc | 167 +-
src/lib/dns/python/rcode_python.h | 57 +
src/lib/dns/python/rdata_python.cc | 1 +
src/lib/dns/python/rrclass_python.cc | 12 +-
src/lib/dns/python/rrset_python.cc | 9 +-
src/lib/dns/python/rrttl_python.cc | 9 +-
src/lib/dns/python/rrtype_python.cc | 13 +-
src/lib/dns/python/tests/Makefile.am | 13 +-
src/lib/dns/python/tests/edns_python_test.py | 13 +-
src/lib/dns/python/tests/message_python_test.py | 114 +-
.../python/tests/messagerenderer_python_test.py | 5 +
src/lib/dns/python/tests/name_python_test.py | 19 +-
src/lib/dns/python/tests/question_python_test.py | 2 +-
src/lib/dns/python/tests/rcode_python_test.py | 19 +-
src/lib/dns/python/tests/rrclass_python_test.py | 9 +-
src/lib/dns/python/tests/rrttl_python_test.py | 10 +-
src/lib/dns/python/tests/rrtype_python_test.py | 16 +-
src/lib/dns/python/tests/tsig_python_test.py | 554 ++
src/lib/dns/python/tests/tsig_rdata_python_test.py | 30 +
src/lib/dns/python/tests/tsigerror_python_test.py | 97 +
src/lib/dns/python/tests/tsigkey_python_test.py | 56 +-
src/lib/dns/python/tests/tsigrecord_python_test.py | 44 +
src/lib/dns/python/tsig_python.cc | 364 ++
src/lib/dns/python/tsig_python.h | 47 +
src/lib/dns/python/tsig_rdata_python.cc | 369 ++
src/lib/dns/python/tsig_rdata_python.h | 57 +
src/lib/dns/python/tsigerror_python.cc | 370 ++
src/lib/dns/python/tsigerror_python.h | 52 +
src/lib/dns/python/tsigerror_python_inc.cc | 83 +
src/lib/dns/python/tsigkey_python.cc | 380 +-
src/lib/dns/python/tsigkey_python.h | 53 +
src/lib/dns/python/tsigrecord_python.cc | 311 +
src/lib/dns/python/tsigrecord_python.h | 53 +
src/lib/dns/question.cc | 5 +-
src/lib/dns/question.h | 16 +-
src/lib/dns/rdata.cc | 14 +-
src/lib/dns/rdata.h | 29 +-
src/lib/dns/rdata/any_255/tsig_250.cc | 25 +-
src/lib/dns/rdata/ch_3/a_1.cc | 5 +-
src/lib/dns/rdata/generic/cname_5.cc | 5 +-
src/lib/dns/rdata/generic/dname_39.cc | 5 +-
src/lib/dns/rdata/generic/dnskey_48.cc | 8 +-
src/lib/dns/rdata/generic/ds_43.cc | 9 +-
src/lib/dns/rdata/generic/mx_15.cc | 5 +-
src/lib/dns/rdata/generic/ns_2.cc | 5 +-
src/lib/dns/rdata/generic/nsec3_50.cc | 11 +-
src/lib/dns/rdata/generic/nsec3param_51.cc | 8 +-
src/lib/dns/rdata/generic/nsec_47.cc | 8 +-
src/lib/dns/rdata/generic/opt_41.cc | 5 +-
src/lib/dns/rdata/generic/ptr_12.cc | 5 +-
src/lib/dns/rdata/generic/rp_17.cc | 125 +
src/lib/dns/rdata/generic/rp_17.h | 88 +
src/lib/dns/rdata/generic/rrsig_46.cc | 10 +-
src/lib/dns/rdata/generic/soa_6.cc | 5 +-
src/lib/dns/rdata/generic/txt_16.cc | 5 +-
src/lib/dns/rdata/hs_4/a_1.cc | 5 +-
src/lib/dns/rdata/in_1/a_1.cc | 5 +-
src/lib/dns/rdata/in_1/aaaa_28.cc | 5 +-
src/lib/dns/rdata/template.cc | 7 +-
src/lib/dns/rdata/template.h | 2 +-
src/lib/dns/rdatafields.cc | 223 +
src/lib/dns/rdatafields.h | 427 ++
src/lib/dns/rrclass-placeholder.h | 15 +-
src/lib/dns/rrclass.cc | 5 +-
src/lib/dns/rrparamregistry-placeholder.cc | 1 +
src/lib/dns/rrparamregistry.h | 4 +-
src/lib/dns/rrset.cc | 9 +-
src/lib/dns/rrset.h | 15 +-
src/lib/dns/rrttl.cc | 5 +-
src/lib/dns/rrttl.h | 15 +-
src/lib/dns/rrtype-placeholder.h | 15 +-
src/lib/dns/rrtype.cc | 6 +-
src/lib/dns/tests/Makefile.am | 21 +-
src/lib/dns/tests/base32hex_unittest.cc | 159 -
src/lib/dns/tests/base64_unittest.cc | 93 -
src/lib/dns/tests/buffer_unittest.cc | 183 -
src/lib/dns/tests/dnssectime_unittest.cc | 163 -
src/lib/dns/tests/edns_unittest.cc | 3 +-
src/lib/dns/tests/hex_unittest.cc | 120 -
src/lib/dns/tests/message_unittest.cc | 195 +-
src/lib/dns/tests/messagerenderer_unittest.cc | 4 +-
src/lib/dns/tests/name_unittest.cc | 3 +-
src/lib/dns/tests/question_unittest.cc | 3 +-
src/lib/dns/tests/rdata_cname_unittest.cc | 3 +-
src/lib/dns/tests/rdata_dname_unittest.cc | 3 +-
src/lib/dns/tests/rdata_dnskey_unittest.cc | 3 +-
src/lib/dns/tests/rdata_ds_unittest.cc | 3 +-
src/lib/dns/tests/rdata_in_a_unittest.cc | 3 +-
src/lib/dns/tests/rdata_in_aaaa_unittest.cc | 3 +-
src/lib/dns/tests/rdata_mx_unittest.cc | 3 +-
src/lib/dns/tests/rdata_ns_unittest.cc | 3 +-
src/lib/dns/tests/rdata_nsec3_unittest.cc | 6 +-
src/lib/dns/tests/rdata_nsec3param_unittest.cc | 8 +-
src/lib/dns/tests/rdata_nsec_unittest.cc | 3 +-
src/lib/dns/tests/rdata_opt_unittest.cc | 3 +-
src/lib/dns/tests/rdata_ptr_unittest.cc | 3 +-
src/lib/dns/tests/rdata_rp_unittest.cc | 163 +
src/lib/dns/tests/rdata_rrsig_unittest.cc | 5 +-
src/lib/dns/tests/rdata_soa_unittest.cc | 3 +-
src/lib/dns/tests/rdata_tsig_unittest.cc | 3 +-
src/lib/dns/tests/rdata_txt_unittest.cc | 3 +-
src/lib/dns/tests/rdata_unittest.cc | 3 +-
src/lib/dns/tests/rdata_unittest.h | 4 +-
src/lib/dns/tests/rdatafields_unittest.cc | 380 ++
src/lib/dns/tests/rrclass_unittest.cc | 3 +-
src/lib/dns/tests/rrparamregistry_unittest.cc | 1 +
src/lib/dns/tests/rrset_unittest.cc | 3 +-
src/lib/dns/tests/rrttl_unittest.cc | 3 +-
src/lib/dns/tests/rrtype_unittest.cc | 3 +-
src/lib/dns/tests/run_unittests.cc | 6 +-
src/lib/dns/tests/sha1_unittest.cc | 108 -
src/lib/dns/tests/testdata/Makefile.am | 41 +-
src/lib/dns/tests/testdata/gen-wiredata.py.in | 140 +-
src/lib/dns/tests/testdata/message_fromWire12.spec | 21 +
src/lib/dns/tests/testdata/message_fromWire13.spec | 20 +
src/lib/dns/tests/testdata/message_fromWire14.spec | 21 +
src/lib/dns/tests/testdata/message_fromWire15.spec | 22 +
src/lib/dns/tests/testdata/message_fromWire16.spec | 21 +
src/lib/dns/tests/testdata/message_toText1.spec | 24 +
src/lib/dns/tests/testdata/message_toText1.txt | 14 +
src/lib/dns/tests/testdata/message_toText2.spec | 14 +
src/lib/dns/tests/testdata/message_toText2.txt | 8 +
src/lib/dns/tests/testdata/message_toText3.spec | 31 +
src/lib/dns/tests/testdata/message_toText3.txt | 17 +
src/lib/dns/tests/testdata/message_toWire2.spec | 21 +
src/lib/dns/tests/testdata/message_toWire3.spec | 22 +
src/lib/dns/tests/testdata/rdata_rp_fromWire1.spec | 6 +
src/lib/dns/tests/testdata/rdata_rp_fromWire2.spec | 12 +
src/lib/dns/tests/testdata/rdata_rp_fromWire3.spec | 7 +
src/lib/dns/tests/testdata/rdata_rp_fromWire4.spec | 7 +
src/lib/dns/tests/testdata/rdata_rp_fromWire5.spec | 7 +
src/lib/dns/tests/testdata/rdata_rp_fromWire6.spec | 7 +
src/lib/dns/tests/testdata/rdata_rp_toWire1.spec | 8 +
src/lib/dns/tests/testdata/rdata_rp_toWire2.spec | 14 +
src/lib/dns/tests/testdata/rdatafields1.spec | 10 +
src/lib/dns/tests/testdata/rdatafields2.spec | 11 +
src/lib/dns/tests/testdata/rdatafields3.spec | 11 +
src/lib/dns/tests/testdata/rdatafields4.spec | 7 +
src/lib/dns/tests/testdata/rdatafields5.spec | 12 +
src/lib/dns/tests/testdata/rdatafields6.spec | 13 +
src/lib/dns/tests/testdata/tsig_verify1.spec | 19 +
src/lib/dns/tests/testdata/tsig_verify10.spec | 22 +
src/lib/dns/tests/testdata/tsig_verify2.spec | 32 +
src/lib/dns/tests/testdata/tsig_verify3.spec | 26 +
src/lib/dns/tests/testdata/tsig_verify4.spec | 27 +
src/lib/dns/tests/testdata/tsig_verify5.spec | 26 +
src/lib/dns/tests/testdata/tsig_verify6.spec | 21 +
src/lib/dns/tests/testdata/tsig_verify7.spec | 21 +
src/lib/dns/tests/testdata/tsig_verify8.spec | 23 +
src/lib/dns/tests/testdata/tsig_verify9.spec | 21 +
src/lib/dns/tests/testdata/tsigrecord_toWire1.spec | 16 +
src/lib/dns/tests/testdata/tsigrecord_toWire2.spec | 19 +
src/lib/dns/tests/tsig_unittest.cc | 930 +++
src/lib/dns/tests/tsigerror_unittest.cc | 116 +
src/lib/dns/tests/tsigkey_unittest.cc | 141 +-
src/lib/dns/tests/tsigrecord_unittest.cc | 159 +
src/lib/dns/tsig.cc | 453 ++
src/lib/dns/tsig.h | 394 ++
src/lib/dns/tsigerror.cc | 68 +
src/lib/dns/tsigerror.h | 338 +
src/lib/dns/tsigkey.cc | 143 +-
src/lib/dns/tsigkey.h | 79 +-
src/lib/dns/tsigrecord.cc | 147 +
src/lib/dns/tsigrecord.h | 308 +
src/lib/dns/util/README | 31 -
src/lib/dns/util/base32hex.h | 61 -
src/lib/dns/util/base64.h | 76 -
src/lib/dns/util/base_n.cc | 398 --
src/lib/dns/util/hex.h | 62 -
src/lib/dns/util/sha1.cc | 484 --
src/lib/dns/util/sha1.h | 84 -
src/lib/exceptions/exceptions.h | 11 +
src/lib/exceptions/tests/run_unittests.cc | 3 +
src/lib/log/Makefile.am | 29 +-
src/lib/log/README | 482 +-
src/lib/log/compiler/Makefile.am | 6 +-
src/lib/log/compiler/message.cc | 311 +-
src/lib/log/debug_levels.h | 29 -
src/lib/log/dummylog.cc | 2 +-
src/lib/log/dummylog.h | 4 +-
src/lib/log/filename.cc | 138 -
src/lib/log/filename.h | 161 -
src/lib/log/log_formatter.cc | 42 +
src/lib/log/log_formatter.h | 219 +
src/lib/log/log_messages.cc | 63 +
src/lib/log/log_messages.h | 35 +
src/lib/log/log_messages.mes | 146 +
src/lib/log/logger.cc | 99 +-
src/lib/log/logger.h | 187 +-
src/lib/log/logger_impl.cc | 217 +-
src/lib/log/logger_impl.h | 202 +-
src/lib/log/logger_impl_log4cxx.cc | 241 -
src/lib/log/logger_impl_log4cxx.h | 315 -
src/lib/log/logger_level.cc | 48 +
src/lib/log/logger_level.h | 76 +
src/lib/log/logger_level_impl.cc | 217 +
src/lib/log/logger_level_impl.h | 127 +
src/lib/log/logger_levels.h | 42 -
src/lib/log/logger_manager.cc | 184 +
src/lib/log/logger_manager.h | 141 +
src/lib/log/logger_manager_impl.cc | 228 +
src/lib/log/logger_manager_impl.h | 171 +
src/lib/log/logger_name.cc | 59 +
src/lib/log/logger_name.h | 57 +
src/lib/log/logger_specification.h | 156 +
src/lib/log/logger_support.cc | 232 +-
src/lib/log/logger_support.h | 75 +-
src/lib/log/logimpl_messages.cc | 29 +
src/lib/log/logimpl_messages.h | 18 +
src/lib/log/logimpl_messages.mes | 43 +
src/lib/log/macros.h | 50 +
src/lib/log/message_dictionary.cc | 2 +-
src/lib/log/message_exception.cc | 26 -
src/lib/log/message_exception.h | 37 +-
src/lib/log/message_reader.cc | 192 +-
src/lib/log/message_reader.h | 19 +-
src/lib/log/messagedef.cc | 57 -
src/lib/log/messagedef.h | 32 -
src/lib/log/messagedef.mes | 119 -
src/lib/log/output_option.cc | 55 +
src/lib/log/output_option.h | 85 +
src/lib/log/root_logger_name.cc | 44 -
src/lib/log/root_logger_name.h | 46 -
src/lib/log/strutil.cc | 135 -
src/lib/log/strutil.h | 145 -
src/lib/log/tests/Makefile.am | 65 +-
src/lib/log/tests/console_test.sh.in | 67 +
src/lib/log/tests/destination_test.sh.in | 91 +
src/lib/log/tests/filename_unittest.cc | 179 -
src/lib/log/tests/init_logger_test.cc | 42 +
src/lib/log/tests/init_logger_test.sh.in | 110 +
src/lib/log/tests/local_file_test.sh.in | 83 +
src/lib/log/tests/log_formatter_unittest.cc | 126 +
src/lib/log/tests/logger_example.cc | 305 +
src/lib/log/tests/logger_impl_log4cxx_unittest.cc | 91 -
src/lib/log/tests/logger_level_impl_unittest.cc | 171 +
src/lib/log/tests/logger_level_unittest.cc | 82 +
src/lib/log/tests/logger_manager_unittest.cc | 321 +
src/lib/log/tests/logger_name_unittest.cc | 77 +
src/lib/log/tests/logger_specification_unittest.cc | 96 +
src/lib/log/tests/logger_support_test.cc | 104 -
src/lib/log/tests/logger_support_unittest.cc | 72 +
src/lib/log/tests/logger_unittest.cc | 113 +-
src/lib/log/tests/message_dictionary_unittest.cc | 4 +-
src/lib/log/tests/message_reader_unittest.cc | 67 +-
src/lib/log/tests/output_option_unittest.cc | 66 +
src/lib/log/tests/root_logger_name_unittest.cc | 50 -
src/lib/log/tests/run_time_init_test.sh.in | 89 -
src/lib/log/tests/run_unittests.cc | 6 +-
src/lib/log/tests/severity_test.sh.in | 89 +
src/lib/log/tests/strutil_unittest.cc | 214 -
src/lib/log/tests/tempdir.h.in | 29 +
src/lib/log/tests/xdebuglevel_unittest.cc | 203 -
src/lib/log/xdebuglevel.cc | 146 -
src/lib/log/xdebuglevel.h | 162 -
src/lib/nsas/Makefile.am | 36 +-
src/lib/nsas/fetchable.h | 2 +-
src/lib/nsas/glue_hints.cc | 168 +
src/lib/nsas/glue_hints.h | 71 +
src/lib/nsas/hash_deleter.h | 5 +-
src/lib/nsas/hash_table.h | 9 +-
src/lib/nsas/locks.h | 116 -
src/lib/nsas/lru_list.h | 234 -
src/lib/nsas/nameserver_address.cc | 2 +-
src/lib/nsas/nameserver_address_store.cc | 36 +-
src/lib/nsas/nameserver_address_store.h | 15 +-
src/lib/nsas/nameserver_entry.cc | 37 +-
src/lib/nsas/nameserver_entry.h | 9 +-
src/lib/nsas/nsas_entry.h | 10 +-
src/lib/nsas/nsas_log.cc | 26 +
src/lib/nsas/nsas_log.h | 53 +
src/lib/nsas/nsas_messages.mes | 69 +
src/lib/nsas/random_number_generator.h | 160 -
src/lib/nsas/tests/Makefile.am | 10 +-
src/lib/nsas/tests/address_entry_unittest.cc | 2 +-
src/lib/nsas/tests/fetchable_unittest.cc | 2 +-
src/lib/nsas/tests/hash_deleter_unittest.cc | 3 +-
src/lib/nsas/tests/lru_list_unittest.cc | 289 -
.../tests/nameserver_address_store_unittest.cc | 350 +-
src/lib/nsas/tests/nameserver_address_unittest.cc | 17 +-
src/lib/nsas/tests/nameserver_entry_unittest.cc | 40 +-
src/lib/nsas/tests/nsas_test.h | 145 +-
.../nsas/tests/random_number_generator_unittest.cc | 307 -
src/lib/nsas/tests/run_unittests.cc | 10 +-
src/lib/nsas/tests/zone_entry_unittest.cc | 13 +-
src/lib/nsas/zone_entry.cc | 26 +-
src/lib/nsas/zone_entry.h | 25 +-
src/lib/python/Makefile.am | 5 +
src/lib/python/bind10_config.py.in | 39 +-
src/lib/python/isc/Makefile.am | 7 +-
src/lib/python/isc/__init__.py | 1 -
src/lib/python/isc/cc/Makefile.am | 5 +
src/lib/python/isc/cc/message.py | 2 +-
src/lib/python/isc/cc/session.py | 37 +-
src/lib/python/isc/cc/tests/Makefile.am | 8 +
src/lib/python/isc/cc/tests/message_test.py | 5 +
src/lib/python/isc/cc/tests/session_test.py | 10 +
src/lib/python/isc/config/Makefile.am | 14 +
src/lib/python/isc/config/ccsession.py | 71 +-
src/lib/python/isc/config/cfgmgr.py | 173 +-
src/lib/python/isc/config/cfgmgr_messages.mes | 57 +
src/lib/python/isc/config/config_data.py | 100 +-
src/lib/python/isc/config/module_spec.py | 4 +-
src/lib/python/isc/config/tests/Makefile.am | 16 +-
src/lib/python/isc/config/tests/ccsession_test.py | 77 +-
src/lib/python/isc/config/tests/cfgmgr_test.py | 118 +-
.../python/isc/config/tests/config_data_test.py | 54 +-
.../isc/config/tests/unittest_fakesession.py | 9 +-
src/lib/python/isc/datasrc/Makefile.am | 5 +
src/lib/python/isc/datasrc/sqlite3_ds.py | 38 +-
src/lib/python/isc/datasrc/tests/Makefile.am | 10 +
.../python/isc/datasrc/tests/sqlite3_ds_test.py | 58 +-
src/lib/python/isc/log/Makefile.am | 37 +-
src/lib/python/isc/log/__init__.py | 34 +-
src/lib/python/isc/log/log.cc | 701 ++
src/lib/python/isc/log/log.py | 280 -
src/lib/python/isc/log/tests/Makefile.am | 18 +-
src/lib/python/isc/log/tests/check_output.sh | 3 +
src/lib/python/isc/log/tests/console.out | 4 +
src/lib/python/isc/log/tests/log_console.py.in | 15 +
src/lib/python/isc/log/tests/log_test.in | 26 -
src/lib/python/isc/log/tests/log_test.py | 350 +-
src/lib/python/isc/net/Makefile.am | 5 +
src/lib/python/isc/net/parse.py | 2 +-
src/lib/python/isc/net/tests/Makefile.am | 8 +
src/lib/python/isc/net/tests/parse_test.py | 2 +-
src/lib/python/isc/notify/Makefile.am | 5 +
src/lib/python/isc/notify/notify_out.py | 136 +-
src/lib/python/isc/notify/tests/Makefile.am | 2 +-
src/lib/python/isc/notify/tests/notify_out_test.py | 130 +-
src/lib/python/isc/testutils/Makefile.am | 6 +
src/lib/python/isc/testutils/README | 3 +
src/lib/python/isc/testutils/__init__.py | 17 +
src/lib/python/isc/testutils/parse_args.py | 30 +
src/lib/python/isc/testutils/tsigctx_mock.py | 53 +
src/lib/python/isc/util/Makefile.am | 7 +-
src/lib/python/isc/util/file.py | 29 +
src/lib/python/isc/util/process.py | 2 +-
src/lib/python/isc/util/tests/Makefile.am | 10 +-
src/lib/python/isc/util/tests/file_test.py | 32 +
src/lib/python/isc/util/tests/process_test.py | 2 +-
src/lib/rbmsgq/lib/cc.rb | 60 -
src/lib/rbmsgq/lib/cc/message.rb | 324 -
src/lib/rbmsgq/lib/cc/session.rb | 214 -
src/lib/resolve/Makefile.am | 22 +-
src/lib/resolve/recursive_query.cc | 628 ++-
src/lib/resolve/recursive_query.h | 83 +-
src/lib/resolve/resolve_log.cc | 26 +
src/lib/resolve/resolve_log.h | 53 +
src/lib/resolve/resolve_messages.mes | 154 +
src/lib/resolve/resolver_callback.h | 8 +-
src/lib/resolve/resolver_interface.h | 2 +-
src/lib/resolve/tests/Makefile.am | 11 +-
src/lib/resolve/tests/recursive_query_unittest.cc | 257 +-
.../resolve/tests/recursive_query_unittest_2.cc | 122 +-
.../resolve/tests/resolver_callback_unittest.cc | 8 +-
src/lib/resolve/tests/run_unittests.cc | 5 +-
src/lib/server_common/Makefile.am | 17 +-
src/lib/server_common/client.cc | 68 +
src/lib/server_common/client.h | 154 +
src/lib/server_common/keyring.cc | 71 +
src/lib/server_common/keyring.h | 102 +
src/lib/server_common/logger.cc | 23 +
src/lib/server_common/logger.h | 44 +
src/lib/server_common/portconfig.cc | 47 +-
src/lib/server_common/portconfig.h | 6 +-
src/lib/server_common/server_common_messages.mes | 73 +
src/lib/server_common/tests/Makefile.am | 18 +-
src/lib/server_common/tests/client_unittest.cc | 103 +
src/lib/server_common/tests/data_path.h.in | 16 +
src/lib/server_common/tests/keyring_test.cc | 149 +
src/lib/server_common/tests/portconfig_unittest.cc | 6 +-
src/lib/server_common/tests/run_unittests.cc | 6 +-
src/lib/server_common/tests/testdata/spec.spec | 6 +
src/lib/testutils/mockups.h | 4 +-
src/lib/testutils/srv_test.cc | 15 +-
src/lib/testutils/srv_test.h | 9 +-
src/lib/util/Makefile.am | 28 +
src/lib/util/buffer.h | 524 ++
.../{dns/util => util/encode}/base16_from_binary.h | 0
src/lib/util/encode/base32hex.h | 64 +
.../util => util/encode}/base32hex_from_binary.h | 0
src/lib/util/encode/base64.h | 79 +
src/lib/util/encode/base_n.cc | 421 ++
.../{dns/util => util/encode}/binary_from_base16.h | 0
.../util => util/encode}/binary_from_base32hex.h | 0
src/lib/util/encode/hex.h | 65 +
src/lib/util/filename.cc | 137 +
src/lib/util/filename.h | 161 +
src/lib/util/hash/sha1.cc | 492 ++
src/lib/util/hash/sha1.h | 91 +
src/lib/util/io/Makefile.am | 18 +
src/lib/util/io/fd.cc | 70 +
src/lib/util/io/fd.h | 61 +
src/lib/util/io/fd_share.cc | 139 +
src/lib/util/io/fd_share.h | 65 +
src/lib/util/io/fdshare_python.cc | 97 +
src/lib/util/io_utilities.h | 63 +
src/lib/util/locks.h | 115 +
src/lib/util/lru_list.h | 260 +
src/lib/util/python/mkpywrapper.py.in | 100 +
src/lib/util/python/pycppwrapper_util.h | 308 +
src/lib/util/python/wrapper_template.cc | 309 +
src/lib/util/python/wrapper_template.h | 59 +
src/lib/util/pyunittests/Makefile.am | 17 +
src/lib/util/pyunittests/pyunittests_util.cc | 84 +
src/lib/util/random/qid_gen.cc | 57 +
src/lib/util/random/qid_gen.h | 87 +
src/lib/util/random/random_number_generator.h | 208 +
src/lib/util/strutil.cc | 137 +
src/lib/util/strutil.h | 147 +
src/lib/util/tests/Makefile.am | 43 +
src/lib/util/tests/base32hex_unittest.cc | 164 +
src/lib/util/tests/base64_unittest.cc | 99 +
src/lib/util/tests/buffer_unittest.cc | 242 +
src/lib/util/tests/fd_share_tests.cc | 74 +
src/lib/util/tests/fd_tests.cc | 66 +
src/lib/util/tests/filename_unittest.cc | 179 +
src/lib/util/tests/hex_unittest.cc | 120 +
src/lib/util/tests/io_utilities_unittest.cc | 73 +
src/lib/util/tests/lru_list_unittest.cc | 428 ++
src/lib/util/tests/qid_gen_unittest.cc | 60 +
.../util/tests/random_number_generator_unittest.cc | 311 +
src/lib/util/tests/run_unittests.cc | 23 +
src/lib/util/tests/sha1_unittest.cc | 109 +
src/lib/util/tests/strutil_unittest.cc | 215 +
src/lib/util/tests/time_utilities_unittest.cc | 161 +
src/lib/util/time_utilities.cc | 207 +
src/lib/util/time_utilities.h | 173 +
src/lib/util/unittests/Makefile.am | 22 +
src/lib/util/unittests/README | 5 +
src/lib/util/unittests/fork.cc | 145 +
src/lib/util/unittests/fork.h | 52 +
src/lib/util/unittests/newhook.cc | 51 +
src/lib/util/unittests/newhook.h | 82 +
src/lib/util/unittests/resolver.h | 193 +
src/lib/util/unittests/run_all.cc | 95 +
src/lib/util/unittests/run_all.h | 52 +
src/lib/util/unittests/testdata.cc | 61 +
src/lib/util/unittests/testdata.h | 54 +
src/lib/util/unittests/textdata.h | 103 +
src/lib/xfr/Makefile.am | 14 +-
src/lib/xfr/fd_share.cc | 137 -
src/lib/xfr/fd_share.h | 42 -
src/lib/xfr/fdshare_python.cc | 84 -
src/lib/xfr/python_xfr.cc | 32 -
src/lib/xfr/xfrout_client.cc | 5 +-
tests/Makefile.am | 2 +-
tests/system/bindctl/tests.sh | 4 +-
tests/tools/Makefile.am | 1 +
tests/tools/badpacket/Makefile.am | 33 +
tests/tools/badpacket/README | 53 +
tests/tools/badpacket/badpacket.cc | 62 +
tests/tools/badpacket/command_options.cc | 333 +
tests/tools/badpacket/command_options.h | 162 +
tests/tools/badpacket/header_flags.h | 102 +
tests/tools/badpacket/option_info.cc | 114 +
tests/tools/badpacket/option_info.h | 174 +
tests/tools/badpacket/scan.cc | 312 +
tests/tools/badpacket/scan.h | 198 +
tests/tools/badpacket/tests/Makefile.am | 32 +
.../badpacket/tests/command_options_unittest.cc | 300 +
.../tools/badpacket/tests/header_flags_unittest.cc | 141 +
.../tools/badpacket/tests/option_info_unittest.cc | 161 +
tests/tools/badpacket/tests/run_unittests.cc | 25 +
tests/tools/badpacket/version.h | 26 +
tools/query_cmp/README | 27 +
tools/query_cmp/queries/dquery01 | 394 ++
tools/query_cmp/queries/dquery01_no-type | 316 +
tools/query_cmp/queries/dquery01_non-terminal | 317 +
tools/query_cmp/queries/dquery01_nxdomain | 316 +
tools/query_cmp/src/lib/compare_rrset.py | 285 +
tools/query_cmp/src/lib/handledns.py | 284 +
tools/query_cmp/src/lib/read_query.py | 93 +
tools/query_cmp/src/query_two_server.py | 102 +
tools/query_cmp/zonefile/example.com.txt | 1298 ++++
tools/query_cmp/zonefile/example.com.txt.signed | 6858 ++++++++++++++++++++
tools/system_messages.py | 419 ++
1163 files changed, 90613 insertions(+), 31410 deletions(-)
rename src/bin/stats/tests/isc/utils/__init__.py => TODO (100%)
create mode 100644 doc/guide/bind10-messages.html
create mode 100644 doc/guide/bind10-messages.xml
create mode 100644 ext/asio/asio/basic_streambuf_fwd.hpp
create mode 100644 ext/asio/asio/detail/array_fwd.hpp
create mode 100644 ext/asio/asio/detail/config.hpp
create mode 100644 ext/asio/asio/detail/descriptor_read_op.hpp
create mode 100644 ext/asio/asio/detail/descriptor_write_op.hpp
create mode 100644 ext/asio/asio/detail/gcc_arm_fenced_block.hpp
create mode 100644 ext/asio/asio/detail/gcc_hppa_fenced_block.hpp
create mode 100644 ext/asio/asio/detail/gcc_sync_fenced_block.hpp
create mode 100644 ext/asio/asio/detail/impl/descriptor_ops.ipp
create mode 100644 ext/asio/asio/detail/impl/dev_poll_reactor.hpp
create mode 100644 ext/asio/asio/detail/impl/dev_poll_reactor.ipp
create mode 100644 ext/asio/asio/detail/impl/epoll_reactor.hpp
create mode 100644 ext/asio/asio/detail/impl/epoll_reactor.ipp
create mode 100644 ext/asio/asio/detail/impl/eventfd_select_interrupter.ipp
create mode 100644 ext/asio/asio/detail/impl/kqueue_reactor.hpp
create mode 100644 ext/asio/asio/detail/impl/kqueue_reactor.ipp
create mode 100644 ext/asio/asio/detail/impl/pipe_select_interrupter.ipp
create mode 100644 ext/asio/asio/detail/impl/posix_event.ipp
create mode 100644 ext/asio/asio/detail/impl/posix_mutex.ipp
create mode 100644 ext/asio/asio/detail/impl/posix_thread.ipp
create mode 100644 ext/asio/asio/detail/impl/posix_tss_ptr.ipp
create mode 100644 ext/asio/asio/detail/impl/reactive_descriptor_service.ipp
create mode 100644 ext/asio/asio/detail/impl/reactive_serial_port_service.ipp
create mode 100644 ext/asio/asio/detail/impl/reactive_socket_service_base.ipp
create mode 100644 ext/asio/asio/detail/impl/resolver_service_base.ipp
create mode 100644 ext/asio/asio/detail/impl/select_reactor.hpp
create mode 100644 ext/asio/asio/detail/impl/select_reactor.ipp
create mode 100644 ext/asio/asio/detail/impl/service_registry.hpp
create mode 100644 ext/asio/asio/detail/impl/service_registry.ipp
create mode 100644 ext/asio/asio/detail/impl/socket_ops.ipp
create mode 100644 ext/asio/asio/detail/impl/socket_select_interrupter.ipp
create mode 100644 ext/asio/asio/detail/impl/strand_service.hpp
create mode 100644 ext/asio/asio/detail/impl/strand_service.ipp
create mode 100644 ext/asio/asio/detail/impl/task_io_service.hpp
create mode 100644 ext/asio/asio/detail/impl/task_io_service.ipp
create mode 100644 ext/asio/asio/detail/impl/throw_error.ipp
create mode 100644 ext/asio/asio/detail/impl/timer_queue.ipp
create mode 100644 ext/asio/asio/detail/impl/timer_queue_set.ipp
create mode 100644 ext/asio/asio/detail/impl/win_event.ipp
create mode 100644 ext/asio/asio/detail/impl/win_iocp_handle_service.ipp
create mode 100644 ext/asio/asio/detail/impl/win_iocp_io_service.hpp
create mode 100644 ext/asio/asio/detail/impl/win_iocp_io_service.ipp
create mode 100644 ext/asio/asio/detail/impl/win_iocp_serial_port_service.ipp
create mode 100644 ext/asio/asio/detail/impl/win_iocp_socket_service_base.ipp
create mode 100644 ext/asio/asio/detail/impl/win_mutex.ipp
create mode 100644 ext/asio/asio/detail/impl/win_thread.ipp
create mode 100644 ext/asio/asio/detail/impl/win_tss_ptr.ipp
create mode 100644 ext/asio/asio/detail/impl/winsock_init.ipp
create mode 100644 ext/asio/asio/detail/object_pool.hpp
create mode 100644 ext/asio/asio/detail/reactive_null_buffers_op.hpp
create mode 100644 ext/asio/asio/detail/reactive_socket_accept_op.hpp
create mode 100644 ext/asio/asio/detail/reactive_socket_connect_op.hpp
create mode 100644 ext/asio/asio/detail/reactive_socket_recv_op.hpp
create mode 100644 ext/asio/asio/detail/reactive_socket_recvfrom_op.hpp
create mode 100644 ext/asio/asio/detail/reactive_socket_send_op.hpp
create mode 100644 ext/asio/asio/detail/reactive_socket_sendto_op.hpp
create mode 100644 ext/asio/asio/detail/reactive_socket_service_base.hpp
create mode 100644 ext/asio/asio/detail/regex_fwd.hpp
create mode 100644 ext/asio/asio/detail/resolve_endpoint_op.hpp
create mode 100644 ext/asio/asio/detail/resolve_op.hpp
create mode 100644 ext/asio/asio/detail/resolver_service_base.hpp
create mode 100644 ext/asio/asio/detail/shared_ptr.hpp
create mode 100644 ext/asio/asio/detail/wait_handler.hpp
create mode 100644 ext/asio/asio/detail/weak_ptr.hpp
create mode 100644 ext/asio/asio/detail/win_iocp_handle_read_op.hpp
create mode 100644 ext/asio/asio/detail/win_iocp_handle_write_op.hpp
create mode 100644 ext/asio/asio/detail/win_iocp_null_buffers_op.hpp
create mode 100644 ext/asio/asio/detail/win_iocp_overlapped_op.hpp
create mode 100644 ext/asio/asio/detail/win_iocp_socket_accept_op.hpp
create mode 100644 ext/asio/asio/detail/win_iocp_socket_recv_op.hpp
create mode 100644 ext/asio/asio/detail/win_iocp_socket_recvfrom_op.hpp
create mode 100644 ext/asio/asio/detail/win_iocp_socket_send_op.hpp
create mode 100644 ext/asio/asio/detail/win_iocp_socket_service_base.hpp
create mode 100644 ext/asio/asio/impl/error.ipp
create mode 100644 ext/asio/asio/impl/io_service.hpp
create mode 100644 ext/asio/asio/impl/read.hpp
create mode 100644 ext/asio/asio/impl/read_at.hpp
create mode 100644 ext/asio/asio/impl/read_until.hpp
create mode 100644 ext/asio/asio/impl/serial_port_base.hpp
create mode 100644 ext/asio/asio/impl/src.cpp
create mode 100644 ext/asio/asio/impl/src.hpp
create mode 100644 ext/asio/asio/impl/write.hpp
create mode 100644 ext/asio/asio/impl/write_at.hpp
create mode 100644 ext/asio/asio/ip/detail/endpoint.hpp
create mode 100644 ext/asio/asio/ip/detail/impl/endpoint.ipp
create mode 100644 ext/asio/asio/ip/impl/address.hpp
create mode 100644 ext/asio/asio/ip/impl/address.ipp
create mode 100644 ext/asio/asio/ip/impl/address_v4.hpp
create mode 100644 ext/asio/asio/ip/impl/address_v4.ipp
create mode 100644 ext/asio/asio/ip/impl/address_v6.hpp
create mode 100644 ext/asio/asio/ip/impl/address_v6.ipp
create mode 100644 ext/asio/asio/ip/impl/basic_endpoint.hpp
create mode 100644 ext/asio/asio/ip/impl/host_name.ipp
create mode 100644 ext/asio/asio/local/detail/endpoint.hpp
create mode 100644 ext/asio/asio/local/detail/impl/endpoint.ipp
create mode 100644 src/bin/auth/auth_config.cc
rename src/bin/auth/{config.h => auth_config.h} (100%)
create mode 100644 src/bin/auth/auth_log.cc
create mode 100644 src/bin/auth/auth_log.h
create mode 100644 src/bin/auth/auth_messages.mes
create mode 100644 src/bin/auth/common.cc
delete mode 100644 src/bin/auth/config.cc
create mode 100644 src/bin/auth/tests/common_unittest.cc
create mode 100644 src/bin/bind10/bind10_messages.mes
mode change 100644 => 100755 src/bin/bind10/run_bind10.sh.in
delete mode 100755 src/bin/bind10/tests/bind10_test.in
mode change 100644 => 100755 src/bin/bindctl/run_bindctl.sh.in
create mode 100644 src/bin/cfgmgr/plugins/Makefile.am
create mode 100644 src/bin/cfgmgr/plugins/README
create mode 100644 src/bin/cfgmgr/plugins/b10logging.py
create mode 100644 src/bin/cfgmgr/plugins/logging.spec
create mode 100644 src/bin/cfgmgr/plugins/tests/Makefile.am
create mode 100644 src/bin/cfgmgr/plugins/tests/logging_test.py
create mode 100644 src/bin/cfgmgr/plugins/tests/tsig_keys_test.py
create mode 100644 src/bin/cfgmgr/plugins/tsig_keys.py
create mode 100644 src/bin/cfgmgr/plugins/tsig_keys.spec
create mode 100644 src/bin/cfgmgr/tests/testdata/plugins/testplugin.py
create mode 100644 src/bin/cmdctl/cmdctl_messages.mes
create mode 100644 src/bin/dhcp6/.gitignore
create mode 100644 src/bin/dhcp6/Makefile.am
create mode 100644 src/bin/dhcp6/b10-dhcp6.8
create mode 100644 src/bin/dhcp6/dhcp6.h
create mode 100644 src/bin/dhcp6/dhcp6.spec
create mode 100644 src/bin/dhcp6/main.cc
create mode 100644 src/bin/dhcp6/spec_config.h.pre.in
create mode 100644 src/bin/dhcp6/tests/Makefile.am
create mode 100644 src/bin/dhcp6/tests/dhcp6_test.py
create mode 100644 src/bin/host/b10-host.1
create mode 100644 src/bin/host/b10-host.xml
mode change 100644 => 100755 src/bin/loadzone/run_loadzone.sh.in
create mode 100644 src/bin/resolver/resolver_log.cc
create mode 100644 src/bin/resolver/resolver_log.h
create mode 100644 src/bin/resolver/resolver_messages.mes
create mode 100644 src/bin/sockcreator/Makefile.am
create mode 100644 src/bin/sockcreator/README
create mode 100644 src/bin/sockcreator/main.cc
create mode 100644 src/bin/sockcreator/sockcreator.cc
create mode 100644 src/bin/sockcreator/sockcreator.h
create mode 100644 src/bin/sockcreator/tests/Makefile.am
create mode 100644 src/bin/sockcreator/tests/run_unittests.cc
create mode 100644 src/bin/sockcreator/tests/sockcreator_tests.cc
create mode 100644 src/bin/stats/b10-stats-httpd.8
create mode 100644 src/bin/stats/b10-stats-httpd.xml
delete mode 100644 src/bin/stats/run_b10-stats.sh.in
delete mode 100644 src/bin/stats/run_b10-stats_stub.sh.in
create mode 100644 src/bin/stats/stats-httpd-xml.tpl
create mode 100644 src/bin/stats/stats-httpd-xsd.tpl
create mode 100644 src/bin/stats/stats-httpd-xsl.tpl
create mode 100644 src/bin/stats/stats-httpd.spec
create mode 100644 src/bin/stats/stats-schema.spec
mode change 100755 => 100644 src/bin/stats/stats.py.in
create mode 100644 src/bin/stats/stats.spec
delete mode 100644 src/bin/stats/stats.spec.pre.in
create mode 100755 src/bin/stats/stats_httpd.py.in
delete mode 100755 src/bin/stats/stats_stub.py.in
create mode 100644 src/bin/stats/tests/b10-stats-httpd_test.py
delete mode 100644 src/bin/stats/tests/b10-stats_stub_test.py
create mode 100644 src/bin/stats/tests/fake_select.py
create mode 100644 src/bin/stats/tests/fake_socket.py
create mode 100644 src/bin/stats/tests/http/Makefile.am
copy src/bin/stats/tests/{isc => http}/__init__.py (100%)
create mode 100644 src/bin/stats/tests/http/server.py
delete mode 100644 src/bin/stats/tests/isc/utils/Makefile.am
delete mode 100644 src/bin/stats/tests/isc/utils/process.py
delete mode 100644 src/bin/stats/tests/stats_test.in
create mode 100644 src/bin/xfrin/xfrin_messages.mes
delete mode 100644 src/bin/xfrout/tests/xfrout_test.py
create mode 100644 src/bin/xfrout/tests/xfrout_test.py.in
create mode 100644 src/bin/xfrout/xfrout_messages.mes
create mode 100644 src/lib/acl/Makefile.am
create mode 100644 src/lib/acl/acl.h
create mode 100644 src/lib/acl/check.h
create mode 100644 src/lib/acl/dns.cc
create mode 100644 src/lib/acl/dns.h
create mode 100644 src/lib/acl/ip_check.cc
create mode 100644 src/lib/acl/ip_check.h
create mode 100644 src/lib/acl/loader.cc
create mode 100644 src/lib/acl/loader.h
create mode 100644 src/lib/acl/logic_check.h
create mode 100644 src/lib/acl/tests/Makefile.am
create mode 100644 src/lib/acl/tests/acl_test.cc
create mode 100644 src/lib/acl/tests/check_test.cc
create mode 100644 src/lib/acl/tests/creators.h
create mode 100644 src/lib/acl/tests/dns_test.cc
create mode 100644 src/lib/acl/tests/ip_check_unittest.cc
create mode 100644 src/lib/acl/tests/loader_test.cc
create mode 100644 src/lib/acl/tests/logcheck.h
create mode 100644 src/lib/acl/tests/logic_check_test.cc
create mode 100644 src/lib/acl/tests/run_unittests.cc
create mode 100644 src/lib/acl/tests/sockaddr.h
create mode 100644 src/lib/asiodns/Makefile.am
create mode 100644 src/lib/asiodns/README
create mode 100644 src/lib/asiodns/asiodns.h
create mode 100644 src/lib/asiodns/asiodns_messages.mes
create mode 100644 src/lib/asiodns/dns_answer.h
create mode 100644 src/lib/asiodns/dns_lookup.h
create mode 100644 src/lib/asiodns/dns_server.h
create mode 100644 src/lib/asiodns/dns_service.cc
create mode 100644 src/lib/asiodns/dns_service.h
create mode 100644 src/lib/asiodns/io_fetch.cc
create mode 100644 src/lib/asiodns/io_fetch.h
create mode 100644 src/lib/asiodns/tcp_server.cc
create mode 100644 src/lib/asiodns/tcp_server.h
create mode 100644 src/lib/asiodns/tests/Makefile.am
create mode 100644 src/lib/asiodns/tests/dns_server_unittest.cc
create mode 100644 src/lib/asiodns/tests/io_fetch_unittest.cc
create mode 100644 src/lib/asiodns/tests/io_service_unittest.cc
create mode 100644 src/lib/asiodns/tests/run_unittests.cc
create mode 100644 src/lib/asiodns/udp_server.cc
create mode 100644 src/lib/asiodns/udp_server.h
delete mode 100644 src/lib/asiolink/asiodef.cc
delete mode 100644 src/lib/asiolink/asiodef.h
delete mode 100644 src/lib/asiolink/asiodef.msg
delete mode 100644 src/lib/asiolink/asiolink_utilities.h
delete mode 100644 src/lib/asiolink/dns_answer.h
delete mode 100644 src/lib/asiolink/dns_lookup.h
delete mode 100644 src/lib/asiolink/dns_server.h
delete mode 100644 src/lib/asiolink/dns_service.cc
delete mode 100644 src/lib/asiolink/dns_service.h
delete mode 100644 src/lib/asiolink/io_fetch.cc
delete mode 100644 src/lib/asiolink/io_fetch.h
delete mode 100644 src/lib/asiolink/qid_gen.cc
delete mode 100644 src/lib/asiolink/qid_gen.h
delete mode 100644 src/lib/asiolink/tcp_server.cc
delete mode 100644 src/lib/asiolink/tcp_server.h
delete mode 100644 src/lib/asiolink/tests/asiolink_utilities_unittest.cc
delete mode 100644 src/lib/asiolink/tests/io_fetch_unittest.cc
delete mode 100644 src/lib/asiolink/tests/io_service_unittest.cc
delete mode 100644 src/lib/asiolink/tests/qid_gen_unittest.cc
delete mode 100644 src/lib/asiolink/udp_server.cc
delete mode 100644 src/lib/asiolink/udp_server.h
create mode 100644 src/lib/cache/cache_messages.mes
create mode 100644 src/lib/cache/logger.cc
create mode 100644 src/lib/cache/logger.h
create mode 100644 src/lib/cc/cc_messages.mes
create mode 100644 src/lib/cc/logger.cc
create mode 100644 src/lib/cc/logger.h
create mode 100644 src/lib/config/config_log.cc
create mode 100644 src/lib/config/config_log.h
create mode 100644 src/lib/config/config_messages.mes
create mode 100644 src/lib/config/tests/testdata/data22_10.data
create mode 100644 src/lib/config/tests/testdata/spec30.spec
create mode 100644 src/lib/config/tests/testdata/spec31.spec
create mode 100644 src/lib/cryptolink/Makefile.am
create mode 100644 src/lib/cryptolink/crypto_hmac.cc
create mode 100644 src/lib/cryptolink/crypto_hmac.h
create mode 100644 src/lib/cryptolink/cryptolink.cc
create mode 100644 src/lib/cryptolink/cryptolink.h
create mode 100644 src/lib/cryptolink/tests/Makefile.am
create mode 100644 src/lib/cryptolink/tests/crypto_unittests.cc
create mode 100644 src/lib/cryptolink/tests/run_unittests.cc
create mode 100644 src/lib/datasrc/datasrc_messages.mes
create mode 100644 src/lib/datasrc/logger.cc
create mode 100644 src/lib/datasrc/logger.h
create mode 100644 src/lib/datasrc/tests/logger_unittest.cc
create mode 100644 src/lib/dns/benchmarks/Makefile.am
create mode 100644 src/lib/dns/benchmarks/README
create mode 100644 src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_com
create mode 100644 src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_nxdomain
create mode 100644 src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_org
create mode 100644 src/lib/dns/benchmarks/rdatarender_bench.cc
delete mode 100644 src/lib/dns/buffer.h
delete mode 100644 src/lib/dns/dnssectime.cc
delete mode 100644 src/lib/dns/dnssectime.h
create mode 100644 src/lib/dns/python/messagerenderer_python.h
create mode 100644 src/lib/dns/python/name_python.h
create mode 100644 src/lib/dns/python/pydnspp_towire.h
create mode 100644 src/lib/dns/python/rcode_python.h
create mode 100644 src/lib/dns/python/tests/tsig_python_test.py
create mode 100644 src/lib/dns/python/tests/tsig_rdata_python_test.py
create mode 100644 src/lib/dns/python/tests/tsigerror_python_test.py
create mode 100644 src/lib/dns/python/tests/tsigrecord_python_test.py
create mode 100644 src/lib/dns/python/tsig_python.cc
create mode 100644 src/lib/dns/python/tsig_python.h
create mode 100644 src/lib/dns/python/tsig_rdata_python.cc
create mode 100644 src/lib/dns/python/tsig_rdata_python.h
create mode 100644 src/lib/dns/python/tsigerror_python.cc
create mode 100644 src/lib/dns/python/tsigerror_python.h
create mode 100644 src/lib/dns/python/tsigerror_python_inc.cc
create mode 100644 src/lib/dns/python/tsigkey_python.h
create mode 100644 src/lib/dns/python/tsigrecord_python.cc
create mode 100644 src/lib/dns/python/tsigrecord_python.h
create mode 100644 src/lib/dns/rdata/generic/rp_17.cc
create mode 100644 src/lib/dns/rdata/generic/rp_17.h
create mode 100644 src/lib/dns/rdatafields.cc
create mode 100644 src/lib/dns/rdatafields.h
delete mode 100644 src/lib/dns/tests/base32hex_unittest.cc
delete mode 100644 src/lib/dns/tests/base64_unittest.cc
delete mode 100644 src/lib/dns/tests/buffer_unittest.cc
delete mode 100644 src/lib/dns/tests/dnssectime_unittest.cc
delete mode 100644 src/lib/dns/tests/hex_unittest.cc
create mode 100644 src/lib/dns/tests/rdata_rp_unittest.cc
create mode 100644 src/lib/dns/tests/rdatafields_unittest.cc
delete mode 100644 src/lib/dns/tests/sha1_unittest.cc
create mode 100644 src/lib/dns/tests/testdata/message_fromWire12.spec
create mode 100644 src/lib/dns/tests/testdata/message_fromWire13.spec
create mode 100644 src/lib/dns/tests/testdata/message_fromWire14.spec
create mode 100644 src/lib/dns/tests/testdata/message_fromWire15.spec
create mode 100644 src/lib/dns/tests/testdata/message_fromWire16.spec
create mode 100644 src/lib/dns/tests/testdata/message_toText1.spec
create mode 100644 src/lib/dns/tests/testdata/message_toText1.txt
create mode 100644 src/lib/dns/tests/testdata/message_toText2.spec
create mode 100644 src/lib/dns/tests/testdata/message_toText2.txt
create mode 100644 src/lib/dns/tests/testdata/message_toText3.spec
create mode 100644 src/lib/dns/tests/testdata/message_toText3.txt
create mode 100644 src/lib/dns/tests/testdata/message_toWire2.spec
create mode 100644 src/lib/dns/tests/testdata/message_toWire3.spec
create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire1.spec
create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire2.spec
create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire3.spec
create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire4.spec
create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire5.spec
create mode 100644 src/lib/dns/tests/testdata/rdata_rp_fromWire6.spec
create mode 100644 src/lib/dns/tests/testdata/rdata_rp_toWire1.spec
create mode 100644 src/lib/dns/tests/testdata/rdata_rp_toWire2.spec
create mode 100644 src/lib/dns/tests/testdata/rdatafields1.spec
create mode 100644 src/lib/dns/tests/testdata/rdatafields2.spec
create mode 100644 src/lib/dns/tests/testdata/rdatafields3.spec
create mode 100644 src/lib/dns/tests/testdata/rdatafields4.spec
create mode 100644 src/lib/dns/tests/testdata/rdatafields5.spec
create mode 100644 src/lib/dns/tests/testdata/rdatafields6.spec
create mode 100644 src/lib/dns/tests/testdata/tsig_verify1.spec
create mode 100644 src/lib/dns/tests/testdata/tsig_verify10.spec
create mode 100644 src/lib/dns/tests/testdata/tsig_verify2.spec
create mode 100644 src/lib/dns/tests/testdata/tsig_verify3.spec
create mode 100644 src/lib/dns/tests/testdata/tsig_verify4.spec
create mode 100644 src/lib/dns/tests/testdata/tsig_verify5.spec
create mode 100644 src/lib/dns/tests/testdata/tsig_verify6.spec
create mode 100644 src/lib/dns/tests/testdata/tsig_verify7.spec
create mode 100644 src/lib/dns/tests/testdata/tsig_verify8.spec
create mode 100644 src/lib/dns/tests/testdata/tsig_verify9.spec
create mode 100644 src/lib/dns/tests/testdata/tsigrecord_toWire1.spec
create mode 100644 src/lib/dns/tests/testdata/tsigrecord_toWire2.spec
create mode 100644 src/lib/dns/tests/tsig_unittest.cc
create mode 100644 src/lib/dns/tests/tsigerror_unittest.cc
create mode 100644 src/lib/dns/tests/tsigrecord_unittest.cc
create mode 100644 src/lib/dns/tsig.cc
create mode 100644 src/lib/dns/tsig.h
create mode 100644 src/lib/dns/tsigerror.cc
create mode 100644 src/lib/dns/tsigerror.h
create mode 100644 src/lib/dns/tsigrecord.cc
create mode 100644 src/lib/dns/tsigrecord.h
delete mode 100644 src/lib/dns/util/README
delete mode 100644 src/lib/dns/util/base32hex.h
delete mode 100644 src/lib/dns/util/base64.h
delete mode 100644 src/lib/dns/util/base_n.cc
delete mode 100644 src/lib/dns/util/hex.h
delete mode 100644 src/lib/dns/util/sha1.cc
delete mode 100644 src/lib/dns/util/sha1.h
delete mode 100644 src/lib/log/debug_levels.h
delete mode 100644 src/lib/log/filename.cc
delete mode 100644 src/lib/log/filename.h
create mode 100644 src/lib/log/log_formatter.cc
create mode 100644 src/lib/log/log_formatter.h
create mode 100644 src/lib/log/log_messages.cc
create mode 100644 src/lib/log/log_messages.h
create mode 100644 src/lib/log/log_messages.mes
delete mode 100644 src/lib/log/logger_impl_log4cxx.cc
delete mode 100644 src/lib/log/logger_impl_log4cxx.h
create mode 100644 src/lib/log/logger_level.cc
create mode 100644 src/lib/log/logger_level.h
create mode 100644 src/lib/log/logger_level_impl.cc
create mode 100644 src/lib/log/logger_level_impl.h
delete mode 100644 src/lib/log/logger_levels.h
create mode 100644 src/lib/log/logger_manager.cc
create mode 100644 src/lib/log/logger_manager.h
create mode 100644 src/lib/log/logger_manager_impl.cc
create mode 100644 src/lib/log/logger_manager_impl.h
create mode 100644 src/lib/log/logger_name.cc
create mode 100644 src/lib/log/logger_name.h
create mode 100644 src/lib/log/logger_specification.h
create mode 100644 src/lib/log/logimpl_messages.cc
create mode 100644 src/lib/log/logimpl_messages.h
create mode 100644 src/lib/log/logimpl_messages.mes
create mode 100644 src/lib/log/macros.h
delete mode 100644 src/lib/log/message_exception.cc
delete mode 100644 src/lib/log/messagedef.cc
delete mode 100644 src/lib/log/messagedef.h
delete mode 100644 src/lib/log/messagedef.mes
create mode 100644 src/lib/log/output_option.cc
create mode 100644 src/lib/log/output_option.h
delete mode 100644 src/lib/log/root_logger_name.cc
delete mode 100644 src/lib/log/root_logger_name.h
delete mode 100644 src/lib/log/strutil.cc
delete mode 100644 src/lib/log/strutil.h
create mode 100755 src/lib/log/tests/console_test.sh.in
create mode 100755 src/lib/log/tests/destination_test.sh.in
delete mode 100644 src/lib/log/tests/filename_unittest.cc
create mode 100644 src/lib/log/tests/init_logger_test.cc
create mode 100755 src/lib/log/tests/init_logger_test.sh.in
create mode 100755 src/lib/log/tests/local_file_test.sh.in
create mode 100644 src/lib/log/tests/log_formatter_unittest.cc
create mode 100644 src/lib/log/tests/logger_example.cc
delete mode 100644 src/lib/log/tests/logger_impl_log4cxx_unittest.cc
create mode 100644 src/lib/log/tests/logger_level_impl_unittest.cc
create mode 100644 src/lib/log/tests/logger_level_unittest.cc
create mode 100644 src/lib/log/tests/logger_manager_unittest.cc
create mode 100644 src/lib/log/tests/logger_name_unittest.cc
create mode 100644 src/lib/log/tests/logger_specification_unittest.cc
delete mode 100644 src/lib/log/tests/logger_support_test.cc
create mode 100644 src/lib/log/tests/logger_support_unittest.cc
create mode 100644 src/lib/log/tests/output_option_unittest.cc
delete mode 100644 src/lib/log/tests/root_logger_name_unittest.cc
delete mode 100755 src/lib/log/tests/run_time_init_test.sh.in
create mode 100755 src/lib/log/tests/severity_test.sh.in
delete mode 100644 src/lib/log/tests/strutil_unittest.cc
create mode 100644 src/lib/log/tests/tempdir.h.in
delete mode 100644 src/lib/log/tests/xdebuglevel_unittest.cc
delete mode 100644 src/lib/log/xdebuglevel.cc
delete mode 100644 src/lib/log/xdebuglevel.h
create mode 100644 src/lib/nsas/glue_hints.cc
create mode 100644 src/lib/nsas/glue_hints.h
delete mode 100644 src/lib/nsas/locks.h
delete mode 100644 src/lib/nsas/lru_list.h
create mode 100644 src/lib/nsas/nsas_log.cc
create mode 100644 src/lib/nsas/nsas_log.h
create mode 100644 src/lib/nsas/nsas_messages.mes
delete mode 100644 src/lib/nsas/random_number_generator.h
delete mode 100644 src/lib/nsas/tests/lru_list_unittest.cc
delete mode 100644 src/lib/nsas/tests/random_number_generator_unittest.cc
create mode 100644 src/lib/python/isc/config/cfgmgr_messages.mes
create mode 100644 src/lib/python/isc/log/log.cc
delete mode 100644 src/lib/python/isc/log/log.py
create mode 100755 src/lib/python/isc/log/tests/check_output.sh
create mode 100644 src/lib/python/isc/log/tests/console.out
create mode 100755 src/lib/python/isc/log/tests/log_console.py.in
delete mode 100644 src/lib/python/isc/log/tests/log_test.in
create mode 100644 src/lib/python/isc/testutils/Makefile.am
create mode 100644 src/lib/python/isc/testutils/README
create mode 100644 src/lib/python/isc/testutils/__init__.py
create mode 100644 src/lib/python/isc/testutils/parse_args.py
create mode 100644 src/lib/python/isc/testutils/tsigctx_mock.py
create mode 100644 src/lib/python/isc/util/file.py
create mode 100644 src/lib/python/isc/util/tests/file_test.py
delete mode 100644 src/lib/rbmsgq/lib/cc.rb
delete mode 100644 src/lib/rbmsgq/lib/cc/message.rb
delete mode 100644 src/lib/rbmsgq/lib/cc/session.rb
create mode 100644 src/lib/resolve/resolve_log.cc
create mode 100644 src/lib/resolve/resolve_log.h
create mode 100644 src/lib/resolve/resolve_messages.mes
create mode 100644 src/lib/server_common/client.cc
create mode 100644 src/lib/server_common/client.h
create mode 100644 src/lib/server_common/keyring.cc
create mode 100644 src/lib/server_common/keyring.h
create mode 100644 src/lib/server_common/logger.cc
create mode 100644 src/lib/server_common/logger.h
create mode 100644 src/lib/server_common/server_common_messages.mes
create mode 100644 src/lib/server_common/tests/client_unittest.cc
create mode 100644 src/lib/server_common/tests/data_path.h.in
create mode 100644 src/lib/server_common/tests/keyring_test.cc
create mode 100644 src/lib/server_common/tests/testdata/spec.spec
create mode 100644 src/lib/util/Makefile.am
create mode 100644 src/lib/util/buffer.h
rename src/lib/{dns/util => util/encode}/base16_from_binary.h (100%)
create mode 100644 src/lib/util/encode/base32hex.h
rename src/lib/{dns/util => util/encode}/base32hex_from_binary.h (100%)
create mode 100644 src/lib/util/encode/base64.h
create mode 100644 src/lib/util/encode/base_n.cc
rename src/lib/{dns/util => util/encode}/binary_from_base16.h (100%)
rename src/lib/{dns/util => util/encode}/binary_from_base32hex.h (100%)
create mode 100644 src/lib/util/encode/hex.h
create mode 100644 src/lib/util/filename.cc
create mode 100644 src/lib/util/filename.h
create mode 100644 src/lib/util/hash/sha1.cc
create mode 100644 src/lib/util/hash/sha1.h
create mode 100644 src/lib/util/io/Makefile.am
create mode 100644 src/lib/util/io/fd.cc
create mode 100644 src/lib/util/io/fd.h
create mode 100644 src/lib/util/io/fd_share.cc
create mode 100644 src/lib/util/io/fd_share.h
create mode 100644 src/lib/util/io/fdshare_python.cc
create mode 100644 src/lib/util/io_utilities.h
create mode 100644 src/lib/util/locks.h
create mode 100644 src/lib/util/lru_list.h
create mode 100755 src/lib/util/python/mkpywrapper.py.in
create mode 100644 src/lib/util/python/pycppwrapper_util.h
create mode 100644 src/lib/util/python/wrapper_template.cc
create mode 100644 src/lib/util/python/wrapper_template.h
create mode 100644 src/lib/util/pyunittests/Makefile.am
create mode 100644 src/lib/util/pyunittests/pyunittests_util.cc
create mode 100644 src/lib/util/random/qid_gen.cc
create mode 100644 src/lib/util/random/qid_gen.h
create mode 100644 src/lib/util/random/random_number_generator.h
create mode 100644 src/lib/util/strutil.cc
create mode 100644 src/lib/util/strutil.h
create mode 100644 src/lib/util/tests/Makefile.am
create mode 100644 src/lib/util/tests/base32hex_unittest.cc
create mode 100644 src/lib/util/tests/base64_unittest.cc
create mode 100644 src/lib/util/tests/buffer_unittest.cc
create mode 100644 src/lib/util/tests/fd_share_tests.cc
create mode 100644 src/lib/util/tests/fd_tests.cc
create mode 100644 src/lib/util/tests/filename_unittest.cc
create mode 100644 src/lib/util/tests/hex_unittest.cc
create mode 100644 src/lib/util/tests/io_utilities_unittest.cc
create mode 100644 src/lib/util/tests/lru_list_unittest.cc
create mode 100644 src/lib/util/tests/qid_gen_unittest.cc
create mode 100644 src/lib/util/tests/random_number_generator_unittest.cc
create mode 100644 src/lib/util/tests/run_unittests.cc
create mode 100644 src/lib/util/tests/sha1_unittest.cc
create mode 100644 src/lib/util/tests/strutil_unittest.cc
create mode 100644 src/lib/util/tests/time_utilities_unittest.cc
create mode 100644 src/lib/util/time_utilities.cc
create mode 100644 src/lib/util/time_utilities.h
create mode 100644 src/lib/util/unittests/Makefile.am
create mode 100644 src/lib/util/unittests/README
create mode 100644 src/lib/util/unittests/fork.cc
create mode 100644 src/lib/util/unittests/fork.h
create mode 100644 src/lib/util/unittests/newhook.cc
create mode 100644 src/lib/util/unittests/newhook.h
create mode 100644 src/lib/util/unittests/resolver.h
create mode 100644 src/lib/util/unittests/run_all.cc
create mode 100644 src/lib/util/unittests/run_all.h
create mode 100644 src/lib/util/unittests/testdata.cc
create mode 100644 src/lib/util/unittests/testdata.h
create mode 100644 src/lib/util/unittests/textdata.h
delete mode 100644 src/lib/xfr/fd_share.cc
delete mode 100644 src/lib/xfr/fd_share.h
delete mode 100644 src/lib/xfr/fdshare_python.cc
delete mode 100644 src/lib/xfr/python_xfr.cc
create mode 100644 tests/tools/Makefile.am
create mode 100644 tests/tools/badpacket/Makefile.am
create mode 100644 tests/tools/badpacket/README
create mode 100644 tests/tools/badpacket/badpacket.cc
create mode 100644 tests/tools/badpacket/command_options.cc
create mode 100644 tests/tools/badpacket/command_options.h
create mode 100644 tests/tools/badpacket/header_flags.h
create mode 100644 tests/tools/badpacket/option_info.cc
create mode 100644 tests/tools/badpacket/option_info.h
create mode 100644 tests/tools/badpacket/scan.cc
create mode 100644 tests/tools/badpacket/scan.h
create mode 100644 tests/tools/badpacket/tests/Makefile.am
create mode 100644 tests/tools/badpacket/tests/command_options_unittest.cc
create mode 100644 tests/tools/badpacket/tests/header_flags_unittest.cc
create mode 100644 tests/tools/badpacket/tests/option_info_unittest.cc
create mode 100644 tests/tools/badpacket/tests/run_unittests.cc
create mode 100644 tests/tools/badpacket/version.h
create mode 100644 tools/query_cmp/README
create mode 100644 tools/query_cmp/queries/dquery01
create mode 100644 tools/query_cmp/queries/dquery01_no-type
create mode 100644 tools/query_cmp/queries/dquery01_non-terminal
create mode 100644 tools/query_cmp/queries/dquery01_nxdomain
create mode 100755 tools/query_cmp/src/lib/compare_rrset.py
create mode 100755 tools/query_cmp/src/lib/handledns.py
create mode 100755 tools/query_cmp/src/lib/read_query.py
create mode 100755 tools/query_cmp/src/query_two_server.py
create mode 100644 tools/query_cmp/zonefile/example.com.txt
create mode 100644 tools/query_cmp/zonefile/example.com.txt.signed
create mode 100644 tools/system_messages.py
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index cc87bfa..0aee22a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,13 +1,448 @@
- 200. [bug] Jelte
+268. [func] stephen
+ Add environment variable to allow redirection of logging output during
+ unit tests.
+ (Trac #1071, git 05164f9d61006869233b498d248486b4307ea8b6)
+
+267. [func] tomek
+ Added a dummy module for DHCP6. This module does not actually
+ do anything at this point, and BIND 10 has no option for
+ starting it yet. It is included as a base for further
+ development.
+ (Trac #990, git 4a590df96a1b1d373e87f1f56edaceccb95f267d)
+
+266. [func] Multiple developers
+ Convert various error messages, debugging and other output
+ to the new logging interface, including for b10-resolver,
+ the resolver library, the CC library, b10-auth, b10-cfgmgr,
+ b10-xfrin, and b10-xfrout. This includes a lot of new
+ documentation describing the new log messages.
+ (Trac #738, #739, #742, #746, #759, #761, #762)
+
+265. [func]* jinmei
+ b10-resolver: Introduced ACL on incoming queries. By default the
+ resolver accepts queries from ::1 and 127.0.0.1 and rejects all
+ others. The ACL can be configured with bindctl via the
+ "Resolver/query_acl" parameter. For example, to accept queries
+ from 192.0.2.0/24 (in addition to the default list), do this:
+ > config add Resolver/query_acl
+ > config set Resolver/query_acl[2]/action "ACCEPT"
+ > config set Resolver/query_acl[2]/from "192.0.2.0/24"
+ > config commit
+ (Trac #999, git e0744372924442ec75809d3964e917680c57a2ce,
+ also based on other ACL related work done by stephen and vorner)
+
+264. [bug] jerry
+ b10-xfrout: fixed a busy loop in its notify-out subthread. Due to
+ the loop, the thread previously woke up every 0.5 seconds throughout
+ most of the lifetime of b10-xfrout, wasting the corresponding CPU
+ time.
+ (Trac #1001, git fb993ba8c52dca4a3a261e319ed095e5af8db15a)
+
+263. [func] jelte
+ Logging configuration can now also accept a * as a first-level
+ name (e.g. '*', or '*.cache'), indicating that every module
+ should use that configuration, unless overridden by an explicit
+ logging configuration for that module
+ (Trac #1004, git 0fad7d4a8557741f953eda9fed1d351a3d9dc5ef)
+
+262. [func] stephen
+ Add some initial documentation about the logging framework.
+ Provide BIND 10 Messages Manual in HTML and DocBook? XML formats.
+ This provides all the log message descriptions in a single document.
+ A developer tool, tools/system_messages.py (available in git repo),
+ was written to generate this.
+ (Trac #1012, git 502100d7b9cd9d2300e78826a3bddd024ef38a74)
+
+261. [func] stephen
+ Add new-style logging messages to b10-auth.
+ (Trac #738, git c021505a1a0d6ecb15a8fd1592b94baff6d115f4)
+
+260. [func] stephen
+ Remove comma between message identification and the message
+ text in the new-style logging messages.
+ (Trac #1031, git 1c7930a7ba19706d388e4f8dcf2a55a886b74cd2)
+
+259. [bug] stephen
+ Logging now correctly initialized in b10-auth. Also, fixed
+ bug whereby querying for "version.bind txt ch" would cause
+ b10-auth to crash if BIND 10 was started with the "-v" switch.
+ (Trac #1022,#1023, git 926a65fa08617be677a93e9e388df0f229b01067)
+
+258. [build] jelte
+ Now builds and runs with Python 3.2
+ (Trac #710, git dae1d2e24f993e1eef9ab429326652f40a006dfb)
+
+257. [bug] y-aharen
+ Fixed a bug an instance of IntervalTimerImpl may be destructed
+ while deadline_timer is holding the handler. This fix addresses
+ occasional failure of IntervalTimerTest.destructIntervalTimer.
+ (Trac #957, git e59c215e14b5718f62699ec32514453b983ff603)
+
+256. [bug] jerry
+ src/bin/xfrin: update xfrin to check TSIG before other part of
+ incoming message.
+ (Trac #955, git 261450e93af0b0406178e9ef121f81e721e0855c)
+
+255. [func] zhang likun
+ src/lib/cache: remove empty code in lib/cache and the corresponding
+ suppression rule in src/cppcheck-suppress.lst.
+ (Trac #639, git 4f714bac4547d0a025afd314c309ca5cb603e212)
+
+254. [bug] jinmei
+ b10-xfrout: failed to send notifies over IPv6 correctly.
+ (Trac #964, git 3255c92714737bb461fb67012376788530f16e40)
+
+253. [func] jelte
+ Add configuration options for logging through the virtual module
+ Logging.
+ (Trac #736, git 9fa2a95177265905408c51d13c96e752b14a0824)
+
+252. [func] stephen
+ Add syslog as destination for logging.
+ (Trac #976, git 31a30f5485859fd3df2839fc309d836e3206546e)
+
+251. [bug]* jinmei
+ Make sure bindctl private files are non readable to anyone except
+ the owner or users in the same group. Note that if BIND 10 is run
+ with changing the user, this change means that the file owner or
+ group will have to be adjusted. Also note that this change is
+ only effective for a fresh install; if these files already exist,
+ their permissions must be adjusted by hand (if necessary).
+ (Trac #870, git 461fc3cb6ebabc9f3fa5213749956467a14ebfd4)
+
+250. [bug] ocean
+ src/lib/util/encode, in some conditions, the DecodeNormalizer's
+ iterator may reach the end() and when later being dereferenced
+ it will cause crash on some platform.
+ (Trac #838, git 83e33ec80c0c6485d8b116b13045b3488071770f)
+
+249. [func] jerry
+ xfrout: add support for TSIG verification.
+ (Trac #816, git 3b2040e2af2f8139c1c319a2cbc429035d93f217)
+
+248. [func] stephen
+ Add file and stderr as destinations for logging.
+ (Trac #555, git 38b3546867425bd64dbc5920111a843a3330646b)
+
+247. [func] jelte
+ Upstream queries from the resolver now set EDNS0 buffer size.
+ (Trac #834, git 48e10c2530fe52c9bde6197db07674a851aa0f5d)
+
+246. [func] stephen
+ Implement logging using log4cplus (http://log4cplus.sourceforge.net)
+ (Trac #899, git 31d3f525dc01638aecae460cb4bc2040c9e4df10)
+
+245. [func] vorner
+ Authoritative server can now sign the answers using TSIG
+ (configured in tsig_keys/keys, list of strings like
+ "name:<base64-secret>:sha1-hmac"). It doesn't use them for
+ ACL yet, only verifies them and signs if the request is signed.
+ (Trac #875, git fe5e7003544e4e8f18efa7b466a65f336d8c8e4d)
+
+244. [func] stephen
+ In unit tests, allow the choice of whether unhandled exceptions are
+ caught in the unit test program (and details printed) or allowed to
+ propagate to the default exception handler. See the bind10-dev thread
+ https://lists.isc.org/pipermail/bind10-dev/2011-January/001867.html
+ for more details.
+ (Trac #542, git 1aa773d84cd6431aa1483eb34a7f4204949a610f)
+
+243. [func]* feng
+ Add optional hmac algorithm SHA224/384/812.
+ (Trac #782, git 77d792c9d7c1a3f95d3e6a8b721ac79002cd7db1)
+
+bind10-devel-20110519 released on May 19, 2011
+
+242. [func] jinmei
+ xfrin: added support for TSIG verify. This change completes TSIG
+ support in b10-xfrin.
+ (Trac #914, git 78502c021478d97672232015b7df06a7d52e531b)
+
+241. [func] jinmei
+ pydnspp: added python extension for the TSIG API introduced in
+ change 235.
+ (Trac #905, git 081891b38f05f9a186814ab7d1cd5c572b8f777f)
+ (Trac #915, git 0555ab65d0e43d03b2d40c95d833dd050eea6c23)
+
+240. [func]* jelte
+ Updated configuration options to Xfrin, so that you can specify
+ a master address, port, and TSIG key per zone. Still only one per
+ zone at this point, and TSIG keys are (currently) only specified
+ by their full string representation. This replaces the
+ Xfrin/master_addr, Xfrin/master_port, and short-lived
+ Xfrin/tsig_key configurations with a Xfrin/zones list.
+ (Trac #811, git 88504d121c5e08fff947b92e698a54d24d14c375)
+
+239. [bug] jerry
+ src/bin/xfrout: If a zone doesn't have notify slaves (only has
+ one apex ns record - the primary master name server) will cause
+ b10-xfrout uses 100% of CPU.
+ (Trac #684, git d11b5e89203a5340d4e5ca51c4c02db17c33dc1f)
+
+238. [func] zhang likun
+ Implement the simplest forwarder, which pass everything through
+ except QID, port number. The response will not be cached.
+ (Trac #598_new, git 8e28187a582820857ef2dae9b13637a3881f13ba)
+
+237. [bug] naokikambe
+ Resolved that the stats module wasn't configurable in bindctl in
+ spite of its having configuration items. The configuration part
+ was removed from the original spec file "stats.spec" and was
+ placed in a new spec file "stats-schema.spec". Because it means
+ definitions of statistics items. The command part is still
+ there. Thus stats module currently has no its own configuration,
+ and the items in "stats-schema.spec" are neither visible nor
+ configurable through bindctl. "stats-schema.spec" is shared with
+ stats module and stats-httpd module, and maybe with other
+ statistical modules in future. "stats.spec" has own configuration
+ and commands of stats module, if it requires.
+ (Trac #719, git a234b20dc6617392deb8a1e00eb0eed0ff353c0a)
+
+236. [func] jelte
+ C++ client side of configuration now uses BIND10 logging system.
+ It also has improved error handling when communicating with the
+ rest of the system.
+ (Trac #743, git 86632c12308c3ed099d75eb828f740c526dd7ec0)
+
+235. [func] jinmei
+ libdns++: added support for TSIG signing and verification. It can
+ be done using a newly introduced TSIGContext class.
+ Note: we temporarily disabled support for truncated signature
+ and modified some part of the code introduced in #226 accordingly.
+ We plan to fix this pretty soon.
+ (Trac #812, git ebe0c4b1e66d359227bdd1bd47395fee7b957f14)
+ (Trac #871, git 7c54055c0e47c7a0e36fcfab4b47ff180c0ca8c8)
+ (Trac #813, git ffa2f0672084c1f16e5784cdcdd55822f119feaa)
+ (Trac #893, git 5aaa6c0f628ed7c2093ecdbac93a2c8cf6c94349)
+
+234. [func] jerry
+ src/bin/xfrin: update xfrin to use TSIG. Currently it only supports
+ sending a signed TSIG request or SOA request.
+ (Trac #815, git a892818fb13a1839c82104523cb6cb359c970e88)
+
+233. [func] stephen
+ Added new-style logging statements to the NSAS code.
+ (Trac #745, git ceef68cd1223ae14d8412adbe18af2812ade8c2d)
+
+232. [func] stephen
+ To facilitate the writing of extended descriptions in
+ message files, altered the message file format. The message
+ is now flagged with a "%" as the first non-blank character
+ in the line and the lines in the extended description are
+ no longer preceded by a "+".
+ (Trac #900, git b395258c708b49a5da8d0cffcb48d83294354ba3)
+
+231. [func]* vorner
+ The logging interface changed slightly. We use
+ logger.foo(MESSAGE_ID).arg(bar); instead of logger.foo(MESSAGE_ID,
+ bar); internally. The message definitions use '%1,%2,...'
+ instead of '%s,%d', which allows us to cope better with
+ mismatched placeholders and allows reordering of them in
+ case of translation.
+ (Trac #901, git 4903410e45670b30d7283f5d69dc28c2069237d6)
+
+230. [bug] naokikambe
+ Removed too repeated verbose messages in two cases of:
+ - when auth sends statistics data to stats
+ - when stats receives statistics data from other modules
+ (Trac #620, git 0ecb807011196eac01f281d40bc7c9d44565b364)
+
+229. [doc] jreed
+ Add manual page for b10-host.
+ (git a437d4e26b81bb07181ff35a625c540703eee845)
+
+228. [func]* jreed
+ The host tool is renamed to b10-host. While the utility is
+ a work in progress, it is expected to now be shipped with
+ tarballs. Its initial goal was to be a host(1) clone,
+ rewritten in C++ from scratch and using BIND 10's libdns++.
+ It now supports the -a (any), -c class, -d (verbose) switches
+ and has improved output.
+ (Trac #872, git d846851699d5c76937533adf9ff9d948dfd593ca)
+
+227. [build] jreed
+ Add missing libdns++ rdata files for the distribution (this
+ fixes distcheck error). Change three generated libdns++
+ headers to "nodist" so they aren't included in the distribution
+ (they were mistakenly included in last tarball).
+
+226. [func]* jelte
+ Introduced an API for cryptographic operations. Currently it only
+ supports HMAC, intended for use with TSIG. The current
+ implementation uses Botan as the backend library.
+ This introduces a new dependency, on Botan. Currently only Botan
+ 1.8.x works; older or newer versions don't.
+ (Trac #781, git 9df42279a47eb617f586144dce8cce680598558a)
+
+225. [func] naokikambe
+ Added the HTTP/XML interface (b10-stats-httpd) to the
+ statistics feature in BIND 10. b10-stats-httpd is a standalone
+ HTTP server and it requests statistics data to the stats
+ daemon (b10-stats) and sends it to HTTP clients in XML
+ format. Items of the data collected via b10-stats-httpd
+ are almost equivalent to ones which are collected via
+ bindctl. Since it also can send XSL (Extensible Stylesheet
+ Language) document and XSD (XML Schema definition) document,
+ XML document is human-friendly to view through web browsers
+ and its data types are strictly defined.
+ (Trac #547, git 1cbd51919237a6e65983be46e4f5a63d1877b1d3)
+
+224. [bug] jinmei
+ b10-auth, src/lib/datasrc: inconsistency between the hot spot
+ cache and actual data source could cause a crash while query
+ processing. The crash could happen, e.g., when an sqlite3 DB file
+ is being updated after a zone transfer while b10-auth handles a
+ query using the corresponding sqlite3 data source.
+ (Trac #851, git 2463b96680bb3e9a76e50c38a4d7f1d38d810643)
+
+223. [bug] feng
+ If ip address or port isn't usable for name server, name
+ server process won't exist and give end user chance to
+ reconfigure them.
+ (Trac #775, git 572ac2cf62e18f7eb69d670b890e2a3443bfd6e7)
+
+222. [bug]* jerry
+ src/lib/zonemgr: Fix a bug that xfrin not checking for new
+ copy of zone on startup. Imposes some random jitters to
+ avoid many zones need to do refresh at the same time. This
+ removed the Zonemgr/jitter_scope setting and introduced
+ Zonemgr/refresh_jitter and Zonemgr/reload_jitter.
+ (Trac #387, git 1241ddcffa16285d0a7bb01d6a8526e19fbb70cb)
+
+221. [func]* jerry
+ src/lib/util: Create C++ utility library.
+ (Trac #749, git 084d1285d038d31067f8cdbb058d626acf03566d)
+
+220. [func] stephen
+ Added the 'badpacket' program for testing; it sends a set of
+ (potentially) bad packets to a nameserver and prints the responses.
+ (Trac #703, git 1b666838b6c0fe265522b30971e878d9f0d21fde)
+
+219. [func] ocean
+ src/lib: move some dns related code out of asiolink library to
+ asiodns library
+ (Trac #751, git 262ac6c6fc61224d54705ed4c700dadb606fcb1c)
+
+218. [func] jinmei
+ src/lib/dns: added support for RP RDATA.
+ (Trac #806, git 4e47d5f6b692c63c907af6681a75024450884a88)
+
+217. [bug] jerry
+ src/lib/dns/python: Use a signed version of larger size of
+ integer and perform more strict range checks with
+ PyArg_ParseTuple() in case of overflows.
+ (Trac #363, git ce281e646be9f0f273229d94ccd75bf7e08d17cf)
+
+216. [func] vorner
+ The BIND10_XFROUT_SOCKET_FILE environment variable can be
+ used to specify which socket should be used for communication
+ between b10-auth and b10-xfrout. Mostly for testing reasons.
+ (Trac #615, git 28b01ad5bf72472c824a7b8fc4a8dc394e22e462)
+
+215. [func] vorner
+ A new process, b10-sockcreator, is added, which will create
+ sockets for the rest of the system. It is the only part
+ which will need to keep the root privileges. However, only
+ the process exists, nothing can talk to it yet.
+ (Trac #366, git b509cbb77d31e388df68dfe52709d6edef93df3f)
+
+214. [func]* vorner
+ Zone manager no longer thinks it is secondary master for
+ all zones in the database. They are listed in
+ Zonemgr/secondary_zones configuration variable (in the form
+ [{"name": "example.com", "class": "IN"}]).
+ (Trac #670, git 7c1e4d5e1e28e556b1d10a8df8d9486971a3f052)
+
+213. [bug] naokikambe
+ Solved incorrect datetime of "bind10.boot_time" and also
+ added a new command "sendstats" for Bob. This command is
+ to send statistics data to the stats daemon immediately.
+ The solved problem is that statistics data doesn't surely
+ reach to the daemon because Bob sent statistics data to
+ the daemon while it is starting. So the daemon invokes the
+ command for Bob after it starts up. This command is also
+ useful for resending statistics data via bindctl manually.
+ (Trac #521, git 1c269cbdc76f5dc2baeb43387c4d7ccc6dc863d2)
+
+212. [bug] naokikambe
+ Fixed that the ModuleCCSession object may group_unsubscribe in the
+ closed CC session in being deleted.
+ (Trac #698, git 0355bddc92f6df66ef50b920edd6ec3b27920d61)
+
+211. [func] shane
+ Implement "--brittle" option, which causes the server to exit
+ if any of BIND 10's processes dies.
+ (Trac #788, git 88c0d241fe05e5ea91b10f046f307177cc2f5bc5)
+
+210. [bug] jerry
+ src/bin/auth: fixed a bug where type ANY queries don't provide
+ additional glue records for ANSWER section.
+ (Trac #699, git 510924ebc57def8085cc0e5413deda990b2abeee)
+
+bind10-devel-20110322 released on March 22, 2011
+
+209. [func] jelte
+ Resolver now uses the NSAS when looking for a nameserver to
+ query for any specific zone. This also includes keeping track of
+ the RTT for that nameserver.
+ (Trac #495, git 76022a7e9f3ff339f0f9f10049aa85e5784d72c5)
+
+208. [bug]* jelte
+ Resolver now answers REFUSED on queries that are not for class IN.
+ This includes the various CH TXT queries, which will be added
+ later.
+ (git 012f9e78dc611c72ea213f9bd6743172e1a2ca20)
+
+207. [func] jelte
+ Resolver now starts listening on localhost:53 if no configuration
+ is set.
+ (Trac #471, git 1960b5becbba05570b9c7adf5129e64338659f07)
+
+206. [func] shane
+ Add the ability to list the running BIND 10 processes using the
+ command channel. To try this, use "Boss show_processes".
+ (Trac #648, git 451bbb67c2b5d544db2f7deca4315165245d2b3b)
+
+205. [bug] jinmei
+ b10-auth, src/lib/datasrc: fixed a bug where b10-auth could return
+ an empty additional section for delegation even if some glue is
+ crucial when it fails to find some other glue records in its data
+ source.
+ (Trac #646, git 6070acd1c5b2f7a61574eda4035b93b40aab3e2b)
+
+204. [bug] jinmei
+ b10-auth, src/lib/datasrc: class ANY queries were not handled
+ correctly in the generic data source (mainly for sqlite3). It
+ could crash b10-auth in the worst case, and could result in
+ incorrect responses in some other cases.
+ (Trac #80, git c65637dd41c8d94399bd3e3cee965b694b633339)
+
+203. [bug] zhang likun
+ Fix resolver cache memory leak: when cache is destructed, rrset
+ and message entries in it are not destructed properly.
+ (Trac #643, git aba4c4067da0dc63c97c6356dc3137651755ffce)
+
+202. [func] vorner
+ It is possible to specify a different directory where we look for
+ configuration files (by -p) and different configuration file to
+ use (-c). Also, it is possible to specify the port on which
+ cmdctl should listen (--cmdctl-port).
+ (Trac #615, git 5514dd78f2d61a222f3069fc94723ca33fb3200b)
+
+201. [bug] jerry
+ src/bin/bindctl: bindctl doesn't show traceback on shutdown.
+ (Trac #588, git 662e99ef050d98e86614c4443326568a0b5be437)
+
+200. [bug] Jelte
Fixed a bug where incoming TCP connections were not closed.
(Trac #589, git 1d88daaa24e8b1ab27f28be876f40a144241e93b)
- 199. [func] ocean
+199. [func] ocean
Cache negative responses (NXDOMAIN/NODATA) from authoritative
server for recursive resolver.
(Trac #493, git f8fb852bc6aef292555063590c361f01cf29e5ca)
- 198. [bug] jinmei
+198. [bug] jinmei
b10-auth, src/lib/datasrc: fixed a bug where hot spot cache failed
to reuse cached SOA for negative responses. Due to this bug
b10-auth returned SERVFAIL when it was expected to return a
@@ -15,42 +450,42 @@
the zone.
(Trac #626, git 721a53160c15e8218f6798309befe940b9597ba0)
- 197. [bug] zhang likun
+197. [bug] zhang likun
Remove expired message and rrset entries when looking up them
in cache, touch or remove the rrset entry in cache properly
when doing lookup or update.
(Trac #661, git 9efbe64fe3ff22bb5fba46de409ae058f199c8a7)
- 196. [bug] jinmei
+196. [bug] jinmei
b10-auth, src/lib/datasrc: the backend of the in-memory data
source could not handle the root name. As a result b10-auth could
not work as a root server when using the in-memory data source.
(Trac #683, git 420ec42bd913fb83da37b26b75faae49c7957c46)
- 195. [func] stephen
+195. [func] stephen
Resolver will now re-try a query over TCP if a response to a UDP
query has the TC bit set.
(Trac #499, git 4c05048ba059b79efeab53498737abe94d37ee07)
- 194. [bug] vorner
+194. [bug] vorner
Solved a 100% CPU usage problem after switching addresses in b10-auth
(and possibly, but unconfirmed, in b10-resolver). It was caused by
repeated reads/accepts on closed socket (the bug was in the code for a
long time, recent changes made it show).
(Trac #657, git e0863720a874d75923ea66adcfbf5b2948efb10a)
- 193. [func]* jreed
+193. [func]* jreed
Listen on the IPv6 (::) and IPv4 (0.0.0.0) wildcard addresses
for b10-auth. This returns to previous behavior prior to
change #184. Document the listen_on configuration in manual.
(Trac #649, git 65a77d8fde64d464c75917a1ab9b6b3f02640ca6)
- 192. [func]* jreed
+192. [func]* jreed
Listen on standard domain port 53 for b10-auth and
b10-resolver.
(Trac #617, #618, git 137a6934a14cf0c5b5c065e910b8b364beb0973f)
- 191. [func] jinmei
+191. [func] jinmei
Imported system test framework of BIND 9. It can be run by
'make systest' at the top source directory. Notes: currently it
doesn't work when built in a separate tree. It also requires
@@ -60,51 +495,51 @@
to the specified file.
(Trac #606, git 6ac000df85625f5921e8895a1aafff5e4be3ba9c)
- 190. [func] jelte
+190. [func] jelte
Resolver now sets random qids on outgoing queries using
the boost::mt19937 prng.
(Trac #583, git 5222b51a047d8f2352bc9f92fd022baf1681ed81)
- 189. [bug] jreed
+189. [bug] jreed
Do not install the log message compiler.
(Trac #634, git eb6441aca464980d00e3ff827cbf4195c5a7afc5)
- 188. [bug] zhang likun
+188. [bug] zhang likun
Make the rrset trust level ranking algorithm used by
isc::cache::MessageEntry::getRRsetTrustLevel() follow RFC2181
section 5.4.1.
(Trac #595 git 19197b5bc9f2955bd6a8ca48a2d04472ed696e81)
- 187. [bug] zhang likun
+187. [bug] zhang likun
Fix the assert error in class isc::cache::RRsetCache by adding the
check for empty pointer and test case for it.
(Trac #638, git 54e61304131965c4a1d88c9151f8697dcbb3ce12)
- 186. [bug] jelte
+186. [bug] jelte
b10-resolver could stop with an assertion failure on certain kinds
of messages (there was a problem in error message creation). This
fixes that.
(Trac #607, git 25a5f4ec755bc09b54410fcdff22691283147f32)
- 185. [bug] vorner
+185. [bug] vorner
Tests use port from private range (53210), lowering chance of
a conflict with something else (eg. running bind 10).
(Trac #523, git 301da7d26d41e64d87c0cf72727f3347aa61fb40)
- 184. [func]* vorner
+184. [func]* vorner
Listening address and port configuration of b10-auth is the same as
for b10-resolver now. That means, it is configured through bindctl
at runtime, in the Auth/listen_on list, not through command line
arguments.
(Trac #575, #576, git f06ce638877acf6f8e1994962bf2dbfbab029edf)
- 183. [bug] jerry
+183. [bug] jerry
src/bin/xfrout: Enable parallel sessions between xfrout server and
muti-Auth. The session needs to be created only on the first time
or if an error occur.
(Trac #419, git 1d60afb59e9606f312caef352ecb2fe488c4e751)
- 182. [func] jinmei
+182. [func] jinmei
Support cppcheck for static code check on C++ code. If cppcheck
is available, 'make cppcheck' on the top source directory will run
the checker and should cleanly complete with an exit code of 0
@@ -114,46 +549,46 @@
the git repository.
(Trac #613, git b973f67520682b63ef38b1451d309be9f4f4b218)
- 181. [func] feng
+181. [func] feng
Add stop interface into dns server, so we can stop each running
server individually. With it, user can reconfigure her running server
with different ip address or port.
(Trac #388, git 6df94e2db856c1adc020f658cc77da5edc967555)
- 180. [build] jreed
+180. [build] jreed
Fix custom DESTDIR for make install. Patch from Jan Engelhardt.
(Trac #629, git 5ac67ede03892a5eacf42ce3ace1e4e376164c9f)
bind10-devel-20110224 released on February 24, 2011
- 179. [func] vorner
+179. [func] vorner
It is possible to start and stop resolver and authoritative
server without restart of the whole system. Change of the
configuration (Boss/start_auth and Boss/start_resolver) is
enough.
(Trac #565, git 0ac0b4602fa30852b0d86cc3c0b4730deb1a58fe)
- 178. [func] jelte
+178. [func] jelte
Resolver now makes (limited) use of the cache
(Trac #491, git 8b41f77f0099ddc7ca7d34d39ad8c39bb1a8363c)
- 177. [func] stephen
+177. [func] stephen
The upstream fetch code in asiolink is now protocol agnostic to
allow for the addition of fallback to TCP if a fetch response
indicates truncation.
(Trac #554, git 9739cbce2eaffc7e80640db58a8513295cf684de)
- 176. [func] zhang likun
+176. [func] likun
src/lib/cache: Rename one interface: from lookupClosestRRset()
to lookupDeepestNS(), and remove one parameter of it.
(Trac #492, git ecbfb7cf929d62a018dd4cdc7a841add3d5a35ae)
- 175. [bug] jerry
+175. [bug] jerry
src/bin/xfrout: Xfrout use the case-sensitive mode to compress
names in an AXFR massage.
(Trac #253, git 004e382616150f8a2362e94d3458b59bb2710182)
- 174. [bug]* jinmei
+174. [bug]* jinmei
src/lib/dns: revised dnssectime functions so that they don't rely
on the time_t type (whose size varies on different systems, which
can lead to subtle bugs like some form of "year 2038 problem").
@@ -162,74 +597,74 @@ bind10-devel-20110224 released on February 24, 2011
should be minimal because these functions are mostly private.
(Trac #61, git 09ece8cdd41c0f025e8b897b4883885d88d4ba5d)
- 173. [bug] jerry
+173. [bug] jerry
python/isc/notify: A notify_out test fails without network
connectivity, encapsulate the socket behavior using a mock
socket class to fix it.
(Trac #346, git 319debfb957641f311102739a15059f8453c54ce)
- 172. [func] jelte
+172. [func] jelte
Improved the bindctl cli in various ways, mainly concerning
list and map item addressing, the correct display of actual values,
and internal help.
(Trac #384, git e5fb3bc1ed5f3c0aec6eb40a16c63f3d0fc6a7b2)
- 171. [func] feng, jerry, jinmei, vorner
+171. [func] vorner
b10-auth, src/lib/datasrc: in memory data source now works as a
complete data source for authoritative DNS servers and b10-auth
uses it. It still misses major features, however, including
DNSSEC support and zone transfer.
- (Last trac #553, but many more,
+ (Last Trac #553, but many more,
git 6f031a09a248e7684723c000f3e8cc981dcdb349)
- 170. [bug] jinmei
+170. [bug] jinmei
Tightened validity checks in the NSEC3 constructors, both "from
"text" and "from wire". Specifically, wire data containing
invalid type bitmaps or invalid lengths of salt or hash is now
correctly rejected.
(Trac #117, git 9c690982f24fef19c747a72f43c4298333a58f48)
- 169. [func] zhang likun, jelte
+169. [func] jelte
Added a basic implementation for a resolver cache (though not
used yet).
(Trac #449, git 8aa3b2246ae095bbe7f855fd11656ae3bdb98986)
- 168. [bug] vorner
+168. [bug] vorner
Boss no longer has the -f argument, which was undocumented and
stayed as a relict of previous versions, currently causing only
strange behaviour.
(Trac #572, git 17f237478961005707d649a661cc72a4a0d612d4)
- 167. [bug] naokikambe
+167. [bug] naokikambe
Fixed failure of termination of msgq_test.py with python3
- coverage(3.3.1)
+ coverage (3.3.1).
(Trac #573, git 0e6a18e12f61cc482e07078776234f32605312e5)
- 166. [func] jelte
+166. [func] jelte
The resolver now sends back a SERVFAIL when there is a client
timeout (timeout_client config setting), but it will not stop
resolving (until there is a lookup timeout or a result).
(Trac #497 and #489, git af0e5cd93bebb27cb5c4457f7759d12c8bf953a6)
- 165. [func] jelte
+165. [func] jelte
The resolver now handles CNAMEs, it will follow them, and include
them in the answer. The maximum length of CNAME chains that is
supported is 16.
(Trac #497, git af0e5cd93bebb27cb5c4457f7759d12c8bf953a6)
- 164. [bug] y-aharen
+164. [bug] y-aharen
IntervalTimer: Modified the interface to accept interval in
milliseconds. It shortens the time of the tests of IntervalTimer.
(Trac #452, git c9f6acc81e24c4b8f0eb351123dc7b43f64e0914)
- 163. [func] vorner
+163. [func] vorner
The pimpl design pattern is used in UDPServer, with a shared
pointer. This makes it smaller to copy (which is done a lot as a
sideeffect of being coroutine) and speeds applications of this
class (notably b10-auth) up by around 10%.
(Trac #537, git 94cb95b1d508541201fc064302ba836164d3cbe6)
- 162. [func] stephen
+162. [func] stephen
Added C++ logging, allowing logging at different severities.
Code specifies the message to be logged via a symbol, and the
logging code picks up the message from an in-built dictionary.
@@ -238,28 +673,28 @@ bind10-devel-20110224 released on February 24, 2011
to create message header files and supply the default messages.
(Trac #438, git 7b1606cea7af15dc71f5ec1d70d958b00aa98af7)
- 161. [func] stephen
+161. [func] stephen
Added ResponseScrubber class to examine response from
a server and to remove out-of-bailiwick RRsets. Also
does cross-section checks to ensure consistency.
(Trac #496, git b9296ca023cc9e76cda48a7eeebb0119166592c5)
- 160. [func] jelte
+160. [func] jelte
Updated the resolver to take 3 different timeout values;
timeout_query for outstanding queries we sent while resolving
timeout_client for sending an answer back to the client
timeout_lookup for stopping the resolving
(currently 2 and 3 have the same final effect)
- (Trac 489, git 578ea7f4ba94dc0d8a3d39231dad2be118e125a2)
+ (Trac #489, git 578ea7f4ba94dc0d8a3d39231dad2be118e125a2)
- 159. [func] smann
+159. [func] smann
The resolver now has a configurable set of root servers to start
resolving at (called root_addresses). By default these are not
(yet) filled in. If empty, a hardcoded address for f-root will be
used right now.
(Trac #483, git a07e078b4feeb01949133fc88c9939254c38aa7c)
- 158. [func] jelte
+158. [func] jelte
The Resolver module will now do (very limited) resolving, if not
set to forwarding mode (i.e. if the configuration option
forward_addresses is left empty). It only supports referrals that
@@ -267,24 +702,24 @@ bind10-devel-20110224 released on February 24, 2011
of authoritative answers.
(Trac #484, git 7b84de4c0e11f4a070e038ca4f093486e55622af)
- 157. [bug] vorner
+157. [bug] vorner
One frozen process no longer freezes the whole b10-msgq. It caused the
whole system to stop working.
(Trac #420, git 93697f58e4d912fa87bc7f9a591c1febc9e0d139)
- 156. [func] stephen
+156. [func] stephen
Added ResponseClassifier class to examine response from
a server and classify it into one of several categories.
(Trac #487, git 18491370576e7438c7893f8551bbb8647001be9c)
bind10-devel-20110120 released on January 20, 2011
- 155. [doc] jreed
+155. [doc] jreed
Miscellaneous documentation improvements for man pages and
the guide, including auth, resolver, stats, xfrout, and
zonemgr. (git c14c4741b754a1eb226d3bdc3a7abbc4c5d727c0)
- 154. [bug] jinmei
+154. [bug] jinmei
b10-xfrin/b10-zonemgr: Fixed a bug where these programs didn't
receive command responses from CC sessions. Eventually the
receive buffer became full, and many other components that rely
@@ -293,12 +728,12 @@ bind10-devel-20110120 released on January 20, 2011
to revisit it for cleaner fix later.
(Trac #516, git 62c72fcdf4617e4841e901408f1e7961255b8194)
- 153. [bug] jelte
+153. [bug] jelte
b10-cfgmgr: Fixed a bug where configuration updates sometimes
lost previous settings in the configuration manager.
(Trac #427, git 2df894155657754151e0860e2ca9cdbed7317c70)
- 152. [func]* jinmei
+152. [func]* jinmei
b10-auth: Added new configuration variable "statistics-interval"
to allow the user to change the timer interval for periodic
statistics updates. The update can also be disabled by setting
@@ -307,46 +742,47 @@ bind10-devel-20110120 released on January 20, 2011
sending statistics and stop responding to queries as a result.
(Trac #513, git 285c5ee3d5582ed6df02d1aa00387f92a74e3695)
- 151. [bug] smann
+151. [bug] smann
lib/log/dummylog.h:
- lib/log/dummylog.cc: Modify dlog so that it takes an optional 2nd
- argument of type bool (true or false). This flag, if set, will cause
- the message to be printed whether or not -v is chosen.
- (trac #432, git 880220478c3e8702d56d761b1e0b21b77d08ee5a)
+ lib/log/dummylog.cc: Modify dlog so that it takes an optional
+ 2nd argument of type bool (true or false). This flag, if
+ set, will cause the message to be printed whether or not
+ -v is chosen.
+ (Trac #432, git 880220478c3e8702d56d761b1e0b21b77d08ee5a)
- 150. [bug] jelte
+150. [bug] jelte
b10-cfgmgr: No longer save the configuration on exit. Configuration
is already saved if it is changed successfully, so writing it on
exit (and hence, when nothing has changed too) is unnecessary and
may even cause problems.
(Trac #435, git fd7baa38c08d54d5b5f84930c1684c436d2776dc)
- 149. [bug] jelte
+149. [bug] jelte
bindctl: Check if the user session has disappeared (either by a
timeout or by a server restart), and reauthenticate if so. This
fixes the 'cmdctl not running' problem.
- (trac #431, git b929be82fec5f92e115d8985552f84b4fdd385b9)
+ (Trac #431, git b929be82fec5f92e115d8985552f84b4fdd385b9)
- 148. [func] jelte
+148. [func] jelte
bindctl: Command results are now pretty-printed (i.e. printed in
a more readable form). Empty results are no longer printed at all
(used to print '{}'), and the message
'send the command to cmd-ctrl' has also been removed.
(git 3954c628c13ec90722a2d8816f52a380e0065bae)
- 147. [bug] jinmei
+147. [bug] jinmei
python/isc/config: Fixed a bug that importing custom configuration
(in b10-config.db) of a remote module didn't work.
(Trac #478, git ea4a481003d80caf2bff8d0187790efd526d72ca)
- 146. [func] jelte
+146. [func] jelte
Command arguments were not validated internally against their
specifications. This change fixes that (on the C++ side, Python
side depends on an as yet planned addition). Note: this is only
an added internal check, the cli already checks format.
(Trac #473, git 5474eba181cb2fdd80e2b2200e072cd0a13a4e52)
- 145. [func]* jinmei
+145. [func]* jinmei
b10-auth: added a new command 'loadzone' for (re)loading a
specific zone. The command syntax is generic but it is currently
only feasible for class IN in memory data source. To reload a
@@ -355,7 +791,7 @@ bind10-devel-20110120 released on January 20, 2011
(Trac #467 git 4f7e1f46da1046de527ab129a88f6aad3dba7562
from 1d7d3918661ba1c6a8b1e40d8fcbc5640a84df12)
- 144. [build] jinmei
+144. [build] jinmei
Introduced a workaround for clang++ build on FreeBSD (and probably
some other OSes). If building BIND 10 fails with clang++ due to
a link error about "__dso_handle", try again from the configure
@@ -366,23 +802,23 @@ bind10-devel-20110120 released on January 20, 2011
variable by hand.
(Trac #474, git cfde436fbd7ddf3f49cbbd153999656e8ca2a298)
- 143. [build] jinmei
+143. [build] jinmei
Fixed build problems with clang++ in unit tests due to recent
changes. No behavior change. (Trac #448, svn r4133)
- 142. [func] jinmei
+142. [func] jinmei
b10-auth: updated query benchmark so that it can test in memory
data source. Also fixed a bug that the output buffer isn't
cleared after query processing, resulting in misleading results
or program crash. This is a regression due to change #135.
(Trac #465, svn r4103)
- 141. [bug] jinmei
+141. [bug] jinmei
b10-auth: Fixed a bug that the authoritative server includes
trailing garbage data in responses. This is a regression due to
change #135. (Trac #462, svn r4081)
- 140. [func] y-aharen
+140. [func] y-aharen
src/bin/auth: Added a feature to count queries and send counter
values to statistics periodically. To support it, added wrapping
class of asio::deadline_timer to use as interval timer.
@@ -393,14 +829,14 @@ bind10-devel-20110120 released on January 20, 2011
counters to b10-stats immediately.
(Trac #347, svn r4026)
- 139. [build] jreed
+139. [build] jreed
Introduced configure option and make targets for generating
Python code coverage report. This adds new make targets:
report-python-coverage and clean-python-coverage. The C++
code coverage targets were renamed to clean-cpp-coverage
and report-cpp-coverage. (Trac #362, svn r4023)
- 138. [func]* jinmei
+138. [func]* jinmei
b10-auth: added a configuration interface to support in memory
data sources. For example, the following command to bindctl
will configure a memory data source containing the "example.com"
@@ -417,70 +853,70 @@ bind10-devel-20110120 released on January 20, 2011
future versions.
(Trac #446, svn r3998)
- 137. [bug] jreed
+137. [bug] jreed
Fix run_*.sh scripts that are used for development testing
so they use a msgq socket file in the build tree.
(Trac #226, svn r3989)
- 136. [bug] jelte
+136. [bug] jelte
bindctl (and the configuration manager in general) now no longer
accepts 'unknown' data; i.e. data for modules that it does not know
about, or configuration items that are not specified in the .spec
files.
(Trac #202, svn r3967)
- 135. [func] each
+135. [func] each
Add b10-resolver. This is an example recursive server that
currently does forwarding only and no caching.
(Trac #327, svn r3903)
- 134. [func] vorner
+134. [func] vorner
b10-resolver supports timeouts and retries in forwarder mode.
(Trac #401, svn r3660)
- 133. [func] vorner
+133. [func] vorner
New temporary logging function available in isc::log. It is used by
b10-resolver.
(Trac #393, r3602)
- 132. [func] vorner
+132. [func] vorner
The b10-resolver is configured through config manager.
It has "listen_on" and "forward_addresses" options.
(Trac #389, r3448)
- 131. [func] feng, jerry
+131. [func] jerry
src/lib/datasrc: Introduced two template classes RBTree and RBNode
to provide the generic map with domain name as key and anything as
the value. Because of some unresolved design issue, the new classes
are only intended to be used by memory zone and zone table.
(Trac #397, svn r3890)
- 130. [func] jerry
+130. [func] jerry
src/lib/datasrc: Introduced a new class MemoryDataSrc to provide
the general interface for memory data source. For the initial
implementation, we don't make it a derived class of AbstractDataSrc
- because the interface is so different(we'll eventually consider this
- as part of the generalization work).
+ because the interface is so different (we'll eventually
+ consider this as part of the generalization work).
(Trac #422, svn r3866)
- 129. [func] jinmei
+129. [func] jinmei
src/lib/dns: Added new functions masterLoad() for loading master
zone files. The initial implementation can only parse a limited
form of master files, but BIND 9's named-compilezone can convert
any valid zone file into the acceptable form.
(Trac #423, svn r3857)
- 128. [build] vorner
+128. [build] vorner
Test for query name = '.', type = DS to authoritative nameserver
for root zone was added.
(Trac #85, svn r3836)
- 127. [bug] stephen
+127. [bug] stephen
During normal operation process termination and resurrection messages
are now output regardless of the state of the verbose flag.
(Trac #229, svn r3828)
- 126. [func] stephen, vorner, ocean
+126. [func] ocean
The Nameserver Address Store (NSAS) component has been added. It takes
care of choosing an IP address of a nameserver when a zone needs to be
contacted.
@@ -488,38 +924,38 @@ bind10-devel-20110120 released on January 20, 2011
bind10-devel-20101201 released on December 01, 2010
- 125. [func] jelte
+125. [func] jelte
Added support for addressing individual list items in bindctl
configuration commands; If you have an element that is a list, you
- can use foo[X] to address a specific item, where X is an integer
+ can use foo[X] integer
(starting at 0)
(Trac #405, svn r3739)
- 124. [bug] jreed
+124. [bug] jreed
Fix some wrong version reporting. Now also show the version
for the component and BIND 10 suite. (Trac #302, svn r3696)
- 123. [bug] jelte
+123. [bug] jelte
src/bin/bindctl printed values had the form of python literals
(e.g. 'True'), while the input requires valid JSON (e.g. 'true').
Output changed to JSON format for consistency. (svn r3694)
- 122. [func] stephen
+122. [func] stephen
src/bin/bind10: Added configuration options to Boss to determine
whether to start the authoritative server, recursive server (or
both). A dummy program has been provided for test purposes.
(Trac #412, svn r3676)
- 121. [func] jinmei
+121. [func] jinmei
src/lib/dns: Added support for TSIG RDATA. At this moment this is
not much of real use, however, because no protocol support was
added yet. It will soon be added. (Trac #372, svn r3649)
- 120. [func] jinmei
+120. [func] jinmei
src/lib/dns: introduced two new classes, TSIGKey and TSIGKeyRing,
to manage TSIG keys. (Trac #381, svn r3622)
- 119. [bug] jinmei
+119. [bug] jinmei
The master file parser of the python datasrc module incorrectly
regarded a domain name beginning with a decimal number as a TTL
specification. This confused b10-loadzone and had it reject to
@@ -529,158 +965,158 @@ bind10-devel-20101201 released on December 01, 2010
from a TTL specification. This is part of a more general issue
and will be addressed in Trac #413. (Trac #411, svn r3599)
- 118. [func] jinmei
+118. [func] jinmei
src/lib/dns: changed the interface of
AbstractRRset::getRdataIterator() so that the internal
cursor would point to the first RDATA automatically. This
will be a more intuitive and less error prone behavior.
This is a backward compatible change. (Trac #410, r3595)
- 117. [func] jinmei
+117. [func] jinmei
src/lib/datasrc: added new zone and zone table classes for the
support of in memory data source. This is an intermediate step to
the bigger feature, and is not yet actually usable in practice.
(Trac #399, svn r3590)
- 116. [bug] jerry
+116. [bug] jerry
src/bin/xfrout: Xfrout and Auth will communicate by long tcp
connection, Auth needs to make a new connection only on the first
time or if an error occurred.
(Trac #299, svn r3482)
- 115. [func]* jinmei
+115. [func]* jinmei
src/lib/dns: Changed DNS message flags and section names from
separate classes to simpler enums, considering the balance between
type safety and usability. API has been changed accordingly.
More documentation and tests were provided with these changes.
(Trac #358, r3439)
- 114. [build] jinmei
+114. [build] jinmei
Supported clang++. Note: Boost >= 1.44 is required.
(Trac #365, svn r3383)
- 113. [func]* zhanglikun
+113. [func]* zhanglikun
Folder name 'utils'(the folder in /src/lib/python/isc/) has been
renamed to 'util'. Programs that used 'import isc.utils.process'
now need to use 'import isc.util.process'. The folder
/src/lib/python/isc/Util is removed since it isn't used by any
program. (Trac #364, r3382)
- 112. [func] zhang likun
+112. [func] zhang likun
Add one mixin class to override the naive serve_forever() provided
in python library socketserver. Instead of polling for shutdown
every poll_interval seconds, one socketpair is used to wake up
the waiting server. (Trac #352, svn r3366)
- 111. [bug]* zhanglikun, Michal Vaner
+111. [bug]* Vaner
Make sure process xfrin/xfrout/zonemgr/cmdctl can be stopped
properly when user enter "ctrl+c" or 'Boss shutdown' command
through bindctl. The ZonemgrRefresh.run_timer and
NotifyOut.dispatcher spawn a thread themselves.
(Trac #335, svn r3273)
- 110. [func] Michal Vaner
+110. [func] Vaner
Added isc.net.check module to check ip addresses and ports for
correctness and isc.net.addr to hold IP address. The bind10, xfrin
and cmdctl programs are modified to use it.
(Trac #353, svn r3240)
- 109. [func] naokikambe
+109. [func] naokikambe
Added the initial version of the stats module for the statistics
feature of BIND 10, which supports the restricted features and
items and reports via bindctl command. (Trac #191, r3218)
Added the document of the stats module, which is about how stats
module collects the data (Trac #170, [wiki:StatsModule])
- 108. [func] jerry
+108. [func] jerry
src/bin/zonemgr: Provide customizable configurations for
lowerbound_refresh, lowerbound_retry, max_transfer_timeout and
jitter_scope. (Trac #340, r3205)
- 107. [func] zhang likun
+107. [func] likun
Remove the parameter 'db_file' for command 'retransfer' of
xfrin module. xfrin.spec will not be generated by script.
(Trac #329, r3171)
- 106. [bug] zhang likun
+106. [bug] likun
When xfrin can't connect with one zone's master, it should tell
the bad news to zonemgr, so that zonemgr can reset the timer for
that zone. (Trac #329, r3170)
- 105. [bug] Michal Vaner
+105. [bug] Vaner
Python processes: they no longer take 100% CPU while idle
due to a busy loop in reading command session in a nonblocking way.
(Trac #349, svn r3153), (Trac #382, svn r3294)
- 104. [bug] jerry
+104. [bug] jerry
bin/zonemgr: zonemgr should be attempting to refresh expired zones.
(Trac #336, r3139)
- 103. [bug] jerry
+103. [bug] jerry
lib/python/isc/log: Fixed an issue with python logging,
python log shouldn't die with OSError. (Trac #267, r3137)
- 102. [build] jinmei
+102. [build] jinmei
Disable threads in ASIO to minimize build time dependency.
(Trac #345, r3100)
- 101. [func] jinmei
+101. [func] jinmei
src/lib/dns: Completed Opcode and Rcode implementation with more
tests and documentation. API is mostly the same but the
validation was a bit tightened. (Trac #351, svn r3056)
- 100. [func] Michal Vaner
+100. [func] Vaner
Python processes: support naming of python processes so
they're not all called python3.
(Trac #322, svn r3052)
- 99. [func]* jinmei
+99. [func]* jinmei
Introduced a separate EDNS class to encapsulate EDNS related
information more cleanly. The related APIs are changed a bit,
although it won't affect most of higher level applications.
(Trac #311, svn r3020)
- 98. [build] jinmei
+98. [build] jinmei
The ./configure script now tries to search some common include
paths for boost header files to minimize the need for explicit
configuration with --with-boost-include. (Trac #323, svn r3006)
- 97. [func] jinmei
+97. [func] jinmei
Added a micro benchmark test for query processing of b10-auth.
(Trac #308, svn r2982)
- 96. [bug] jinmei
+96. [bug] jinmei
Fixed two small issues with configure: Do not set CXXFLAGS so that
it can be customized; Make sure --disable-static works.
(Trac #325, r2976)
bind10-devel-20100917 released on September 17, 2010
- 95. [doc] jreed
+95. [doc] jreed
Add b10-zonemgr manual page. Update other docs to introduce
this secondary manager. (Trac #341, svn r2951)
- 95. [bug] jreed
+95. [bug] jreed
bin/xfrout and bin/zonemgr: Fixed some stderr output.
(Trac #342, svn r2949)
- 94. [bug] jelte
+94. [bug] jelte
bin/xfrout: Fixed a problem in xfrout where only 2 or 3 RRs
were used per DNS message in the xfrout stream.
(Trac #334, r2931)
- 93. [bug] jinmei
+93. [bug] jinmei
lib/datasrc: A DS query could crash the library (and therefore,
e.g. the authoritative server) if some RR of the same apex name
is stored in the hot spot cache. (Trac #307, svn r2923)
- 92. [func]* jelte
+92. [func]* jelte
libdns_python (the python wrappers for libdns++) has been renamed
to pydnspp (Python DNS++). Programs and libraries that used
'import libdns_python' now need to use 'import pydnspp'.
(Trac #314, r2902)
- 91. [func]* jinmei
+91. [func]* jinmei
lib/cc: Use const pointers and const member functions for the API
as much as possible for safer operations. Basically this does not
change the observable behavior, but some of the API were changed
@@ -688,36 +1124,36 @@ bind10-devel-20100917 released on September 17, 2010
copies, but at this moment the overhead is deemed acceptable.
(Trac #310, r2803)
- 90. [build] jinmei
+90. [build] jinmei
(Darwin/Mac OS X specific) Specify DYLD_LIBRARY_PATH for tests and
experimental run under the source tree. Without this loadable
python modules refer to installation paths, which may confuse the
operation due to version mismatch or even trigger run time errors
due to missing libraries. (Trac #313, r2782)
- 89. [build] jinmei
+89. [build] jinmei
Generate b10-config.db for tests at build time so that the source
tree does not have to be writable. (Trac #315, r2776)
- 88. [func] jelte
+88. [func] jelte
Blocking reads on the msgq command channel now have a timeout
(defaults to 4 seconds, modifiable as needed by modules).
Because of this, modules will no longer block indefinitely
if they are waiting for a message that is not sent for whatever
reason. (Trac #296, r2761)
- 87. [func] zhanglikun
+87. [func] zhanglikun
lib/python/isc/notifyout: Add the feature of notify-out, when
zone axfr/ixfr finishing, the server will notify its slaves.
(Trac #289, svn r2737)
- 86. [func] jerry
+86. [func] jerry
bin/zonemgr: Added zone manager module. The zone manager is one
of the co-operating processes of BIND10, which keeps track of
timers and other information necessary for BIND10 to act as a
slave. (Trac #215, svn r2737)
- 85. [build]* jinmei
+85. [build]* jinmei
Build programs using dynamic link by default. A new configure
option --enable-static-link is provided to force static link for
executable programs. Statically linked programs can be run on a
@@ -726,34 +1162,34 @@ bind10-devel-20100917 released on September 17, 2010
bind10-devel-20100812 released on August 12, 2010
- 84. [bug] jinmei, jerry
+84. [bug] jinmei, jerry
This is a quick fix patch for the issue: AXFR fails half the
time because of connection problems. xfrout client will make
a new connection every time. (Trac #299, svn r2697)
- 83. [build]* jreed
+83. [build]* jreed
The configure --with-boost-lib option is removed. It was not
used since the build included ASIO. (svn r2684)
- 82. [func] jinmei
+82. [func] jinmei
bin/auth: Added -u option to change the effective process user
of the authoritative server after invocation. The same option to
the boss process will be propagated to b10-auth, too.
(Trac #268, svn r2675)
- 81. [func] jinmei
+81. [func] jinmei
Added a C++ framework for micro benchmark tests. A supplemental
library functions to build query data for the tests were also
provided. (Trac #241, svn r2664)
- 80. [bug] jelte
+80. [bug] jelte
bindctl no longer accepts configuration changes for unknown or
non-running modules (for the latter, this is until we have a
way to verify those options, at which point it'll be allowed
again).
(Trac #99, r2657)
- 79. [func] feng, jinmei
+79. [func] feng, jinmei
Refactored the ASIO link interfaces to move incoming XFR and
NOTIFY processing to the auth server class. Wrapper classes for
ASIO specific concepts were also provided, so that other BIND 10
@@ -765,7 +1201,7 @@ bind10-devel-20100812 released on August 12, 2010
Note: Right now, NOTIFY doesn't actually trigger subsequent zone
transfer due to security reasons. (Trac #221, r2565)
- 78. [bug] jinmei
+78. [bug] jinmei
lib/dns: Fixed miscellaneous bugs in the base32 (hex) and hex
(base16) implementation, including incorrect padding handling,
parser failure in decoding with a SunStudio build, missing
@@ -776,44 +1212,44 @@ bind10-devel-20100812 released on August 12, 2010
libdns++, so we don't consider it a backward incompatible change.
(Trac #256, r2549)
- 77. [func] zhanglikun
+77. [func] zhanglikun
Make error message be more friendly when running cmdctl and it's
- already running(listening on same port)(Trac #277, r2540)
+ already running (listening on same port)(Trac #277, r2540)
- 76. [bug] jelte
+76. [bug] jelte
Fixed a bug in the handling of 'remote' config modules (i.e.
modules that peek at the configuration of other modules), where
they answered 'unknown command' to commands for those other
modules. (Trac #278, r2506)
- 75. [bug] jinmei
+75. [bug] jinmei
Fixed a bug in the sqlite3 data source where temporary strings
could be referenced after destruction. It caused various lookup
failures with SunStudio build. (Trac #288, r2494)
- 74. [func]* jinmei
+74. [func]* jinmei
Refactored the cc::Session class by introducing an abstract base
class. Test code can use their own derived mock class so that
tests can be done without establishing a real CC session. This
change also modified some public APIs, mainly in the config
module. (Trac #275, r2459)
- 73. [bug] jelte
+73. [bug] jelte
Fixed a bug where in bindctl, locally changed settings were
reset when the list of running modules is updated. (Trac #285,
r2452)
- 72. [build] jinmei
+72. [build] jinmei
Added -R when linking python wrapper modules to libpython when
possible. This helps build BIND 10 on platforms that install
libpython whose path is unknown to run-time loader. NetBSD is a
known such platform. (Trac #148, r2427)
- 71. [func] each
+71. [func] each
Add "-a" (address) option to bind10 to specify an address for
the auth server to listen on.
- 70. [func] each
+70. [func] each
Added a hot-spot cache to libdatasrc to speed up access to
repeatedly-queried data and reduce the number of queries to
the underlying database; this should substantially improve
@@ -823,22 +1259,22 @@ bind10-devel-20100812 released on August 12, 2010
bind10-devel-20100701 released on July 1, 2010
- 69. [func]* jelte
+69. [func]* jelte
Added python wrappers for libdns++ (isc::dns), and libxfr. This
removes the dependency on Boost.Python. The wrappers don't
completely implement all functionality, but the high-level API
is wrapped, and current modules use it now.
(Trac #181, svn r2361)
- 68. [func] zhanglikun
- Add options -c(--certificate-chain) to bindctl. Override class
+68. [func] zhanglikun
+ Add options -c (--certificate-chain) to bindctl. Override class
HTTPSConnection to support server certificate validation.
Add support to cmdctl.spec file, now there are three configurable
items for cmdctl: 'key_file', 'cert_file' and 'accounts_file',
all of them can be changed in runtime.
(Trac #127, svn r2357)
- 67. [func] zhanglikun
+67. [func] zhanglikun
Make bindctl's command parser only do minimal check.
Parameter value can be a sequence of non-space characters,
or a string surrounded by quotation marks (these marks can
@@ -849,13 +1285,13 @@ bind10-devel-20100701 released on July 1, 2010
avoid using Exception to catch all exceptions.
(Trac #220, svn r2356)
- 66. [bug] each
+66. [bug] each
Check for duplicate RRsets before inserting data into a message
section; this, among other things, will prevent multiple copies
of the same CNAME from showing up when there's a loop. (Trac #69,
svn r2350)
- 65. [func] shentingting
+65. [func] shentingting
Various loadzone improvements: allow optional comment for
$TTL, allow optional origin and comment for $INCLUDE, allow
optional comment for $ORIGIN, support BIND9 extension of
@@ -866,71 +1302,71 @@ bind10-devel-20100701 released on July 1, 2010
formats to load.
(Trac #197, #199, #244, #161, #198, #174, #175, svn r2340)
- 64. [func] jerry
+64. [func] jerry
Added python logging framework. It is for testing and
experimenting with logging ideas. Currently, it supports
three channels (file, syslog and stderr) and five levels
(debug, info, warning, error and critical).
(Trac #176, svn r2338)
- 63. [func] shane
+63. [func] shane
Added initial support for setuid(), using the "-u" flag. This will
be replaced in the future, but for now provides a reasonable
starting point.
(Trac #180, svn r2330)
- 62. [func] jelte
+62. [func] jelte
bin/xfrin: Use the database_file as configured in Auth to transfers
bin/xfrout: Use the database_file as configured in Auth to transfers
- 61. [bug] jelte
+61. [bug] jelte
bin/auth: Enable b10-auth to be launched in source tree
(i.e. use a zone database file relative to that)
- 60. [build] jinmei
+60. [build] jinmei
Supported SunStudio C++ compiler. Note: gtest still doesn't work.
(Trac #251, svn r2310)
- 59. [bug] jinmei
+59. [bug] jinmei
lib/datasrc,bin/auth: The authoritative server could return a
SERVFAIL with a partial answer if it finds a data source broken
while looking for an answer. This can happen, for example, if a
zone that doesn't have an NS RR is configured and loaded as a
sqlite3 data source. (Trac #249, r2286)
- 58. [bug] jinmei
+58. [bug] jinmei
Worked around an interaction issue between ASIO and standard C++
library headers. Without this ASIO didn't work: sometimes the
application crashes, sometimes it blocked in the ASIO module.
(Trac #248, svn r2187, r2190)
- 57. [func] jinmei
+57. [func] jinmei
lib/datasrc: used a simpler version of Name::split (change 31) for
better readability. No behavior change. (Trac #200, svn r2159)
- 56. [func]* jinmei
+56. [func]* jinmei
lib/dns: renamed the library name to libdns++ to avoid confusion
with the same name of library of BIND 9.
(Trac #190, svn r2153)
- 55. [bug] shane
+55. [bug] shane
bin/xfrout: xfrout exception on Ctrl-C now no longer generates
exception for 'Interrupted system call'
- (Track #136, svn r2147)
+ (Trac #136, svn r2147)
- 54. [bug] zhanglikun
+54. [bug] zhanglikun
bin/xfrout: Enable b10-xfrout can be launched in source
code tree.
(Trac #224, svn r2103)
- 53. [bug] zhanglikun
+53. [bug] zhanglikun
bin/bindctl: Generate a unique session ID by using
socket.gethostname() instead of socket.gethostbyname(),
since the latter one could make bindctl stall if its own
host name can't be resolved.
(Trac #228, svn r2096)
- 52. [func] zhanglikun
+52. [func] zhanglikun
bin/xfrout: When xfrout is launched, check whether the
socket file is being used by one running xfrout process,
if it is, exit from python. If the file isn't a socket file
@@ -940,210 +1376,210 @@ bind10-devel-20100701 released on July 1, 2010
bind10-devel-20100602 released on June 2, 2010
- 51. [build] jelte
+51. [build] jelte
lib/python: Add bind10_config.py module for paths and
possibly other configure-time variables. Allow some components
to find spec files in build tree when ran from source.
(Trac #223)
- 50. [bug] zhanglikun
+50. [bug] zhanglikun
bin/xfrin: a regression in xfrin: it can't communicate with
a remote server. (Trac #218, svn r2038)
- 49. [func]* jelte
+49. [func]* jelte
Use unix domain sockets for msgq. For b10-msgq, the command
line options --msgq-port and -m were removed. For bind10,
the -msgq-port option was removed, and the -m command line
option was changed to be a filename (instead of port number).
(Trac #183, svn r2009)
- 48. [func] jelte
+48. [func] jelte
bin/auth: Use asio's io_service for the msgq handling.
(svn r2007)
- 47. [func] zhanglikun
+47. [func] zhanglikun
bin/cmdctl: Add value/type check for commands sent to
cmdctl. (Trac #201, svn r1959)
- 46. [func] zhanglikun
+46. [func] zhanglikun
lib/cc: Fix real type data encoding/decoding. (Trac #193,
svn r1959)
- 45. [func] zhanglikun
+45. [func] zhanglikun
bin/bind10: Pass verbose option to more modules. (Trac
#205, svn r1957)
- 44. [build] jreed
+44. [build] jreed
Install headers for libdns and libexception. (Trac #68,
svn r1941)
- 43. [func] jelte
+43. [func] jelte
lib/cc: Message queuing on cc channel. (Trac #58, svn r1870)
- 42. [func] jelte
+42. [func] jelte
lib/python/isc/config: Make temporary file with python
tempfile module instead of manual with fixed name. (Trac
#184, svn r1859)
- 41. [func] jelte
+41. [func] jelte
Module descriptions in spec files. (Trac #90, svn r1856)
- 40. [build] jreed
+40. [build] jreed
Report detected features and configure settings at end of
configure output. (svn r1836)
- 39. [func]* each
+39. [func]* each
Renamed libauth to libdatasrc.
- 38. [bug] zhanglikun
+38. [bug] zhanglikun
Send command 'shutdown' to Xfrin and Xfrout when boss receive SIGINT.
Remove unused socket file when Xfrout process exits. Make sure Xfrout
exit by itself when it receives SIGINT, instead of being killed by the
signal SIGTERM or SIGKILL sent from boss.
(Trac #135, #151, #134, svn r1797)
- 37. [build] jinmei
+37. [build] jinmei
Check for the availability of python-config. (Trac #159,
svn r1794)
- 36. [func] shane
+36. [func] shane
bin/bind10: Miscellaneous code cleanups and improvements.
(Trac #40, svn r2012)
- 35. [bug] jinmei
+35. [bug] jinmei
bin/bindctl: fixed a bug that it didn't accept IPv6 addresses as
command arguments. (Trac #219, svn r2022)
- 34. [bug] jinmei
+34. [bug] jinmei
bin/xfrin: fixed several small bugs with many additional unit
tests. Fixes include: IPv6 transport support, resource leak,
and non IN class support. (Trac #185, svn r2000)
- 33. [bug] each
+33. [bug] each
bin/auth: output now prepended with "[b10-auth]" (Trac
#109, svn r1985)
- 32. [func]* each
+32. [func]* each
bin/auth: removed custom query-processing code, changed
boost::asio code to use plain asio instead, and added asio
headers to the source tree. This allows building without
using an external boost library. (Trac #163, svn r1983)
- 31. [func] jinmei
+31. [func] jinmei
lib/dns: added a separate signature for Name::split() as a
convenient wrapper for common usage. (Trac #49, svn r1903)
- 30. [bug] jinmei
+30. [bug] jinmei
lib/dns: parameter validation of Name::split() was not sufficient,
and invalid parameters could cause integer overflow and make the
library crash. (Trac #177, svn r1806)
bind10-devel-20100421 released on April 21, 2010
- 29. [build]
+29. [build] jreed
Enable Python unit tests for "make check". (svn r1762)
- 28. [bug]
+28. [bug] jreed
Fix msgq CC test so it can find its module. (svn r1751)
- 27. [build]
+27. [build] jelte
Add missing copyright license statements to various source
files. (svn r1750)
- 26. [func]
+26. [func] jelte
Use PACKAGE_STRING (name + version) from config.h instead
of hard-coded value in CH TXT version.bind replies (Trac
#114, svn r1749)
- 25. [func]*
+25. [func]* jreed
Renamed msgq to b10-msgq. (Trac #25, svn r1747, r1748)
- 24. [func]
+24. [func] jinmei
Support case-sensitive name compression in MessageRenderer.
(Trac #142, svn r1704)
- 23. [func]
+23. [func] jinmei
Support a simple name with possible compression. (svn r1701)
- 22. [func]
+22. [func] zhanglikun
b10-xfrout for AXFR-out support added. (svn r1629, r1630)
- 21. [bug]
+21. [bug] zhanglikun
Make log message more readable when xfrin failed. (svn
r1697)
- 20. [bug]
+20. [bug] jinmei
Keep stderr for child processes if -v is specified. (svn
r1690, r1698)
- 19. [bug]
+19. [bug] jinmei
Allow bind10 boss to pass environment variables from parent.
(svn r1689)
- 18. [bug]
+18. [bug] jinmei
Xfrin warn if bind10_dns load failed. (svn r1688)
- 17. [bug]
+17. [bug] jinmei
Use sqlite3_ds.load() in xfrin module and catch Sqlite3DSError
explicitly. (svn r1684)
- 16. [func]*
+16. [func]* zhanglikun
Removed print_message and print_settings configuration
commands from Xfrin. (Trac #136, svn r1682)
- 15. [func]*
+15. [func]* jinmei
Changed zone loader/updater so trailing dot is not required.
(svn r1681)
- 14. [bug]
+14. [bug] shane
Change shutdown to actually SIGKILL properly. (svn r1675)
- 13. [bug]
+13. [bug] jinmei
Don't ignore other RRs than SOA even if the second SOA is
found. (svn r1674)
- 12. [build]
+12. [build] jreed
Fix tests and testdata so can be used from a read-only
source directory.
- 11. [build]
+11. [build] jreed
Make sure python tests scripts are included in tarball.
(svn r1648)
- 10. [build]
+10. [build] jinmei
Improve python detection for configure. (svn r1622)
- 9. [build]
+9. [build] jinmei
Automake the python binding of libdns. (svn r1617)
- 8. [bug]
+8. [bug] zhanglikun
Fix log errors which may cause xfrin module to crash. (svn
r1613)
- 7. [func]
+7. [func] zhanglikun
New API for inserting zone data to sqlite3 database for
AXFR-in. (svn r1612, r1613)
- 6. [bug]
+6. [bug] jreed
More code review, miscellaneous cleanups, style guidelines,
and new and improved unit tests added.
- 5. [doc]
+5. [doc] jreed
Manual page cleanups and improvements.
- 4. [bug]
+4. [bug] jinmei
NSEC RDATA fixes for buffer overrun lookups, incorrect
boundary checks, spec-non-conformant behaviors. (svn r1611)
- 3. [bug]
+3. [bug] jelte
Remove a re-raise of an exception that should only have
been included in an error answer on the cc channel. (svn
r1601)
- 2. [bug]
+2. [bug] mgraff
Removed unnecessary sleep() from ccsession.cc. (svn r1528)
- 1. [build]*
+1. [build]* jreed
The configure --with-boostlib option changed to --with-boost-lib.
bind10-devel-20100319 released on March 19, 2010
@@ -1162,10 +1598,10 @@ LEGEND
unless it's deemed to be impossible or very hard to keep
compatibility to fix the bug.
[build] compilation and installation infrastructure change.
-[doc] update to documentation. This shouldn't change run time behavior.
+[doc] update to documentation. This shouldn't change run time behavior.
[func] new feature. In some cases this may be a backward incompatible
change, which would require a bump of major version.
-[security] security hole fix. This is no different than a general bug fix
- except that it will be handled as confidential and will cause
+[security] security hole fix. This is no different than a general bug
+ fix except that it will be handled as confidential and will cause
security patch releases.
*: Backward incompatible or operational change.
diff --git a/INSTALL b/INSTALL
index 6ab63ea..44c380a 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1,5 +1,5 @@
-To build "configure" file:
- autoreconf
+If using git (not the tarball), build the "configure" file:
+ autoreconf --install
To then build from source:
./configure
diff --git a/Makefile.am b/Makefile.am
index e31a1a5..b07ef0f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -38,8 +38,11 @@ report-cpp-coverage:
c++/4.4\*/ext/\* \
c++/4.4\*/\*-\*/bits/\* \
boost/\* \
+ botan/\* \
ext/asio/\* \
+ ext/coroutine/\* \
gtest/\* \
+ log4cplus/\* \
usr/include/\* \
tests/\* \
unittests/\* \
@@ -84,214 +87,310 @@ systest:
#### include external sources in the distributed tarball:
EXTRA_DIST = ext/asio/README
-EXTRA_DIST += ext/asio/asio/local/stream_protocol.hpp
-EXTRA_DIST += ext/asio/asio/local/basic_endpoint.hpp
-EXTRA_DIST += ext/asio/asio/local/datagram_protocol.hpp
-EXTRA_DIST += ext/asio/asio/local/connect_pair.hpp
-EXTRA_DIST += ext/asio/asio/windows/basic_handle.hpp
-EXTRA_DIST += ext/asio/asio/windows/random_access_handle.hpp
-EXTRA_DIST += ext/asio/asio/windows/random_access_handle_service.hpp
-EXTRA_DIST += ext/asio/asio/windows/basic_random_access_handle.hpp
-EXTRA_DIST += ext/asio/asio/windows/overlapped_ptr.hpp
-EXTRA_DIST += ext/asio/asio/windows/stream_handle.hpp
-EXTRA_DIST += ext/asio/asio/windows/stream_handle_service.hpp
-EXTRA_DIST += ext/asio/asio/windows/basic_stream_handle.hpp
-EXTRA_DIST += ext/asio/asio/impl/write.ipp
-EXTRA_DIST += ext/asio/asio/impl/read.ipp
-EXTRA_DIST += ext/asio/asio/impl/serial_port_base.ipp
-EXTRA_DIST += ext/asio/asio/impl/write_at.ipp
-EXTRA_DIST += ext/asio/asio/impl/read_at.ipp
-EXTRA_DIST += ext/asio/asio/impl/error_code.ipp
-EXTRA_DIST += ext/asio/asio/impl/read_until.ipp
-EXTRA_DIST += ext/asio/asio/impl/io_service.ipp
-EXTRA_DIST += ext/asio/asio/detail/win_iocp_serial_port_service.hpp
-EXTRA_DIST += ext/asio/asio/detail/reactive_socket_service.hpp
-EXTRA_DIST += ext/asio/asio/detail/wince_thread.hpp
-EXTRA_DIST += ext/asio/asio/detail/win_event.hpp
-EXTRA_DIST += ext/asio/asio/detail/descriptor_ops.hpp
-EXTRA_DIST += ext/asio/asio/detail/pipe_select_interrupter.hpp
-EXTRA_DIST += ext/asio/asio/detail/task_io_service_fwd.hpp
-EXTRA_DIST += ext/asio/asio/detail/handler_alloc_helpers.hpp
-EXTRA_DIST += ext/asio/asio/detail/pop_options.hpp
-EXTRA_DIST += ext/asio/asio/detail/strand_service.hpp
-EXTRA_DIST += ext/asio/asio/detail/buffer_resize_guard.hpp
-EXTRA_DIST += ext/asio/asio/detail/win_iocp_io_service_fwd.hpp
-EXTRA_DIST += ext/asio/asio/detail/timer_scheduler.hpp
-EXTRA_DIST += ext/asio/asio/detail/kqueue_reactor_fwd.hpp
-EXTRA_DIST += ext/asio/asio/detail/hash_map.hpp
-EXTRA_DIST += ext/asio/asio/detail/event.hpp
-EXTRA_DIST += ext/asio/asio/detail/buffered_stream_storage.hpp
-EXTRA_DIST += ext/asio/asio/detail/posix_mutex.hpp
-EXTRA_DIST += ext/asio/asio/detail/timer_queue_fwd.hpp
-EXTRA_DIST += ext/asio/asio/detail/deadline_timer_service.hpp
-EXTRA_DIST += ext/asio/asio/detail/posix_fd_set_adapter.hpp
-EXTRA_DIST += ext/asio/asio/detail/service_registry.hpp
+EXTRA_DIST += ext/asio/README
+EXTRA_DIST += ext/asio/asio.hpp
+EXTRA_DIST += ext/asio/asio/basic_socket.hpp
+EXTRA_DIST += ext/asio/asio/streambuf.hpp
+EXTRA_DIST += ext/asio/asio/thread.hpp
+EXTRA_DIST += ext/asio/asio/detail/wait_handler.hpp
+EXTRA_DIST += ext/asio/asio/detail/resolve_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/gcc_hppa_fenced_block.hpp
EXTRA_DIST += ext/asio/asio/detail/null_fenced_block.hpp
+EXTRA_DIST += ext/asio/asio/detail/noncopyable.hpp
+EXTRA_DIST += ext/asio/asio/detail/eventfd_select_interrupter.hpp
EXTRA_DIST += ext/asio/asio/detail/task_io_service_operation.hpp
-EXTRA_DIST += ext/asio/asio/detail/epoll_reactor.hpp
+EXTRA_DIST += ext/asio/asio/detail/service_base.hpp
+EXTRA_DIST += ext/asio/asio/detail/task_io_service_fwd.hpp
+EXTRA_DIST += ext/asio/asio/detail/null_buffers_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_iocp_handle_write_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/thread.hpp
+EXTRA_DIST += ext/asio/asio/detail/select_reactor_fwd.hpp
+EXTRA_DIST += ext/asio/asio/detail/event.hpp
+EXTRA_DIST += ext/asio/asio/detail/reactive_descriptor_service.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_iocp_overlapped_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/reactive_socket_recv_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/macos_fenced_block.hpp
+EXTRA_DIST += ext/asio/asio/detail/dev_poll_reactor_fwd.hpp
+EXTRA_DIST += ext/asio/asio/detail/reactive_socket_service.hpp
+EXTRA_DIST += ext/asio/asio/detail/posix_tss_ptr.hpp
EXTRA_DIST += ext/asio/asio/detail/local_free_on_block_exit.hpp
-EXTRA_DIST += ext/asio/asio/detail/socket_select_interrupter.hpp
-EXTRA_DIST += ext/asio/asio/detail/null_mutex.hpp
-EXTRA_DIST += ext/asio/asio/detail/reactor_op.hpp
-EXTRA_DIST += ext/asio/asio/detail/fenced_block.hpp
-EXTRA_DIST += ext/asio/asio/detail/tss_ptr.hpp
-EXTRA_DIST += ext/asio/asio/detail/timer_queue_set.hpp
-EXTRA_DIST += ext/asio/asio/detail/winsock_init.hpp
-EXTRA_DIST += ext/asio/asio/detail/signal_init.hpp
-EXTRA_DIST += ext/asio/asio/detail/call_stack.hpp
-EXTRA_DIST += ext/asio/asio/detail/operation.hpp
-EXTRA_DIST += ext/asio/asio/detail/posix_signal_blocker.hpp
-EXTRA_DIST += ext/asio/asio/detail/win_iocp_handle_service.hpp
-EXTRA_DIST += ext/asio/asio/detail/fd_set_adapter.hpp
-EXTRA_DIST += ext/asio/asio/detail/io_control.hpp
-EXTRA_DIST += ext/asio/asio/detail/null_signal_blocker.hpp
-EXTRA_DIST += ext/asio/asio/detail/socket_ops.hpp
-EXTRA_DIST += ext/asio/asio/detail/bind_handler.hpp
-EXTRA_DIST += ext/asio/asio/detail/win_iocp_overlapped_ptr.hpp
-EXTRA_DIST += ext/asio/asio/detail/timer_scheduler_fwd.hpp
+EXTRA_DIST += ext/asio/asio/detail/timer_scheduler.hpp
EXTRA_DIST += ext/asio/asio/detail/signal_blocker.hpp
-EXTRA_DIST += ext/asio/asio/detail/consuming_buffers.hpp
-EXTRA_DIST += ext/asio/asio/detail/socket_option.hpp
-EXTRA_DIST += ext/asio/asio/detail/reactor.hpp
-EXTRA_DIST += ext/asio/asio/detail/win_fd_set_adapter.hpp
-EXTRA_DIST += ext/asio/asio/detail/select_interrupter.hpp
-EXTRA_DIST += ext/asio/asio/detail/null_buffers_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/resolver_service_base.hpp
EXTRA_DIST += ext/asio/asio/detail/socket_holder.hpp
-EXTRA_DIST += ext/asio/asio/detail/scoped_lock.hpp
-EXTRA_DIST += ext/asio/asio/detail/service_registry_fwd.hpp
-EXTRA_DIST += ext/asio/asio/detail/base_from_completion_cond.hpp
-EXTRA_DIST += ext/asio/asio/detail/posix_thread.hpp
-EXTRA_DIST += ext/asio/asio/detail/solaris_fenced_block.hpp
-EXTRA_DIST += ext/asio/asio/detail/epoll_reactor_fwd.hpp
-EXTRA_DIST += ext/asio/asio/detail/push_options.hpp
-EXTRA_DIST += ext/asio/asio/detail/win_signal_blocker.hpp
-EXTRA_DIST += ext/asio/asio/detail/eventfd_select_interrupter.hpp
+EXTRA_DIST += ext/asio/asio/detail/dev_poll_reactor.hpp
EXTRA_DIST += ext/asio/asio/detail/select_reactor.hpp
-EXTRA_DIST += ext/asio/asio/detail/old_win_sdk_compat.hpp
+EXTRA_DIST += ext/asio/asio/detail/gcc_arm_fenced_block.hpp
+EXTRA_DIST += ext/asio/asio/detail/consuming_buffers.hpp
+EXTRA_DIST += ext/asio/asio/detail/reactor_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/base_from_completion_cond.hpp
+EXTRA_DIST += ext/asio/asio/detail/epoll_reactor.hpp
+EXTRA_DIST += ext/asio/asio/detail/bind_handler.hpp
+EXTRA_DIST += ext/asio/asio/detail/strand_service.hpp
+EXTRA_DIST += ext/asio/asio/detail/op_queue.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_mutex.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_iocp_operation.hpp
+EXTRA_DIST += ext/asio/asio/detail/pipe_select_interrupter.hpp
+EXTRA_DIST += ext/asio/asio/detail/wince_thread.hpp
+EXTRA_DIST += ext/asio/asio/detail/buffered_stream_storage.hpp
+EXTRA_DIST += ext/asio/asio/detail/mutex.hpp
+EXTRA_DIST += ext/asio/asio/detail/posix_mutex.hpp
EXTRA_DIST += ext/asio/asio/detail/reactor_op_queue.hpp
-EXTRA_DIST += ext/asio/asio/detail/null_thread.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_event.hpp
+EXTRA_DIST += ext/asio/asio/detail/select_interrupter.hpp
+EXTRA_DIST += ext/asio/asio/detail/io_control.hpp
EXTRA_DIST += ext/asio/asio/detail/buffer_sequence_adapter.hpp
-EXTRA_DIST += ext/asio/asio/detail/posix_event.hpp
-EXTRA_DIST += ext/asio/asio/detail/thread.hpp
-EXTRA_DIST += ext/asio/asio/detail/handler_invoke_helpers.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_iocp_io_service.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_iocp_handle_service.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_iocp_socket_send_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/epoll_reactor_fwd.hpp
+EXTRA_DIST += ext/asio/asio/detail/operation.hpp
+EXTRA_DIST += ext/asio/asio/detail/descriptor_ops.hpp
+EXTRA_DIST += ext/asio/asio/detail/reactor.hpp
+EXTRA_DIST += ext/asio/asio/detail/shared_ptr.hpp
+EXTRA_DIST += ext/asio/asio/detail/winsock_init.hpp
+EXTRA_DIST += ext/asio/asio/detail/timer_queue_set.hpp
+EXTRA_DIST += ext/asio/asio/detail/completion_handler.hpp
+EXTRA_DIST += ext/asio/asio/detail/reactive_serial_port_service.hpp
+EXTRA_DIST += ext/asio/asio/detail/fenced_block.hpp
EXTRA_DIST += ext/asio/asio/detail/null_event.hpp
+EXTRA_DIST += ext/asio/asio/detail/hash_map.hpp
+EXTRA_DIST += ext/asio/asio/detail/gcc_sync_fenced_block.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_tss_ptr.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_fd_set_adapter.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_iocp_null_buffers_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/timer_queue_fwd.hpp
+EXTRA_DIST += ext/asio/asio/detail/old_win_sdk_compat.hpp
+EXTRA_DIST += ext/asio/asio/detail/call_stack.hpp
+EXTRA_DIST += ext/asio/asio/detail/weak_ptr.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_iocp_socket_accept_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/gcc_x86_fenced_block.hpp
+EXTRA_DIST += ext/asio/asio/detail/gcc_fenced_block.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_iocp_socket_service.hpp
+EXTRA_DIST += ext/asio/asio/detail/null_mutex.hpp
+EXTRA_DIST += ext/asio/asio/detail/reactive_socket_recvfrom_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/posix_event.hpp
EXTRA_DIST += ext/asio/asio/detail/service_id.hpp
-EXTRA_DIST += ext/asio/asio/detail/socket_types.hpp
+EXTRA_DIST += ext/asio/asio/detail/kqueue_reactor.hpp
+EXTRA_DIST += ext/asio/asio/detail/regex_fwd.hpp
+EXTRA_DIST += ext/asio/asio/detail/reactive_socket_sendto_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/push_options.hpp
+EXTRA_DIST += ext/asio/asio/detail/null_thread.hpp
+EXTRA_DIST += ext/asio/asio/detail/socket_select_interrupter.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_iocp_socket_service_base.hpp
EXTRA_DIST += ext/asio/asio/detail/throw_error.hpp
-EXTRA_DIST += ext/asio/asio/detail/timer_op.hpp
-EXTRA_DIST += ext/asio/asio/detail/win_mutex.hpp
-EXTRA_DIST += ext/asio/asio/detail/reactive_descriptor_service.hpp
-EXTRA_DIST += ext/asio/asio/detail/resolver_service.hpp
-EXTRA_DIST += ext/asio/asio/detail/op_queue.hpp
-EXTRA_DIST += ext/asio/asio/detail/dev_poll_reactor.hpp
-EXTRA_DIST += ext/asio/asio/detail/win_thread.hpp
-EXTRA_DIST += ext/asio/asio/detail/win_iocp_operation.hpp
-EXTRA_DIST += ext/asio/asio/detail/service_base.hpp
-EXTRA_DIST += ext/asio/asio/detail/select_reactor_fwd.hpp
-EXTRA_DIST += ext/asio/asio/detail/reactor_fwd.hpp
-EXTRA_DIST += ext/asio/asio/detail/win_iocp_socket_service.hpp
+EXTRA_DIST += ext/asio/asio/detail/null_signal_blocker.hpp
+EXTRA_DIST += ext/asio/asio/detail/reactive_socket_accept_op.hpp
EXTRA_DIST += ext/asio/asio/detail/wrapped_handler.hpp
-EXTRA_DIST += ext/asio/asio/detail/mutex.hpp
-EXTRA_DIST += ext/asio/asio/detail/completion_handler.hpp
-EXTRA_DIST += ext/asio/asio/detail/noncopyable.hpp
-EXTRA_DIST += ext/asio/asio/detail/task_io_service.hpp
-EXTRA_DIST += ext/asio/asio/detail/gcc_fenced_block.hpp
+EXTRA_DIST += ext/asio/asio/detail/object_pool.hpp
+EXTRA_DIST += ext/asio/asio/detail/timer_scheduler_fwd.hpp
+EXTRA_DIST += ext/asio/asio/detail/resolve_endpoint_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/array_fwd.hpp
+EXTRA_DIST += ext/asio/asio/detail/config.hpp
+EXTRA_DIST += ext/asio/asio/detail/socket_option.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_iocp_overlapped_ptr.hpp
EXTRA_DIST += ext/asio/asio/detail/win_fenced_block.hpp
-EXTRA_DIST += ext/asio/asio/detail/win_tss_ptr.hpp
-EXTRA_DIST += ext/asio/asio/detail/kqueue_reactor.hpp
+EXTRA_DIST += ext/asio/asio/detail/socket_types.hpp
+EXTRA_DIST += ext/asio/asio/detail/null_tss_ptr.hpp
+EXTRA_DIST += ext/asio/asio/detail/handler_invoke_helpers.hpp
+EXTRA_DIST += ext/asio/asio/detail/reactive_socket_send_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/reactive_null_buffers_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/pop_options.hpp
+EXTRA_DIST += ext/asio/asio/detail/resolver_service.hpp
+EXTRA_DIST += ext/asio/asio/detail/reactive_socket_service_base.hpp
+EXTRA_DIST += ext/asio/asio/detail/descriptor_read_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/reactive_socket_connect_op.hpp
EXTRA_DIST += ext/asio/asio/detail/timer_queue_base.hpp
-EXTRA_DIST += ext/asio/asio/detail/win_iocp_io_service.hpp
-EXTRA_DIST += ext/asio/asio/detail/gcc_x86_fenced_block.hpp
-EXTRA_DIST += ext/asio/asio/detail/posix_tss_ptr.hpp
-EXTRA_DIST += ext/asio/asio/detail/macos_fenced_block.hpp
-EXTRA_DIST += ext/asio/asio/detail/dev_poll_reactor_fwd.hpp
+EXTRA_DIST += ext/asio/asio/detail/reactor_fwd.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_iocp_socket_recvfrom_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_iocp_serial_port_service.hpp
+EXTRA_DIST += ext/asio/asio/detail/tss_ptr.hpp
+EXTRA_DIST += ext/asio/asio/detail/buffer_resize_guard.hpp
+EXTRA_DIST += ext/asio/asio/detail/kqueue_reactor_fwd.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_thread.hpp
+EXTRA_DIST += ext/asio/asio/detail/deadline_timer_service.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_iocp_socket_recv_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/timer_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/posix_thread.hpp
+EXTRA_DIST += ext/asio/asio/detail/signal_init.hpp
+EXTRA_DIST += ext/asio/asio/detail/descriptor_write_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_signal_blocker.hpp
+EXTRA_DIST += ext/asio/asio/detail/impl/reactive_socket_service_base.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/win_mutex.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/posix_event.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/win_iocp_io_service.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/select_reactor.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/posix_tss_ptr.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/task_io_service.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/dev_poll_reactor.hpp
+EXTRA_DIST += ext/asio/asio/detail/impl/select_reactor.hpp
+EXTRA_DIST += ext/asio/asio/detail/impl/eventfd_select_interrupter.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/epoll_reactor.hpp
+EXTRA_DIST += ext/asio/asio/detail/impl/strand_service.hpp
+EXTRA_DIST += ext/asio/asio/detail/impl/winsock_init.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/pipe_select_interrupter.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/win_iocp_serial_port_service.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/dev_poll_reactor.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/win_iocp_io_service.hpp
+EXTRA_DIST += ext/asio/asio/detail/impl/strand_service.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/win_iocp_socket_service_base.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/timer_queue.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/posix_mutex.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/reactive_serial_port_service.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/socket_ops.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/socket_select_interrupter.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/posix_thread.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/reactive_descriptor_service.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/kqueue_reactor.hpp
+EXTRA_DIST += ext/asio/asio/detail/impl/kqueue_reactor.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/win_event.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/timer_queue_set.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/win_tss_ptr.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/win_iocp_handle_service.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/resolver_service_base.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/win_thread.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/task_io_service.hpp
+EXTRA_DIST += ext/asio/asio/detail/impl/throw_error.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/epoll_reactor.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/service_registry.hpp
+EXTRA_DIST += ext/asio/asio/detail/impl/descriptor_ops.ipp
+EXTRA_DIST += ext/asio/asio/detail/impl/service_registry.ipp
+EXTRA_DIST += ext/asio/asio/detail/win_iocp_io_service_fwd.hpp
+EXTRA_DIST += ext/asio/asio/detail/fd_set_adapter.hpp
+EXTRA_DIST += ext/asio/asio/detail/task_io_service.hpp
+EXTRA_DIST += ext/asio/asio/detail/solaris_fenced_block.hpp
EXTRA_DIST += ext/asio/asio/detail/timer_queue.hpp
-EXTRA_DIST += ext/asio/asio/detail/reactive_serial_port_service.hpp
-EXTRA_DIST += ext/asio/asio/detail/null_tss_ptr.hpp
-EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_stream_service.hpp
-EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_operation.hpp
-EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_init.hpp
-EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_context_service.hpp
-EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_types.hpp
-EXTRA_DIST += ext/asio/asio/ssl/stream.hpp
-EXTRA_DIST += ext/asio/asio/ssl/stream_service.hpp
+EXTRA_DIST += ext/asio/asio/detail/handler_alloc_helpers.hpp
+EXTRA_DIST += ext/asio/asio/detail/scoped_lock.hpp
+EXTRA_DIST += ext/asio/asio/detail/win_iocp_handle_read_op.hpp
+EXTRA_DIST += ext/asio/asio/detail/service_registry_fwd.hpp
+EXTRA_DIST += ext/asio/asio/detail/service_registry.hpp
+EXTRA_DIST += ext/asio/asio/detail/posix_fd_set_adapter.hpp
+EXTRA_DIST += ext/asio/asio/detail/socket_ops.hpp
+EXTRA_DIST += ext/asio/asio/detail/posix_signal_blocker.hpp
+EXTRA_DIST += ext/asio/asio/serial_port_base.hpp
EXTRA_DIST += ext/asio/asio/ssl/context_base.hpp
EXTRA_DIST += ext/asio/asio/ssl/context.hpp
EXTRA_DIST += ext/asio/asio/ssl/context_service.hpp
+EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_types.hpp
+EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_context_service.hpp
+EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_stream_service.hpp
+EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_operation.hpp
+EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_init.hpp
EXTRA_DIST += ext/asio/asio/ssl/basic_context.hpp
+EXTRA_DIST += ext/asio/asio/ssl/stream_service.hpp
+EXTRA_DIST += ext/asio/asio/ssl/stream.hpp
EXTRA_DIST += ext/asio/asio/ssl/stream_base.hpp
-EXTRA_DIST += ext/asio/asio/posix/stream_descriptor.hpp
-EXTRA_DIST += ext/asio/asio/posix/stream_descriptor_service.hpp
+EXTRA_DIST += ext/asio/asio/basic_streambuf.hpp
+EXTRA_DIST += ext/asio/asio/serial_port_service.hpp
+EXTRA_DIST += ext/asio/asio/error.hpp
+EXTRA_DIST += ext/asio/asio/handler_alloc_hook.hpp
+EXTRA_DIST += ext/asio/asio/buffers_iterator.hpp
+EXTRA_DIST += ext/asio/asio/is_read_buffered.hpp
+EXTRA_DIST += ext/asio/asio/buffered_stream_fwd.hpp
+EXTRA_DIST += ext/asio/asio/placeholders.hpp
+EXTRA_DIST += ext/asio/asio/local/stream_protocol.hpp
+EXTRA_DIST += ext/asio/asio/local/detail/impl/endpoint.ipp
+EXTRA_DIST += ext/asio/asio/local/detail/endpoint.hpp
+EXTRA_DIST += ext/asio/asio/local/datagram_protocol.hpp
+EXTRA_DIST += ext/asio/asio/local/connect_pair.hpp
+EXTRA_DIST += ext/asio/asio/local/basic_endpoint.hpp
+EXTRA_DIST += ext/asio/asio/buffered_stream.hpp
+EXTRA_DIST += ext/asio/asio/basic_serial_port.hpp
+EXTRA_DIST += ext/asio/asio/datagram_socket_service.hpp
+EXTRA_DIST += ext/asio/asio/socket_base.hpp
+EXTRA_DIST += ext/asio/asio/io_service.hpp
+EXTRA_DIST += ext/asio/asio/ssl.hpp
+EXTRA_DIST += ext/asio/asio/basic_socket_iostream.hpp
+EXTRA_DIST += ext/asio/asio/basic_io_object.hpp
+EXTRA_DIST += ext/asio/asio/basic_socket_streambuf.hpp
+EXTRA_DIST += ext/asio/asio/error_code.hpp
+EXTRA_DIST += ext/asio/asio/basic_stream_socket.hpp
+EXTRA_DIST += ext/asio/asio/read_until.hpp
+EXTRA_DIST += ext/asio/asio/basic_streambuf_fwd.hpp
+EXTRA_DIST += ext/asio/asio/is_write_buffered.hpp
+EXTRA_DIST += ext/asio/asio/basic_datagram_socket.hpp
+EXTRA_DIST += ext/asio/asio/buffered_write_stream_fwd.hpp
+EXTRA_DIST += ext/asio/asio/basic_deadline_timer.hpp
+EXTRA_DIST += ext/asio/asio/socket_acceptor_service.hpp
+EXTRA_DIST += ext/asio/asio/raw_socket_service.hpp
+EXTRA_DIST += ext/asio/asio/buffered_read_stream.hpp
+EXTRA_DIST += ext/asio/asio/time_traits.hpp
+EXTRA_DIST += ext/asio/asio/completion_condition.hpp
EXTRA_DIST += ext/asio/asio/posix/basic_stream_descriptor.hpp
EXTRA_DIST += ext/asio/asio/posix/basic_descriptor.hpp
EXTRA_DIST += ext/asio/asio/posix/descriptor_base.hpp
+EXTRA_DIST += ext/asio/asio/posix/stream_descriptor_service.hpp
+EXTRA_DIST += ext/asio/asio/posix/stream_descriptor.hpp
+EXTRA_DIST += ext/asio/asio/write.hpp
+EXTRA_DIST += ext/asio/asio/write_at.hpp
+EXTRA_DIST += ext/asio/asio/basic_raw_socket.hpp
+EXTRA_DIST += ext/asio/asio/serial_port.hpp
+EXTRA_DIST += ext/asio/asio/windows/basic_stream_handle.hpp
+EXTRA_DIST += ext/asio/asio/windows/basic_handle.hpp
+EXTRA_DIST += ext/asio/asio/windows/random_access_handle.hpp
+EXTRA_DIST += ext/asio/asio/windows/overlapped_ptr.hpp
+EXTRA_DIST += ext/asio/asio/windows/stream_handle.hpp
+EXTRA_DIST += ext/asio/asio/windows/random_access_handle_service.hpp
+EXTRA_DIST += ext/asio/asio/windows/stream_handle_service.hpp
+EXTRA_DIST += ext/asio/asio/windows/basic_random_access_handle.hpp
+EXTRA_DIST += ext/asio/asio/read.hpp
+EXTRA_DIST += ext/asio/asio/deadline_timer_service.hpp
+EXTRA_DIST += ext/asio/asio/buffered_write_stream.hpp
+EXTRA_DIST += ext/asio/asio/buffer.hpp
+EXTRA_DIST += ext/asio/asio/impl/read_until.ipp
+EXTRA_DIST += ext/asio/asio/impl/serial_port_base.hpp
+EXTRA_DIST += ext/asio/asio/impl/read_at.ipp
+EXTRA_DIST += ext/asio/asio/impl/read.ipp
+EXTRA_DIST += ext/asio/asio/impl/error.ipp
+EXTRA_DIST += ext/asio/asio/impl/io_service.ipp
+EXTRA_DIST += ext/asio/asio/impl/io_service.hpp
+EXTRA_DIST += ext/asio/asio/impl/src.hpp
+EXTRA_DIST += ext/asio/asio/impl/src.cpp
+EXTRA_DIST += ext/asio/asio/impl/read_until.hpp
+EXTRA_DIST += ext/asio/asio/impl/serial_port_base.ipp
+EXTRA_DIST += ext/asio/asio/impl/write.hpp
+EXTRA_DIST += ext/asio/asio/impl/write_at.hpp
+EXTRA_DIST += ext/asio/asio/impl/write.ipp
+EXTRA_DIST += ext/asio/asio/impl/read.hpp
+EXTRA_DIST += ext/asio/asio/impl/write_at.ipp
+EXTRA_DIST += ext/asio/asio/impl/error_code.ipp
+EXTRA_DIST += ext/asio/asio/impl/read_at.hpp
+EXTRA_DIST += ext/asio/asio/strand.hpp
+EXTRA_DIST += ext/asio/asio/version.hpp
+EXTRA_DIST += ext/asio/asio/basic_socket_acceptor.hpp
+EXTRA_DIST += ext/asio/asio/ip/basic_resolver_query.hpp
+EXTRA_DIST += ext/asio/asio/ip/address.hpp
+EXTRA_DIST += ext/asio/asio/ip/host_name.hpp
EXTRA_DIST += ext/asio/asio/ip/detail/socket_option.hpp
+EXTRA_DIST += ext/asio/asio/ip/detail/impl/endpoint.ipp
+EXTRA_DIST += ext/asio/asio/ip/detail/endpoint.hpp
+EXTRA_DIST += ext/asio/asio/ip/udp.hpp
+EXTRA_DIST += ext/asio/asio/ip/basic_resolver_iterator.hpp
EXTRA_DIST += ext/asio/asio/ip/v6_only.hpp
EXTRA_DIST += ext/asio/asio/ip/address_v4.hpp
-EXTRA_DIST += ext/asio/asio/ip/address_v6.hpp
-EXTRA_DIST += ext/asio/asio/ip/basic_resolver.hpp
+EXTRA_DIST += ext/asio/asio/ip/resolver_query_base.hpp
EXTRA_DIST += ext/asio/asio/ip/multicast.hpp
+EXTRA_DIST += ext/asio/asio/ip/address_v6.hpp
+EXTRA_DIST += ext/asio/asio/ip/tcp.hpp
+EXTRA_DIST += ext/asio/asio/ip/basic_resolver_entry.hpp
EXTRA_DIST += ext/asio/asio/ip/unicast.hpp
-EXTRA_DIST += ext/asio/asio/ip/basic_resolver_iterator.hpp
-EXTRA_DIST += ext/asio/asio/ip/host_name.hpp
-EXTRA_DIST += ext/asio/asio/ip/resolver_query_base.hpp
-EXTRA_DIST += ext/asio/asio/ip/basic_endpoint.hpp
EXTRA_DIST += ext/asio/asio/ip/resolver_service.hpp
-EXTRA_DIST += ext/asio/asio/ip/basic_resolver_entry.hpp
-EXTRA_DIST += ext/asio/asio/ip/address.hpp
-EXTRA_DIST += ext/asio/asio/ip/tcp.hpp
-EXTRA_DIST += ext/asio/asio/ip/basic_resolver_query.hpp
-EXTRA_DIST += ext/asio/asio/ip/udp.hpp
EXTRA_DIST += ext/asio/asio/ip/icmp.hpp
-EXTRA_DIST += ext/asio/asio/error.hpp
-EXTRA_DIST += ext/asio/asio/basic_socket.hpp
-EXTRA_DIST += ext/asio/asio/buffered_stream.hpp
-EXTRA_DIST += ext/asio/asio/system_error.hpp
-EXTRA_DIST += ext/asio/asio/basic_io_object.hpp
-EXTRA_DIST += ext/asio/asio/read_at.hpp
-EXTRA_DIST += ext/asio/asio/basic_raw_socket.hpp
-EXTRA_DIST += ext/asio/asio/serial_port_service.hpp
-EXTRA_DIST += ext/asio/asio/basic_stream_socket.hpp
-EXTRA_DIST += ext/asio/asio/placeholders.hpp
-EXTRA_DIST += ext/asio/asio/basic_deadline_timer.hpp
-EXTRA_DIST += ext/asio/asio/thread.hpp
-EXTRA_DIST += ext/asio/asio/buffered_write_stream_fwd.hpp
-EXTRA_DIST += ext/asio/asio/datagram_socket_service.hpp
+EXTRA_DIST += ext/asio/asio/ip/basic_endpoint.hpp
+EXTRA_DIST += ext/asio/asio/ip/basic_resolver.hpp
+EXTRA_DIST += ext/asio/asio/ip/impl/address.hpp
+EXTRA_DIST += ext/asio/asio/ip/impl/address_v4.hpp
+EXTRA_DIST += ext/asio/asio/ip/impl/address_v4.ipp
+EXTRA_DIST += ext/asio/asio/ip/impl/address_v6.hpp
+EXTRA_DIST += ext/asio/asio/ip/impl/address.ipp
+EXTRA_DIST += ext/asio/asio/ip/impl/host_name.ipp
+EXTRA_DIST += ext/asio/asio/ip/impl/basic_endpoint.hpp
+EXTRA_DIST += ext/asio/asio/ip/impl/address_v6.ipp
EXTRA_DIST += ext/asio/asio/handler_invoke_hook.hpp
-EXTRA_DIST += ext/asio/asio/is_read_buffered.hpp
-EXTRA_DIST += ext/asio/asio/buffer.hpp
-EXTRA_DIST += ext/asio/asio/basic_socket_acceptor.hpp
-EXTRA_DIST += ext/asio/asio/write_at.hpp
-EXTRA_DIST += ext/asio/asio/completion_condition.hpp
-EXTRA_DIST += ext/asio/asio/raw_socket_service.hpp
-EXTRA_DIST += ext/asio/asio/socket_base.hpp
-EXTRA_DIST += ext/asio/asio/serial_port.hpp
-EXTRA_DIST += ext/asio/asio/error_code.hpp
-EXTRA_DIST += ext/asio/asio/basic_serial_port.hpp
-EXTRA_DIST += ext/asio/asio/version.hpp
-EXTRA_DIST += ext/asio/asio/deadline_timer_service.hpp
-EXTRA_DIST += ext/asio/asio/io_service.hpp
-EXTRA_DIST += ext/asio/asio/buffered_read_stream.hpp
-EXTRA_DIST += ext/asio/asio/streambuf.hpp
-EXTRA_DIST += ext/asio/asio/basic_datagram_socket.hpp
-EXTRA_DIST += ext/asio/asio/basic_streambuf.hpp
-EXTRA_DIST += ext/asio/asio/write.hpp
-EXTRA_DIST += ext/asio/asio/strand.hpp
-EXTRA_DIST += ext/asio/asio/basic_socket_iostream.hpp
-EXTRA_DIST += ext/asio/asio/buffered_stream_fwd.hpp
-EXTRA_DIST += ext/asio/asio/basic_socket_streambuf.hpp
-EXTRA_DIST += ext/asio/asio/ssl.hpp
+EXTRA_DIST += ext/asio/asio/read_at.hpp
+EXTRA_DIST += ext/asio/asio/buffered_read_stream_fwd.hpp
+EXTRA_DIST += ext/asio/asio/system_error.hpp
EXTRA_DIST += ext/asio/asio/deadline_timer.hpp
-EXTRA_DIST += ext/asio/asio/buffers_iterator.hpp
-EXTRA_DIST += ext/asio/asio/handler_alloc_hook.hpp
-EXTRA_DIST += ext/asio/asio/buffered_write_stream.hpp
-EXTRA_DIST += ext/asio/asio/read.hpp
-EXTRA_DIST += ext/asio/asio/serial_port_base.hpp
EXTRA_DIST += ext/asio/asio/stream_socket_service.hpp
-EXTRA_DIST += ext/asio/asio/time_traits.hpp
-EXTRA_DIST += ext/asio/asio/read_until.hpp
-EXTRA_DIST += ext/asio/asio/is_write_buffered.hpp
-EXTRA_DIST += ext/asio/asio/buffered_read_stream_fwd.hpp
-EXTRA_DIST += ext/asio/asio/socket_acceptor_service.hpp
-EXTRA_DIST += ext/asio/asio.hpp
EXTRA_DIST += ext/coroutine/coroutine.h
diff --git a/README b/README
index b10d12e..a6509da 100644
--- a/README
+++ b/README
@@ -15,11 +15,13 @@ five year plan are described here:
This release includes the bind10 master process, b10-msgq message
bus, b10-auth authoritative DNS server (with SQLite3 and in-memory
-backends), b10-resolver forwarding DNS server, b10-cmdctl remote
-control daemon, b10-cfgmgr configuration manager, b10-xfrin AXFR
-inbound service, b10-xfrout outgoing AXFR service, b10-zonemgr
+backends), b10-resolver recursive or forwarding DNS server, b10-cmdctl
+remote control daemon, b10-cfgmgr configuration manager, b10-xfrin
+AXFR inbound service, b10-xfrout outgoing AXFR service, b10-zonemgr
secondary manager, b10-stats statistics collection and reporting
-daemon, and a new libdns++ library for C++ with a python wrapper.
+daemon, b10-stats-httpd for HTTP access to XML-formatted stats,
+b10-host DNS lookup utility, and a new libdns++ library for C++
+with a python wrapper.
Documentation is included and also available via the BIND 10
website at http://bind10.isc.org/
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..e69de29
diff --git a/configure.ac b/configure.ac
index acc7628..8bd37a2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.59])
-AC_INIT(bind10-devel, 20110224, bind10-dev at isc.org)
+AC_INIT(bind10-devel, 20110519, bind10-dev at isc.org)
AC_CONFIG_SRCDIR(README)
AM_INIT_AUTOMAKE
AC_CONFIG_HEADERS([config.h])
@@ -280,6 +280,35 @@ namespace isc {class Bar {Foo foo_;};} ],,
[AC_MSG_RESULT(yes)])
CXXFLAGS="$CXXFLAGS_SAVED"
+# Python 3.2 has an unused parameter in one of its headers. This
+# has been reported, but not fixed as of yet, so we check if we need
+# to set -Wno-unused-parameter.
+if test $werror_ok = 1; then
+ CPPFLAGS_SAVED="$CPPFLAGS"
+ CPPFLAGS=${PYTHON_INCLUDES}
+ CXXFLAGS_SAVED="$CXXFLAGS"
+ CXXFLAGS="$CXXFLAGS $B10_CXXFLAGS -Werror"
+ AC_MSG_CHECKING([whether we need -Wno-unused-parameter for python])
+ AC_TRY_COMPILE(
+ [#include <Python.h>],
+ [],
+ [AC_MSG_RESULT(no)],
+ [
+ CXXFLAGS="$CXXFLAGS -Wno-unused-parameter"
+ AC_TRY_COMPILE([#include <Python.h>],
+ [],
+ [AC_MSG_RESULT(yes)
+ PYTHON_CXXFLAGS="${PYTHON_CXXFLAGS} -Wno-unused-parameter"
+ AC_SUBST(PYTHON_CXXFLAGS)
+ ],
+ [AC_MSG_ERROR([Can't compile against Python.h])]
+ )
+ ]
+ )
+ CXXFLAGS="$CXXFLAGS_SAVED"
+ CPPFLAGS="$CPPFLAGS_SAVED"
+fi
+
fi dnl GXX = yes
AM_CONDITIONAL(GCC_WERROR_OK, test $werror_ok = 1)
@@ -374,6 +403,128 @@ if test "$lcov" != "no"; then
fi
AC_SUBST(USE_LCOV)
+# Check for Botan
+botan_path="yes"
+AC_ARG_WITH([botan],
+ AC_HELP_STRING([--with-botan=PATH],
+ [specify exact directory of Botan library]),
+ [botan_path="$withval"])
+if test "${botan_path}" == "no" ; then
+ AC_MSG_ERROR([Need botan for libcryptolink])
+fi
+if test "${botan_path}" != "yes" ; then
+ if test -x "${botan_path}/bin/botan-config" ; then
+ BOTAN_CONFIG="${botan_path}/bin/botan-config"
+ else
+ AC_MSG_ERROR([${botan_path}/bin/botan-config not found])
+ fi
+else
+ AC_PATH_PROG([BOTAN_CONFIG], [botan-config])
+fi
+
+if test -x "${BOTAN_CONFIG}" ; then
+ BOTAN_LDFLAGS=`${BOTAN_CONFIG} --libs`
+ # We expect botan-config --libs to contain -L<path_to_libbotan>, but
+ # this is not always the case. As a heuristics workaround we add
+ # -L`botan-config --prefix/lib` in this case. Same for BOTAN_INCLUDES
+ # (but using include instead of lib) below.
+ echo ${BOTAN_LDFLAGS} | grep -- -L > /dev/null || \
+ BOTAN_LDFLAGS="-L`${BOTAN_CONFIG} --prefix`/lib ${BOTAN_LDFLAGS}"
+ BOTAN_INCLUDES=`${BOTAN_CONFIG} --cflags`
+ echo ${BOTAN_INCLUDES} | grep -- -I > /dev/null || \
+ BOTAN_INCLUDES="-I`${BOTAN_CONFIG} --prefix`/include ${BOTAN_INCLUDES}"
+ # See python_rpath for some info on why we do this
+ if test $rpath_available = yes; then
+ BOTAN_RPATH=
+ for flag in ${BOTAN_LDFLAGS}; do
+ BOTAN_RPATH="${BOTAN_RPATH} `echo $flag | sed -ne 's/^\(\-L\)/-R/p'`"
+ done
+ AC_SUBST(BOTAN_RPATH)
+
+ # According to the libtool manual, it should be sufficient if we
+ # specify the "-R libdir" in our wrapper library of botan (no other
+ # programs will need libbotan directly); "libdir" should be added to
+ # the program's binary image. But we've seen in our build environments
+ # that (some versions of?) libtool doesn't propagate -R as documented,
+ # and it caused a linker error at run time. To work around this, we
+ # also add the rpath to the global LDFLAGS.
+ LDFLAGS="$BOTAN_RPATH $LDFLAGS"
+ fi
+
+ AC_SUBST(BOTAN_LDFLAGS)
+ AC_SUBST(BOTAN_INCLUDES)
+fi
+
+CPPFLAGS_SAVED=$CPPFLAGS
+CPPFLAGS="$BOTAN_INCLUDES $CPPFLAGS"
+LDFLAGS_SAVED="$LDFLAGS"
+LDFLAGS="$BOTAN_LDFLAGS $LDFLAGS"
+
+AC_CHECK_HEADERS([botan/botan.h],,AC_MSG_ERROR([Missing required header files.]))
+AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([#include <botan/botan.h>
+ #include <botan/hash.h>
+ ],
+ [using namespace Botan;
+ LibraryInitializer::initialize();
+ HashFunction *h = get_hash("MD5");
+ ])],
+ [AC_MSG_RESULT([checking for Botan library... yes])],
+ [AC_MSG_RESULT([checking for Botan library... no])
+ AC_MSG_ERROR([Needs Botan library 1.8 or higher])]
+)
+CPPFLAGS=$CPPFLAGS_SAVED
+LDFLAGS=$LDFLAGS_SAVED
+
+# Check for log4cplus
+log4cplus_path="yes"
+AC_ARG_WITH([log4cplus],
+ AC_HELP_STRING([--with-log4cplus=PATH],
+ [specify exact directory of log4cplus library and headers]),
+ [log4cplus_path="$withval"])
+if test "${log4cplus_path}" == "no" ; then
+ AC_MSG_ERROR([Need log4cplus])
+elif test "${log4cplus_path}" != "yes" ; then
+ LOG4CPLUS_INCLUDES="-I${log4cplus_path}/include"
+ LOG4CPLUS_LDFLAGS="-L${log4cplus_path}/lib"
+else
+# If not specified, try some common paths.
+ log4cplusdirs="/usr/local /usr/pkg /opt /opt/local"
+ for d in $log4cplusdirs
+ do
+ if test -f $d/include/log4cplus/logger.h; then
+ LOG4CPLUS_INCLUDES="-I$d/include"
+ LOG4CPLUS_LDFLAGS="-L$d/lib"
+ break
+ fi
+ done
+fi
+
+LOG4CPLUS_LDFLAGS="$LOG4CPLUS_LDFLAGS -llog4cplus $MULTITHREADING_FLAG"
+
+AC_SUBST(LOG4CPLUS_LDFLAGS)
+AC_SUBST(LOG4CPLUS_INCLUDES)
+
+CPPFLAGS_SAVED=$CPPFLAGS
+CPPFLAGS="$LOG4CPLUS_INCLUDES $CPPFLAGS"
+LDFLAGS_SAVED="$LDFLAGS"
+LDFLAGS="$LOG4CPLUS_LDFLAGS $LDFLAGS"
+
+AC_CHECK_HEADERS([log4cplus/logger.h],,AC_MSG_ERROR([Missing required header files.]))
+AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([#include <log4cplus/logger.h>
+ ],
+ [using namespace log4cplus;
+ Logger logger = Logger::getInstance("main");
+ ])],
+ [AC_MSG_RESULT([checking for log4cplus library... yes])],
+ [AC_MSG_RESULT([checking for log4cplus library... no])
+ AC_MSG_ERROR([Needs log4cplus library])]
+)
+
+CPPFLAGS=$CPPFLAGS_SAVED
+LDFLAGS=$LDFLAGS_SAVED
+
#
# Configure Boost header path
#
@@ -612,6 +763,8 @@ AC_CONFIG_FILES([Makefile
src/bin/bindctl/Makefile
src/bin/bindctl/tests/Makefile
src/bin/cfgmgr/Makefile
+ src/bin/cfgmgr/plugins/Makefile
+ src/bin/cfgmgr/plugins/tests/Makefile
src/bin/cfgmgr/tests/Makefile
src/bin/host/Makefile
src/bin/loadzone/Makefile
@@ -622,8 +775,12 @@ AC_CONFIG_FILES([Makefile
src/bin/auth/Makefile
src/bin/auth/tests/Makefile
src/bin/auth/benchmarks/Makefile
+ src/bin/dhcp6/Makefile
+ src/bin/dhcp6/tests/Makefile
src/bin/resolver/Makefile
src/bin/resolver/tests/Makefile
+ src/bin/sockcreator/Makefile
+ src/bin/sockcreator/tests/Makefile
src/bin/xfrin/Makefile
src/bin/xfrin/tests/Makefile
src/bin/xfrout/Makefile
@@ -637,11 +794,14 @@ AC_CONFIG_FILES([Makefile
src/bin/stats/tests/isc/config/Makefile
src/bin/stats/tests/isc/util/Makefile
src/bin/stats/tests/testdata/Makefile
+ src/bin/stats/tests/http/Makefile
src/bin/usermgr/Makefile
src/bin/tests/Makefile
src/lib/Makefile
src/lib/asiolink/Makefile
src/lib/asiolink/tests/Makefile
+ src/lib/asiodns/Makefile
+ src/lib/asiodns/tests/Makefile
src/lib/bench/Makefile
src/lib/bench/example/Makefile
src/lib/bench/tests/Makefile
@@ -663,14 +823,18 @@ AC_CONFIG_FILES([Makefile
src/lib/python/isc/net/tests/Makefile
src/lib/python/isc/notify/Makefile
src/lib/python/isc/notify/tests/Makefile
+ src/lib/python/isc/testutils/Makefile
src/lib/config/Makefile
src/lib/config/tests/Makefile
src/lib/config/tests/testdata/Makefile
+ src/lib/cryptolink/Makefile
+ src/lib/cryptolink/tests/Makefile
src/lib/dns/Makefile
src/lib/dns/tests/Makefile
src/lib/dns/tests/testdata/Makefile
src/lib/dns/python/Makefile
src/lib/dns/python/tests/Makefile
+ src/lib/dns/benchmarks/Makefile
src/lib/exceptions/Makefile
src/lib/exceptions/tests/Makefile
src/lib/datasrc/Makefile
@@ -689,8 +853,18 @@ AC_CONFIG_FILES([Makefile
src/lib/cache/tests/Makefile
src/lib/server_common/Makefile
src/lib/server_common/tests/Makefile
+ src/lib/util/Makefile
+ src/lib/util/io/Makefile
+ src/lib/util/unittests/Makefile
+ src/lib/util/pyunittests/Makefile
+ src/lib/util/tests/Makefile
+ src/lib/acl/Makefile
+ src/lib/acl/tests/Makefile
tests/Makefile
tests/system/Makefile
+ tests/tools/Makefile
+ tests/tools/badpacket/Makefile
+ tests/tools/badpacket/tests/Makefile
])
AC_OUTPUT([doc/version.ent
src/bin/cfgmgr/b10-cfgmgr.py
@@ -705,6 +879,7 @@ AC_OUTPUT([doc/version.ent
src/bin/xfrout/xfrout.py
src/bin/xfrout/xfrout.spec.pre
src/bin/xfrout/tests/xfrout_test
+ src/bin/xfrout/tests/xfrout_test.py
src/bin/xfrout/run_b10-xfrout.sh
src/bin/resolver/resolver.spec.pre
src/bin/resolver/spec_config.h.pre
@@ -713,15 +888,10 @@ AC_OUTPUT([doc/version.ent
src/bin/zonemgr/tests/zonemgr_test
src/bin/zonemgr/run_b10-zonemgr.sh
src/bin/stats/stats.py
- src/bin/stats/stats_stub.py
- src/bin/stats/stats.spec.pre
- src/bin/stats/run_b10-stats.sh
- src/bin/stats/run_b10-stats_stub.sh
- src/bin/stats/tests/stats_test
+ src/bin/stats/stats_httpd.py
src/bin/bind10/bind10.py
- src/bin/bind10/tests/bind10_test
- src/bin/bind10/tests/bind10_test.py
src/bin/bind10/run_bind10.sh
+ src/bin/bind10/tests/bind10_test.py
src/bin/bindctl/run_bindctl.sh
src/bin/bindctl/bindctl_main.py
src/bin/bindctl/tests/bindctl_test
@@ -736,18 +906,26 @@ AC_OUTPUT([doc/version.ent
src/bin/msgq/run_msgq.sh
src/bin/auth/auth.spec.pre
src/bin/auth/spec_config.h.pre
+ src/bin/dhcp6/spec_config.h.pre
src/bin/tests/process_rename_test.py
src/lib/config/tests/data_def_unittests_config.h
src/lib/python/isc/config/tests/config_test
src/lib/python/isc/cc/tests/cc_test
- src/lib/python/isc/log/tests/log_test
src/lib/python/isc/notify/tests/notify_out_test
+ src/lib/python/isc/log/tests/log_console.py
src/lib/dns/gen-rdatacode.py
src/lib/python/bind10_config.py
src/lib/dns/tests/testdata/gen-wiredata.py
src/lib/cc/session_config.h.pre
src/lib/cc/tests/session_unittests_config.h
- src/lib/log/tests/run_time_init_test.sh
+ src/lib/log/tests/console_test.sh
+ src/lib/log/tests/destination_test.sh
+ src/lib/log/tests/init_logger_test.sh
+ src/lib/log/tests/local_file_test.sh
+ src/lib/log/tests/severity_test.sh
+ src/lib/log/tests/tempdir.h
+ src/lib/util/python/mkpywrapper.py
+ src/lib/server_common/tests/data_path.h
tests/system/conf.sh
tests/system/glue/setup.sh
tests/system/glue/nsx1/b10-config.db
@@ -757,9 +935,6 @@ AC_OUTPUT([doc/version.ent
chmod +x src/bin/xfrin/run_b10-xfrin.sh
chmod +x src/bin/xfrout/run_b10-xfrout.sh
chmod +x src/bin/zonemgr/run_b10-zonemgr.sh
- chmod +x src/bin/stats/tests/stats_test
- chmod +x src/bin/stats/run_b10-stats.sh
- chmod +x src/bin/stats/run_b10-stats_stub.sh
chmod +x src/bin/bind10/run_bind10.sh
chmod +x src/bin/cmdctl/tests/cmdctl_test
chmod +x src/bin/xfrin/tests/xfrin_test
@@ -775,7 +950,13 @@ AC_OUTPUT([doc/version.ent
chmod +x src/bin/msgq/tests/msgq_test
chmod +x src/lib/dns/gen-rdatacode.py
chmod +x src/lib/dns/tests/testdata/gen-wiredata.py
- chmod +x src/lib/log/tests/run_time_init_test.sh
+ chmod +x src/lib/log/tests/console_test.sh
+ chmod +x src/lib/log/tests/destination_test.sh
+ chmod +x src/lib/log/tests/init_logger_test.sh
+ chmod +x src/lib/log/tests/local_file_test.sh
+ chmod +x src/lib/log/tests/severity_test.sh
+ chmod +x src/lib/util/python/mkpywrapper.py
+ chmod +x src/lib/python/isc/log/tests/log_console.py
chmod +x tests/system/conf.sh
])
AC_OUTPUT
@@ -798,12 +979,18 @@ Flags:
DEFS: $DEFS
CPPFLAGS: $CPPFLAGS
CXXFLAGS: $CXXFLAGS
+ LDFLAGS: $LDFLAGS
B10_CXXFLAGS: $B10_CXXFLAGS
dnl includes too
Python: ${PYTHON_INCLUDES}
+ ${PYTHON_CXXFLAGS}
${PYTHON_LDFLAGS}
${PYTHON_LIB}
Boost: ${BOOST_INCLUDES}
+ Botan: ${BOTAN_INCLUDES}
+ ${BOTAN_LDFLAGS}
+ Log4cplus: ${LOG4CPLUS_INCLUDES}
+ ${LOG4CPLUS_LDFLAGS}
SQLite: $SQLITE_CFLAGS
$SQLITE_LIBS
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 46aa178..b8a4656 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -568,7 +568,13 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/
+INPUT = ../src/lib/cc ../src/lib/config \
+ ../src/lib/cryptolink ../src/lib/dns ../src/lib/datasrc \
+ ../src/bin/auth ../src/bin/resolver ../src/lib/bench \
+ ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas \
+ ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \
+ ../src/bin/sockcreator/ ../src/lib/util/ \
+ ../src/lib/resolve ../src/lib/acl
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
@@ -1159,7 +1165,7 @@ XML_DTD =
# and cross-referencing information) to the XML output. Note that
# enabling this will significantly increase the size of the XML output.
-XML_PROGRAMLISTING = YES
+XML_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
diff --git a/doc/guide/Makefile.am b/doc/guide/Makefile.am
index c790139..c84ad06 100644
--- a/doc/guide/Makefile.am
+++ b/doc/guide/Makefile.am
@@ -1,10 +1,12 @@
EXTRA_DIST = bind10-guide.css
-EXTRA_DIST += bind10-guide.html
-EXTRA_DIST += bind10-guide.xml
+EXTRA_DIST += bind10-guide.xml bind10-guide.html
+EXTRA_DIST += bind10-messages.xml bind10-messages.html
# This is not a "man" manual, but reuse this for now for docbook.
if ENABLE_MAN
+.PHONY: bind10-messages.xml
+
bind10-guide.html: bind10-guide.xml
xsltproc --novalid --xinclude --nonet \
--path $(top_builddir)/doc \
@@ -13,4 +15,16 @@ bind10-guide.html: bind10-guide.xml
http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl \
$(srcdir)/bind10-guide.xml
+bind10-messages.html: bind10-messages.xml
+ xsltproc --novalid --xinclude --nonet \
+ --path $(top_builddir)/doc \
+ -o $@ \
+ --stringparam html.stylesheet $(srcdir)/bind10-guide.css \
+ http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl \
+ $(srcdir)/bind10-messages.xml
+
+# So many dependencies that it's easiest just to regenerate it every time
+bind10-messages.xml:
+ $(PYTHON) $(top_srcdir)/tools/system_messages.py -o $@ $(top_srcdir)
+
endif
diff --git a/doc/guide/bind10-guide.html b/doc/guide/bind10-guide.html
index fe6bd93..5754cf0 100644
--- a/doc/guide/bind10-guide.html
+++ b/doc/guide/bind10-guide.html
@@ -1,19 +1,19 @@
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Guide</title><link rel="stylesheet" href="./bind10-guide.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.75.2"><meta name="description" content="BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers. This is the reference guide for BIND 10 version 20110224. The most up-to-date version of this document, along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Guide"><div class="titlepage"><div><div><h1 class="title"><a name="id1168230298903"></a>BIND 10 Guide</h1></div><div><h2 class="subtitle">Administrator Reference for BIND 10</h2></div><div><p class="releaseinfo">This is the referenc
e guide for BIND 10 version
- 20110224.</p></div><div><p class="copyright">Copyright © 2010 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>BIND 10 is a Domain Name System (DNS) suite managed by
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Guide</title><link rel="stylesheet" href="./bind10-guide.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.75.2"><meta name="description" content="BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers. This is the reference guide for BIND 10 version 20110519. The most up-to-date version of this document, along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Guide"><div class="titlepage"><div><div><h1 class="title"><a name="id1168230298903"></a>BIND 10 Guide</h1></div><div><h2 class="subtitle">Administrator Reference for BIND 10</h2></div><div><p class="releaseinfo">This is the referenc
e guide for BIND 10 version
+ 20110519.</p></div><div><p class="copyright">Copyright © 2010 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>BIND 10 is a Domain Name System (DNS) suite managed by
Internet Systems Consortium (ISC). It includes DNS libraries
and modular components for controlling authoritative and
recursive DNS servers.
</p><p>
- This is the reference guide for BIND 10 version 20110224.
+ This is the reference guide for BIND 10 version 20110519.
The most up-to-date version of this document, along with
- other documents for BIND 10, can be found at <a class="ulink" href="http://bind10.isc.org/docs" target="_top">http://bind10.isc.org/docs</a>. </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230299038">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230299065">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#installation">2. Installation</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230284842">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">In
stallation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285028">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230285047">Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168230285108">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230285205">Build</a></span></dt><dt><span class="section"><a href="#id1168230285221">Install</a></span></dt><dt><span class="section"><a href="#id1168230285244">Install Hierarchy</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#bind10">3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></a></span></dt><dd><dl><dt><span class="section"><a href="#start">Starting BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#msgq">4. Command channel</a></span></dt><dt><span class="chapter"><a href="#cfgmgr">5. Configuration manager</a></span></dt><dt><span class="chapter"><a hr
ef="#cmdctl">6. Remote control daemon</a></span></dt><dd><dl><dt><span class="section"><a href="#cmdctl.spec">Configuration specification for b10-cmdctl</a></span></dt></dl></dd><dt><span class="chapter"><a href="#bindctl">7. Control and configure user interface</a></span></dt><dt><span class="chapter"><a href="#authserver">8. Authoritative Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285822">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230285888">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230285918">Loading Master Zones Files</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrin">9. Incoming Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#xfrout">10. Outbound Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#zonemgr">11. Secondary Manager</a></span></dt><dt><span class="chapter"><a href="#resolverserver">12. Recursive Name Server<
/a></span></dt><dt><span class="chapter"><a href="#statistics">13. Statistics</a></span></dt></dl></div><div class="chapter" title="Chapter 1. Introduction"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230299038">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230299065">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></div><p>
+ other documents for BIND 10, can be found at <a class="ulink" href="http://bind10.isc.org/docs" target="_top">http://bind10.isc.org/docs</a>. </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230299038">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230299065">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#installation">2. Installation</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230284846">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">In
stallation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285026">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230285045">Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168230285106">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230285203">Build</a></span></dt><dt><span class="section"><a href="#id1168230285219">Install</a></span></dt><dt><span class="section"><a href="#id1168230285242">Install Hierarchy</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#bind10">3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></a></span></dt><dd><dl><dt><span class="section"><a href="#start">Starting BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#msgq">4. Command channel</a></span></dt><dt><span class="chapter"><a href="#cfgmgr">5. Configuration manager</a></span></dt><dt><span class="chapter"><a hr
ef="#cmdctl">6. Remote control daemon</a></span></dt><dd><dl><dt><span class="section"><a href="#cmdctl.spec">Configuration specification for b10-cmdctl</a></span></dt></dl></dd><dt><span class="chapter"><a href="#bindctl">7. Control and configure user interface</a></span></dt><dt><span class="chapter"><a href="#authserver">8. Authoritative Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285816">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230285881">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230285912">Loading Master Zones Files</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrin">9. Incoming Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#xfrout">10. Outbound Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#zonemgr">11. Secondary Manager</a></span></dt><dt><span class="chapter"><a href="#resolverserver">12. Recursive Name Server<
/a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230286300">Forwarding</a></span></dt></dl></dd><dt><span class="chapter"><a href="#statistics">13. Statistics</a></span></dt><dt><span class="chapter"><a href="#logging">14. Logging</a></span></dt></dl></div><div class="chapter" title="Chapter 1. Introduction"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230299038">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230299065">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></div><p>
BIND is the popular implementation of a DNS server, developer
interfaces, and DNS tools.
BIND 10 is a rewrite of BIND 9. BIND 10 is written in C++ and Python
and provides a modular environment for serving and maintaining DNS.
</p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
This guide covers the experimental prototype of
- BIND 10 version 20110224.
+ BIND 10 version 20110519.
</p></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
BIND 10 provides a EDNS0- and DNSSEC-capable
authoritative DNS server and a caching recursive name server
@@ -31,12 +31,16 @@
</p></div><div class="section" title="Required Software"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230299065"></a>Required Software</h2></div></div></div><p>
BIND 10 requires Python 3.1. Later versions may work, but Python
3.1 is the minimum version which will work.
- </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+ </p><p>
+ BIND 10 uses the Botan crypto library for C++. It requires
+ at least Botan version 1.8. To build BIND 10, install the
+ Botan libraries and development include headers.
+ </p><p>
The authoritative server requires SQLite 3.3.9 or newer.
The <span class="command"><strong>b10-xfrin</strong></span>, <span class="command"><strong>b10-xfrout</strong></span>,
and <span class="command"><strong>b10-zonemgr</strong></span> modules require the
libpython3 library and the Python _sqlite3.so module.
- </p></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+ </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
Some operating systems do not provide these dependencies
in their default installation nor standard packages
collections.
@@ -132,7 +136,7 @@
and, of course, DNS. These include detailed developer
documentation and code examples.
- </p></div><div class="chapter" title="Chapter 2. Installation"><div class="titlepage"><div><div><h2 class="title"><a name="installation"></a>Chapter 2. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230284842">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285028">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230285047">Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168230285108">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230285205">Build</a></span></dt><dt><span class="section"><a href="#id1168230285221">Install</a></span></dt><dt><span class="section"><a href="#id1168230285244">Install Hierarchy<
/a></span></dt></dl></dd></dl></div><div class="section" title="Building Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230284842"></a>Building Requirements</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+ </p></div><div class="chapter" title="Chapter 2. Installation"><div class="titlepage"><div><div><h2 class="title"><a name="installation"></a>Chapter 2. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230284846">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285026">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230285045">Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168230285106">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230285203">Build</a></span></dt><dt><span class="section"><a href="#id1168230285219">Install</a></span></dt><dt><span class="section"><a href="#id1168230285242">Install Hierarchy<
/a></span></dt></dl></dd></dl></div><div class="section" title="Building Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230284846"></a>Building Requirements</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
Some operating systems have split their distribution packages into
a run-time and a development package. You will need to install
the development package versions, which include header files and
@@ -158,10 +162,6 @@
and deploying BIND 10 as an authoritative name server using
its defaults. For troubleshooting, full customizations and further
details, see the respective chapters in the BIND 10 guide.
- </p></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
- The development prototype of the b10-auth server listens on
- 0.0.0.0 (all interfaces) port 5300. (This is not the standard
- domain service port.)
</p></div><p>
To quickly get started with BIND 10, follow these steps.
</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">
@@ -181,7 +181,7 @@
</p></li><li class="listitem"><p>Start the server:
</p><pre class="screen">$ <strong class="userinput"><code>/usr/local/sbin/bind10</code></strong></pre><p>
</p></li><li class="listitem"><p>Test it; for example:
- </p><pre class="screen">$ <strong class="userinput"><code>dig @127.0.0.1 -p 5300 -c CH -t TXT authors.bind</code></strong></pre><p>
+ </p><pre class="screen">$ <strong class="userinput"><code>dig @127.0.0.1 -c CH -t TXT authors.bind</code></strong></pre><p>
</p></li><li class="listitem"><p>Load desired zone file(s), for example:
</p><pre class="screen">$ <strong class="userinput"><code>b10-loadzone <em class="replaceable"><code>your.zone.example.org</code></em></code></strong></pre><p>
</p></li><li class="listitem">
@@ -192,14 +192,14 @@
the Git code revision control system or as a downloadable
tar file. It may also be available in pre-compiled ready-to-use
packages from operating system vendors.
- </p><div class="section" title="Download Tar File"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285028"></a>Download Tar File</h3></div></div></div><p>
+ </p><div class="section" title="Download Tar File"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285026"></a>Download Tar File</h3></div></div></div><p>
Downloading a release tar file is the recommended method to
obtain the source code.
</p><p>
The BIND 10 releases are available as tar file downloads from
<a class="ulink" href="ftp://ftp.isc.org/isc/bind10/" target="_top">ftp://ftp.isc.org/isc/bind10/</a>.
Periodic development snapshots may also be available.
- </p></div><div class="section" title="Retrieve from Git"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285047"></a>Retrieve from Git</h3></div></div></div><p>
+ </p></div><div class="section" title="Retrieve from Git"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285045"></a>Retrieve from Git</h3></div></div></div><p>
Downloading this "bleeding edge" code is recommended only for
developers or advanced users. Using development code in a production
environment is not recommended.
@@ -233,7 +233,7 @@
<span class="command"><strong>autoheader</strong></span>,
<span class="command"><strong>automake</strong></span>,
and related commands.
- </p></div><div class="section" title="Configure before the build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285108"></a>Configure before the build</h3></div></div></div><p>
+ </p></div><div class="section" title="Configure before the build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285106"></a>Configure before the build</h3></div></div></div><p>
BIND 10 uses the GNU Build System to discover build environment
details.
To generate the makefiles using the defaults, simply run:
@@ -264,16 +264,16 @@
</p><p>
If the configure fails, it may be due to missing or old
dependencies.
- </p></div><div class="section" title="Build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285205"></a>Build</h3></div></div></div><p>
+ </p></div><div class="section" title="Build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285203"></a>Build</h3></div></div></div><p>
After the configure step is complete, to build the executables
from the C++ code and prepare the Python scripts, run:
</p><pre class="screen">$ <strong class="userinput"><code>make</code></strong></pre><p>
- </p></div><div class="section" title="Install"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285221"></a>Install</h3></div></div></div><p>
+ </p></div><div class="section" title="Install"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285219"></a>Install</h3></div></div></div><p>
To install the BIND 10 executables, support files,
and documentation, run:
</p><pre class="screen">$ <strong class="userinput"><code>make install</code></strong></pre><p>
- </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The install step may require superuser privileges.</p></div></div><div class="section" title="Install Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285244"></a>Install Hierarchy</h3></div></div></div><p>
+ </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The install step may require superuser privileges.</p></div></div><div class="section" title="Install Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285242"></a>Install Hierarchy</h3></div></div></div><p>
The following is the layout of the complete BIND 10 installation:
</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">
<code class="filename">bin/</code> —
@@ -490,15 +490,12 @@ shutdown
the details and relays (over a <span class="command"><strong>b10-msgq</strong></span> command
channel) the configuration on to the specified module.
</p><p>
- </p></div><div class="chapter" title="Chapter 8. Authoritative Server"><div class="titlepage"><div><div><h2 class="title"><a name="authserver"></a>Chapter 8. Authoritative Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230285822">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230285888">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230285918">Loading Master Zones Files</a></span></dt></dl></div><p>
+ </p></div><div class="chapter" title="Chapter 8. Authoritative Server"><div class="titlepage"><div><div><h2 class="title"><a name="authserver"></a>Chapter 8. Authoritative Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230285816">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230285881">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230285912">Loading Master Zones Files</a></span></dt></dl></div><p>
The <span class="command"><strong>b10-auth</strong></span> is the authoritative DNS server.
It supports EDNS0 and DNSSEC. It supports IPv6.
Normally it is started by the <span class="command"><strong>bind10</strong></span> master
process.
- </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
- This development prototype release listens on all interfaces
- and the non-standard port 5300.
- </p></div><div class="section" title="Server Configurations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285822"></a>Server Configurations</h2></div></div></div><p>
+ </p><div class="section" title="Server Configurations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285816"></a>Server Configurations</h2></div></div></div><p>
<span class="command"><strong>b10-auth</strong></span> is configured via the
<span class="command"><strong>b10-cfgmgr</strong></span> configuration manager.
The module name is <span class="quote">“<span class="quote">Auth</span>”</span>.
@@ -518,7 +515,7 @@ This may be a temporary setting until then.
</p><div class="variablelist"><dl><dt><span class="term">shutdown</span></dt><dd>Stop the authoritative DNS server.
</dd></dl></div><p>
- </p></div><div class="section" title="Data Source Backends"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285888"></a>Data Source Backends</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+ </p></div><div class="section" title="Data Source Backends"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285881"></a>Data Source Backends</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
For the development prototype release, <span class="command"><strong>b10-auth</strong></span>
supports a SQLite3 data source backend and in-memory data source
backend.
@@ -532,7 +529,7 @@ This may be a temporary setting until then.
The default is <code class="filename">/usr/local/var/</code>.)
This data file location may be changed by defining the
<span class="quote">“<span class="quote">database_file</span>”</span> configuration.
- </p></div><div class="section" title="Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285918"></a>Loading Master Zones Files</h2></div></div></div><p>
+ </p></div><div class="section" title="Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285912"></a>Loading Master Zones Files</h2></div></div></div><p>
RFC 1035 style DNS master zone files may imported
into a BIND 10 data source by using the
<span class="command"><strong>b10-loadzone</strong></span> utility.
@@ -562,10 +559,9 @@ This may be a temporary setting until then.
all records from that prior zone disappear and a whole new set
appears.
</p></div></div><div class="chapter" title="Chapter 9. Incoming Zone Transfers"><div class="titlepage"><div><div><h2 class="title"><a name="xfrin"></a>Chapter 9. Incoming Zone Transfers</h2></div></div></div><p>
- The <span class="command"><strong>b10-xfrin</strong></span> process is started by
- <span class="command"><strong>bind10</strong></span>.
- It can be manually triggered to request an AXFR zone
- transfer. When received, it is stored in the BIND 10
+ Incoming zones are transferred using the <span class="command"><strong>b10-xfrin</strong></span>
+ process which is started by <span class="command"><strong>bind10</strong></span>.
+ When received, the zone is stored in the BIND 10
data store, and its records can be served by
<span class="command"><strong>b10-auth</strong></span>.
In combination with <span class="command"><strong>b10-zonemgr</strong></span> (for
@@ -574,6 +570,9 @@ This may be a temporary setting until then.
</p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
The current development release of BIND 10 only supports
AXFR. (IXFR is not supported.)
+
+
+
</p></div><p>
To manually trigger a zone transfer to retrieve a remote zone,
you may use the <span class="command"><strong>bindctl</strong></span> utility.
@@ -608,7 +607,7 @@ This may be a temporary setting until then.
</p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
Access control (such as allowing notifies) is not yet provided.
The primary/secondary service is not yet complete.
- </p></div></div><div class="chapter" title="Chapter 12. Recursive Name Server"><div class="titlepage"><div><div><h2 class="title"><a name="resolverserver"></a>Chapter 12. Recursive Name Server</h2></div></div></div><p>
+ </p></div></div><div class="chapter" title="Chapter 12. Recursive Name Server"><div class="titlepage"><div><div><h2 class="title"><a name="resolverserver"></a>Chapter 12. Recursive Name Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230286300">Forwarding</a></span></dt></dl></div><p>
The <span class="command"><strong>b10-resolver</strong></span> process is started by
<span class="command"><strong>bind10</strong></span>.
@@ -637,26 +636,27 @@ This may be a temporary setting until then.
> <strong class="userinput"><code>config set Resolver/listen_on [{ "address": "127.0.0.1", "port": 53 }]</code></strong>
> <strong class="userinput"><code>config commit</code></strong>
</pre><p>
- </p><p>
- To enable forwarding, the upstream address and port must be
- configured to forward queries to, such as:
+ </p><div class="section" title="Forwarding"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230286300"></a>Forwarding</h2></div></div></div><p>
- </p><pre class="screen">
+ To enable forwarding, the upstream address and port must be
+ configured to forward queries to, such as:
+
+ </p><pre class="screen">
> <strong class="userinput"><code>config set Resolver/forward_addresses [{ "address": "<em class="replaceable"><code>192.168.1.1</code></em>", "port": 53 }]</code></strong>
> <strong class="userinput"><code>config commit</code></strong>
</pre><p>
- (Replace <em class="replaceable"><code>192.168.1.1</code></em> to point to your
- full resolver.)
- </p><p>
- Normal iterative name service can be re-enabled by clearing the
- forwarding address(es); for example:
+ (Replace <em class="replaceable"><code>192.168.1.1</code></em> to point to your
+ full resolver.)
+ </p><p>
+ Normal iterative name service can be re-enabled by clearing the
+ forwarding address(es); for example:
- </p><pre class="screen">
+ </p><pre class="screen">
> <strong class="userinput"><code>config set Resolver/forward_addresses []</code></strong>
> <strong class="userinput"><code>config commit</code></strong>
</pre><p>
- </p></div><div class="chapter" title="Chapter 13. Statistics"><div class="titlepage"><div><div><h2 class="title"><a name="statistics"></a>Chapter 13. Statistics</h2></div></div></div><p>
+ </p></div></div><div class="chapter" title="Chapter 13. Statistics"><div class="titlepage"><div><div><h2 class="title"><a name="statistics"></a>Chapter 13. Statistics</h2></div></div></div><p>
The <span class="command"><strong>b10-stats</strong></span> process is started by
<span class="command"><strong>bind10</strong></span>.
It periodically collects statistics data from various modules
@@ -684,4 +684,48 @@ This may be a temporary setting until then.
"stats.timestamp": 1295543046.823504
}
</pre><p>
+ </p></div><div class="chapter" title="Chapter 14. Logging"><div class="titlepage"><div><div><h2 class="title"><a name="logging"></a>Chapter 14. Logging</h2></div></div></div><p>
+ Each message written by BIND 10 to the configured logging destinations
+ comprises a number of components that identify the origin of the
+ message and, if the message indicates a problem, information about the
+ problem that may be useful in fixing it.
+ </p><p>
+ Consider the message below logged to a file:
+ </p><pre class="screen">2011-06-15 13:48:22.034 ERROR [b10-resolver.asiolink]
+ ASIODNS_OPENSOCK error 111 opening TCP socket to 127.0.0.1(53)</pre><p>
+ </p><p>
+ Note: the layout of messages written to the system logging
+ file (syslog) may be slightly different. This message has
+ been split across two lines here for display reasons; in the
+ logging file, it will appear on one line.)
+ </p><p>
+ The log message comprises a number of components:
+
+ </p><div class="variablelist"><dl><dt><span class="term">2011-06-15 13:48:22.034</span></dt><dd><p>
+ The date and time at which the message was generated.
+ </p></dd><dt><span class="term">ERROR</span></dt><dd><p>
+ The severity of the message.
+ </p></dd><dt><span class="term">[b10-resolver.asiolink]</span></dt><dd><p>
+ The source of the message. This comprises two components:
+ the BIND 10 process generating the message (in this
+ case, <span class="command"><strong>b10-resolver</strong></span>) and the module
+ within the program from which the message originated
+ (which in the example is the asynchronous I/O link
+ module, asiolink).
+ </p></dd><dt><span class="term">ASIODNS_OPENSOCK</span></dt><dd><p>
+ The message identification. Every message in BIND 10
+ has a unique identification, which can be used as an
+ index into the <a class="ulink" href="bind10-messages.html" target="_top"><em class="citetitle">BIND 10 Messages
+ Manual</em></a> (<a class="ulink" href="http://bind10.isc.org/docs/bind10-messages.html" target="_top">http://bind10.isc.org/docs/bind10-messages.html</a>) from which more information can be obtained.
+ </p></dd><dt><span class="term">error 111 opening TCP socket to 127.0.0.1(53)</span></dt><dd><p>
+ A brief description of the cause of the problem. Within this text,
+ information relating to the condition that caused the message to
+ be logged will be included. In this example, error number 111
+ (an operating system-specific error number) was encountered when
+ trying to open a TCP connection to port 53 on the local system
+ (address 127.0.0.1). The next step would be to find out the reason
+ for the failure by consulting your system's documentation to
+ identify what error number 111 means.
+ </p></dd></dl></div><p>
+
</p></div></div></body></html>
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index bceb40c..6a42182 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -5,6 +5,23 @@
<!ENTITY % version SYSTEM "version.ent">
%version;
]>
+
+<!--
+ - Copyright (C) 2010-2011 Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
<book>
<?xml-stylesheet href="bind10-guide.css" type="text/css"?>
@@ -13,7 +30,7 @@
<subtitle>Administrator Reference for BIND 10</subtitle>
<copyright>
- <year>2010</year><holder>Internet Systems Consortium, Inc.</holder>
+ <year>2010-2011</year><holder>Internet Systems Consortium, Inc.</holder>
</copyright>
<abstract>
@@ -79,12 +96,22 @@
3.1 is the minimum version which will work.
</para>
- <note><para>
+ <para>
+ BIND 10 uses the Botan crypto library for C++. It requires
+ at least Botan version 1.8.
+ </para>
+
+ <para>
+ BIND 10 uses the log4cplus C++ logging library. It requires
+ at least log4cplus version 1.0.3.
+ </para>
+
+ <para>
The authoritative server requires SQLite 3.3.9 or newer.
The <command>b10-xfrin</command>, <command>b10-xfrout</command>,
and <command>b10-zonemgr</command> modules require the
libpython3 library and the Python _sqlite3.so module.
- </para></note>
+ </para>
<!-- TODO: this will change ... -->
<!-- TODO: list where to get these from -->
@@ -291,6 +318,12 @@ var/
<section>
<title>Building Requirements</title>
+
+ <para>
+ In addition to the run-time requirements, building BIND 10
+ from source code requires various development include headers.
+ </para>
+
<note>
<simpara>
Some operating systems have split their distribution packages into
@@ -308,6 +341,19 @@ var/
</para>
<para>
+ To build BIND 10, also install the Botan (at least version
+ 1.8) and the log4cplus (at least version 1.0.3)
+ development include headers.
+ </para>
+
+<!--
+TODO
+Debian and Ubuntu:
+ libgmp3-dev and libbz2-dev required for botan too
+-->
+
+ <para>
+<!-- TODO: is this needed at build time? test time? -->
The Python Library and Python _sqlite3 module are required to
enable the Xfrout and Xfrin support.
</para>
@@ -321,7 +367,7 @@ var/
Building BIND 10 also requires a C++ compiler and
standard development headers, make, and pkg-config.
BIND 10 builds have been tested with GCC g++ 3.4.3, 4.1.2,
- 4.1.3, 4.2.1, 4.3.2, and 4.4.1.
+ 4.1.3, 4.2.1, 4.3.2, and 4.4.1; Clang++ 2.8; and Sun C++ 5.10.
</para>
</section>
@@ -1199,10 +1245,9 @@ TODO
<title>Incoming Zone Transfers</title>
<para>
- The <command>b10-xfrin</command> process is started by
- <command>bind10</command>.
- It can be manually triggered to request an AXFR zone
- transfer. When received, it is stored in the BIND 10
+ Incoming zones are transferred using the <command>b10-xfrin</command>
+ process which is started by <command>bind10</command>.
+ When received, the zone is stored in the BIND 10
data store, and its records can be served by
<command>b10-auth</command>.
In combination with <command>b10-zonemgr</command> (for
@@ -1213,8 +1258,22 @@ TODO
<note><simpara>
The current development release of BIND 10 only supports
AXFR. (IXFR is not supported.)
+
+<!-- TODO: sqlite3 data source only? -->
+
</simpara></note>
+<!-- TODO:
+
+how to tell bind10 you are a secondary?
+
+when will it first attempt to check for new zone? (using REFRESH?)
+what if zonemgr is not running?
+
+what if a NOTIFY is sent?
+
+-->
+
<para>
To manually trigger a zone transfer to retrieve a remote zone,
you may use the <command>bindctl</command> utility.
@@ -1223,6 +1282,9 @@ TODO
<screen>> <userinput>Xfrin retransfer zone_name="<option>foo.example.org</option>" master=<option>192.0.2.99</option></userinput></screen>
</para>
+<!-- TODO: can that retransfer be used to identify a new zone? -->
+<!-- TODO: what if doesn't exist at that master IP? -->
+
</chapter>
<chapter id="xfrout">
@@ -1329,28 +1391,34 @@ what is XfroutClient xfr_client??
<!-- TODO: later the above will have some defaults -->
- <para>
- To enable forwarding, the upstream address and port must be
- configured to forward queries to, such as:
+ <section>
+ <title>Forwarding</title>
- <screen>
+ <para>
+
+ To enable forwarding, the upstream address and port must be
+ configured to forward queries to, such as:
+
+ <screen>
> <userinput>config set Resolver/forward_addresses [{ "address": "<replaceable>192.168.1.1</replaceable>", "port": 53 }]</userinput>
> <userinput>config commit</userinput>
</screen>
- (Replace <replaceable>192.168.1.1</replaceable> to point to your
- full resolver.)
- </para>
+ (Replace <replaceable>192.168.1.1</replaceable> to point to your
+ full resolver.)
+ </para>
- <para>
- Normal iterative name service can be re-enabled by clearing the
- forwarding address(es); for example:
+ <para>
+ Normal iterative name service can be re-enabled by clearing the
+ forwarding address(es); for example:
- <screen>
+ <screen>
> <userinput>config set Resolver/forward_addresses []</userinput>
> <userinput>config commit</userinput>
</screen>
- </para>
+ </para>
+
+ </section>
<!-- TODO: later try this
@@ -1399,6 +1467,92 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
</chapter>
+ <chapter id="logging">
+ <title>Logging</title>
+
+<!-- TODO: how to configure logging, logging destinations etc. -->
+
+ <para>
+ Each message written by BIND 10 to the configured logging destinations
+ comprises a number of components that identify the origin of the
+ message and, if the message indicates a problem, information about the
+ problem that may be useful in fixing it.
+ </para>
+
+ <para>
+ Consider the message below logged to a file:
+ <screen>2011-06-15 13:48:22.034 ERROR [b10-resolver.asiolink]
+ ASIODNS_OPENSOCK error 111 opening TCP socket to 127.0.0.1(53)</screen>
+ </para>
+
+ <para>
+ Note: the layout of messages written to the system logging
+ file (syslog) may be slightly different. This message has
+ been split across two lines here for display reasons; in the
+ logging file, it will appear on one line.)
+ </para>
+
+ <para>
+ The log message comprises a number of components:
+
+ <variablelist>
+ <varlistentry>
+ <term>2011-06-15 13:48:22.034</term>
+ <listitem><para>
+ The date and time at which the message was generated.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>ERROR</term>
+ <listitem><para>
+ The severity of the message.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>[b10-resolver.asiolink]</term>
+ <listitem><para>
+ The source of the message. This comprises two components:
+ the BIND 10 process generating the message (in this
+ case, <command>b10-resolver</command>) and the module
+ within the program from which the message originated
+ (which in the example is the asynchronous I/O link
+ module, asiolink).
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>ASIODNS_OPENSOCK</term>
+ <listitem><para>
+ The message identification. Every message in BIND 10
+ has a unique identification, which can be used as an
+ index into the <ulink
+ url="bind10-messages.html"><citetitle>BIND 10 Messages
+ Manual</citetitle></ulink> (<ulink
+ url="http://bind10.isc.org/docs/bind10-messages.html"
+ />) from which more information can be obtained.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>error 111 opening TCP socket to 127.0.0.1(53)</term>
+ <listitem><para>
+ A brief description of the cause of the problem. Within this text,
+ information relating to the condition that caused the message to
+ be logged will be included. In this example, error number 111
+ (an operating system-specific error number) was encountered when
+ trying to open a TCP connection to port 53 on the local system
+ (address 127.0.0.1). The next step would be to find out the reason
+ for the failure by consulting your system's documentation to
+ identify what error number 111 means.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+ </chapter>
+
<!-- TODO: how to help: run unit tests, join lists, review trac tickets -->
<!-- <index> <title>Index</title> </index> -->
diff --git a/doc/guide/bind10-messages.html b/doc/guide/bind10-messages.html
new file mode 100644
index 0000000..b075e96
--- /dev/null
+++ b/doc/guide/bind10-messages.html
@@ -0,0 +1,841 @@
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Messages Manual</title><link rel="stylesheet" href="./bind10-guide.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.75.2"><meta name="description" content="BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers. This is the messages manual for BIND 10 version 20110519. The most up-to-date version of this document, along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Messages Manual"><div class="titlepage"><div><div><h1 class="title"><a name="id1168230298903"></a>BIND 10 Messages Manual</h1></div><div><p class="releaseinfo">This is the messages manual for BIND 10 version
+ 20110519.</p></div><div><p class="copyright">Copyright © 2011 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>BIND 10 is a Domain Name System (DNS) suite managed by
+ Internet Systems Consortium (ISC). It includes DNS libraries
+ and modular components for controlling authoritative and
+ recursive DNS servers.
+ </p><p>
+ This is the messages manual for BIND 10 version 20110519.
+ The most up-to-date version of this document, along with
+ other documents for BIND 10, can be found at
+ <a class="ulink" href="http://bind10.isc.org/docs" target="_top">http://bind10.isc.org/docs</a>.
+ </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dt><span class="chapter"><a href="#messages">2. BIND 10 Messages</a></span></dt></dl></div><div class="chapter" title="Chapter 1. Introduction"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div></div><p>
+ This document lists each message that can be logged by the
+ programs in the BIND 10 package. Each entry in this manual
+ is of the form:
+ </p><pre class="screen">IDENTIFICATION message-text</pre><p>
+ ... where "IDENTIFICATION" is the message identification included
+ in each message logged and "message-text" is the accompanying
+ message text. The "message-text" may include placeholders of the
+ form "%1", "%2" etc.; these parameters are replaced by relevant
+ values when the message is logged.
+ </p><p>
+ Each entry is also accompanied by a description giving more
+ information about the circumstances that result in the message
+ being logged.
+ </p><p>
+ For information on configuring and using BIND 10 logging,
+ refer to the <a class="ulink" href="bind10-guide.html" target="_top">BIND 10 Guide</a>.
+ </p></div><div class="chapter" title="Chapter 2. BIND 10 Messages"><div class="titlepage"><div><div><h2 class="title"><a name="messages"></a>Chapter 2. BIND 10 Messages</h2></div></div></div><p>
+ </p><div class="variablelist"><dl><dt><a name="ASIODNS_FETCHCOMP"></a><span class="term">ASIODNS_FETCHCOMP upstream fetch to %1(%2) has now completed</span></dt><dd><p>
+A debug message, this records the the upstream fetch (a query made by the
+resolver on behalf of its client) to the specified address has completed.
+</p></dd><dt><a name="ASIODNS_FETCHSTOP"></a><span class="term">ASIODNS_FETCHSTOP upstream fetch to %1(%2) has been stopped</span></dt><dd><p>
+An external component has requested the halting of an upstream fetch. This
+is an allowed operation, and the message should only appear if debug is
+enabled.
+</p></dd><dt><a name="ASIODNS_OPENSOCK"></a><span class="term">ASIODNS_OPENSOCK error %1 opening %2 socket to %3(%4)</span></dt><dd><p>
+The asynchronous I/O code encountered an error when trying to open a socket
+of the specified protocol in order to send a message to the target address.
+The the number of the system error that cause the problem is given in the
+message.
+</p></dd><dt><a name="ASIODNS_RECVSOCK"></a><span class="term">ASIODNS_RECVSOCK error %1 reading %2 data from %3(%4)</span></dt><dd><p>
+The asynchronous I/O code encountered an error when trying read data from
+the specified address on the given protocol. The the number of the system
+error that cause the problem is given in the message.
+</p></dd><dt><a name="ASIODNS_RECVTMO"></a><span class="term">ASIODNS_RECVTMO receive timeout while waiting for data from %1(%2)</span></dt><dd><p>
+An upstream fetch from the specified address timed out. This may happen for
+any number of reasons and is most probably a problem at the remote server
+or a problem on the network. The message will only appear if debug is
+enabled.
+</p></dd><dt><a name="ASIODNS_SENDSOCK"></a><span class="term">ASIODNS_SENDSOCK error %1 sending data using %2 to %3(%4)</span></dt><dd><p>
+The asynchronous I/O code encountered an error when trying send data to
+the specified address on the given protocol. The the number of the system
+error that cause the problem is given in the message.
+</p></dd><dt><a name="ASIODNS_UNKORIGIN"></a><span class="term">ASIODNS_UNKORIGIN unknown origin for ASIO error code %1 (protocol: %2, address %3)</span></dt><dd><p>
+This message should not appear and indicates an internal error if it does.
+Please enter a bug report.
+</p></dd><dt><a name="ASIODNS_UNKRESULT"></a><span class="term">ASIODNS_UNKRESULT unknown result (%1) when IOFetch::stop() was executed for I/O to %2(%3)</span></dt><dd><p>
+The termination method of the resolver's upstream fetch class was called with
+an unknown result code (which is given in the message). This message should
+not appear and may indicate an internal error. Please enter a bug report.
+</p></dd><dt><a name="CONFIG_CCSESSION_MSG"></a><span class="term">CONFIG_CCSESSION_MSG error in CC session message: %1</span></dt><dd><p>
+There was a problem with an incoming message on the command and control
+channel. The message does not appear to be a valid command, and is
+missing a required element or contains an unknown data format. This
+most likely means that another BIND10 module is sending a bad message.
+The message itself is ignored by this module.
+</p></dd><dt><a name="CONFIG_CCSESSION_MSG_INTERNAL"></a><span class="term">CONFIG_CCSESSION_MSG_INTERNAL error handling CC session message: %1</span></dt><dd><p>
+There was an internal problem handling an incoming message on the
+command and control channel. An unexpected exception was thrown. This
+most likely points to an internal inconsistency in the module code. The
+exception message is appended to the log error, and the module will
+continue to run, but will not send back an answer.
+</p></dd><dt><a name="CONFIG_FOPEN_ERR"></a><span class="term">CONFIG_FOPEN_ERR error opening %1: %2</span></dt><dd><p>
+There was an error opening the given file.
+</p></dd><dt><a name="CONFIG_JSON_PARSE"></a><span class="term">CONFIG_JSON_PARSE JSON parse error in %1: %2</span></dt><dd><p>
+There was a parse error in the JSON file. The given file does not appear
+to be in valid JSON format. Please verify that the filename is correct
+and that the contents are valid JSON.
+</p></dd><dt><a name="CONFIG_MANAGER_CONFIG"></a><span class="term">CONFIG_MANAGER_CONFIG error getting configuration from cfgmgr: %1</span></dt><dd><p>
+The configuration manager returned an error when this module requested
+the configuration. The full error message answer from the configuration
+manager is appended to the log error. The most likely cause is that
+the module is of a different (command specification) version than the
+running configuration manager.
+</p></dd><dt><a name="CONFIG_MANAGER_MOD_SPEC"></a><span class="term">CONFIG_MANAGER_MOD_SPEC module specification not accepted by cfgmgr: %1</span></dt><dd><p>
+The module specification file for this module was rejected by the
+configuration manager. The full error message answer from the
+configuration manager is appended to the log error. The most likely
+cause is that the module is of a different (specification file) version
+than the running configuration manager.
+</p></dd><dt><a name="CONFIG_MODULE_SPEC"></a><span class="term">CONFIG_MODULE_SPEC module specification error in %1: %2</span></dt><dd><p>
+The given file does not appear to be a valid specification file. Please
+verify that the filename is correct and that its contents are a valid
+BIND10 module specification.
+</p></dd><dt><a name="DATASRC_CACHE_CREATE"></a><span class="term">DATASRC_CACHE_CREATE creating the hotspot cache</span></dt><dd><p>
+Debug information that the hotspot cache was created at startup.
+</p></dd><dt><a name="DATASRC_CACHE_DESTROY"></a><span class="term">DATASRC_CACHE_DESTROY destroying the hotspot cache</span></dt><dd><p>
+Debug information. The hotspot cache is being destroyed.
+</p></dd><dt><a name="DATASRC_CACHE_DISABLE"></a><span class="term">DATASRC_CACHE_DISABLE disabling the cache</span></dt><dd><p>
+The hotspot cache is disabled from now on. It is not going to store
+information or return anything.
+</p></dd><dt><a name="DATASRC_CACHE_ENABLE"></a><span class="term">DATASRC_CACHE_ENABLE enabling the cache</span></dt><dd><p>
+The hotspot cache is enabled from now on.
+</p></dd><dt><a name="DATASRC_CACHE_EXPIRED"></a><span class="term">DATASRC_CACHE_EXPIRED the item '%1' is expired</span></dt><dd><p>
+Debug information. There was an attempt to look up an item in the hotspot
+cache. And the item was actually there, but it was too old, so it was removed
+instead and nothing is reported (the external behaviour is the same as with
+CACHE_NOT_FOUND).
+</p></dd><dt><a name="DATASRC_CACHE_FOUND"></a><span class="term">DATASRC_CACHE_FOUND the item '%1' was found</span></dt><dd><p>
+Debug information. An item was successfully looked up in the hotspot cache.
+</p></dd><dt><a name="DATASRC_CACHE_FULL"></a><span class="term">DATASRC_CACHE_FULL cache is full, dropping oldest</span></dt><dd><p>
+Debug information. After inserting an item into the hotspot cache, the
+maximum number of items was exceeded, so the least recently used item will
+be dropped. This should be directly followed by CACHE_REMOVE.
+</p></dd><dt><a name="DATASRC_CACHE_INSERT"></a><span class="term">DATASRC_CACHE_INSERT inserting item '%1' into the cache</span></dt><dd><p>
+Debug information. It means a new item is being inserted into the hotspot
+cache.
+</p></dd><dt><a name="DATASRC_CACHE_NOT_FOUND"></a><span class="term">DATASRC_CACHE_NOT_FOUND the item '%1' was not found</span></dt><dd><p>
+Debug information. It was attempted to look up an item in the hotspot cache,
+but it is not there.
+</p></dd><dt><a name="DATASRC_CACHE_OLD_FOUND"></a><span class="term">DATASRC_CACHE_OLD_FOUND older instance of cache item found, replacing</span></dt><dd><p>
+Debug information. While inserting an item into the hotspot cache, an older
+instance of an item with the same name was found. The old instance will be
+removed. This should be directly followed by CACHE_REMOVE.
+</p></dd><dt><a name="DATASRC_CACHE_REMOVE"></a><span class="term">DATASRC_CACHE_REMOVE removing '%1' from the cache</span></dt><dd><p>
+Debug information. An item is being removed from the hotspot cache.
+</p></dd><dt><a name="DATASRC_CACHE_SLOTS"></a><span class="term">DATASRC_CACHE_SLOTS setting the cache size to '%1', dropping '%2' items</span></dt><dd><p>
+The maximum allowed number of items of the hotspot cache is set to the given
+number. If there are too many, some of them will be dropped. The size of 0
+means no limit.
+</p></dd><dt><a name="DATASRC_DO_QUERY"></a><span class="term">DATASRC_DO_QUERY handling query for '%1/%2'</span></dt><dd><p>
+Debug information. We're processing some internal query for given name and
+type.
+</p></dd><dt><a name="DATASRC_MEM_ADD_RRSET"></a><span class="term">DATASRC_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3'</span></dt><dd><p>
+Debug information. An RRset is being added to the in-memory data source.
+</p></dd><dt><a name="DATASRC_MEM_ADD_WILDCARD"></a><span class="term">DATASRC_MEM_ADD_WILDCARD adding wildcards for '%1'</span></dt><dd><p>
+Debug information. Some special marks above each * in wildcard name are needed.
+They are being added now for this name.
+</p></dd><dt><a name="DATASRC_MEM_ADD_ZONE"></a><span class="term">DATASRC_MEM_ADD_ZONE adding zone '%1/%2'</span></dt><dd><p>
+Debug information. A zone is being added into the in-memory data source.
+</p></dd><dt><a name="DATASRC_MEM_ANY_SUCCESS"></a><span class="term">DATASRC_MEM_ANY_SUCCESS ANY query for '%1' successful</span></dt><dd><p>
+Debug information. The domain was found and an ANY type query is being answered
+by providing everything found inside the domain.
+</p></dd><dt><a name="DATASRC_MEM_CNAME"></a><span class="term">DATASRC_MEM_CNAME CNAME at the domain '%1'</span></dt><dd><p>
+Debug information. The requested domain is an alias to a different domain,
+returning the CNAME instead.
+</p></dd><dt><a name="DATASRC_MEM_CNAME_COEXIST"></a><span class="term">DATASRC_MEM_CNAME_COEXIST can't add data to CNAME in domain '%1'</span></dt><dd><p>
+This is the same problem as in MEM_CNAME_TO_NONEMPTY, but it happened the
+other way around -- adding some outher data to CNAME.
+</p></dd><dt><a name="DATASRC_MEM_CNAME_TO_NONEMPTY"></a><span class="term">DATASRC_MEM_CNAME_TO_NONEMPTY can't add CNAME to domain with other data in '%1'</span></dt><dd><p>
+Someone or something tried to add a CNAME into a domain that already contains
+some other data. But the protocol forbids coexistence of CNAME with anything
+(RFC 1034, section 3.6.2). This indicates a problem with provided data.
+</p></dd><dt><a name="DATASRC_MEM_CREATE"></a><span class="term">DATASRC_MEM_CREATE creating zone '%1' in '%2' class</span></dt><dd><p>
+Debug information. A representation of a zone for the in-memory data source is
+being created.
+</p></dd><dt><a name="DATASRC_MEM_DELEG_FOUND"></a><span class="term">DATASRC_MEM_DELEG_FOUND delegation found at '%1'</span></dt><dd><p>
+Debug information. A delegation point was found above the requested record.
+</p></dd><dt><a name="DATASRC_MEM_DESTROY"></a><span class="term">DATASRC_MEM_DESTROY destroying zone '%1' in '%2' class</span></dt><dd><p>
+Debug information. A zone from in-memory data source is being destroyed.
+</p></dd><dt><a name="DATASRC_MEM_DNAME_ENCOUNTERED"></a><span class="term">DATASRC_MEM_DNAME_ENCOUNTERED encountered a DNAME</span></dt><dd><p>
+Debug information. While searching for the requested domain, a DNAME was
+encountered on the way. This may lead to redirection to a different domain and
+stop the search.
+</p></dd><dt><a name="DATASRC_MEM_DNAME_FOUND"></a><span class="term">DATASRC_MEM_DNAME_FOUND DNAME found at '%1'</span></dt><dd><p>
+Debug information. A DNAME was found instead of the requested information.
+</p></dd><dt><a name="DATASRC_MEM_DNAME_NS"></a><span class="term">DATASRC_MEM_DNAME_NS dNAME and NS can't coexist in non-apex domain '%1'</span></dt><dd><p>
+It was requested for DNAME and NS records to be put into the same domain
+which is not the apex (the top of the zone). This is forbidden by RFC
+2672, section 3. This indicates a problem with provided data.
+</p></dd><dt><a name="DATASRC_MEM_DOMAIN_EMPTY"></a><span class="term">DATASRC_MEM_DOMAIN_EMPTY requested domain '%1' is empty</span></dt><dd><p>
+Debug information. The requested domain exists in the tree of domains, but
+it is empty. Therefore it doesn't contain the requested resource type.
+</p></dd><dt><a name="DATASRC_MEM_DUP_RRSET"></a><span class="term">DATASRC_MEM_DUP_RRSET duplicate RRset '%1/%2'</span></dt><dd><p>
+An RRset is being inserted into in-memory data source for a second time. The
+original version must be removed first. Note that loading master files where an
+RRset is split into multiple locations is not supported yet.
+</p></dd><dt><a name="DATASRC_MEM_EXACT_DELEGATION"></a><span class="term">DATASRC_MEM_EXACT_DELEGATION delegation at the exact domain '%1'</span></dt><dd><p>
+Debug information. There's a NS record at the requested domain. This means
+this zone is not authoritative for the requested domain, but a delegation
+should be followed. The requested domain is an apex of some zone.
+</p></dd><dt><a name="DATASRC_MEM_FIND"></a><span class="term">DATASRC_MEM_FIND find '%1/%2'</span></dt><dd><p>
+Debug information. A search for the requested RRset is being started.
+</p></dd><dt><a name="DATASRC_MEM_FIND_ZONE"></a><span class="term">DATASRC_MEM_FIND_ZONE looking for zone '%1'</span></dt><dd><p>
+Debug information. A zone object for this zone is being searched for in the
+in-memory data source.
+</p></dd><dt><a name="DATASRC_MEM_LOAD"></a><span class="term">DATASRC_MEM_LOAD loading zone '%1' from file '%2'</span></dt><dd><p>
+Debug information. The content of master file is being loaded into the memory.
+</p></dd><dt><a name="DATASRC_MEM_NOTFOUND"></a><span class="term">DATASRC_MEM_NOTFOUND requested domain '%1' not found</span></dt><dd><p>
+Debug information. The requested domain does not exist.
+</p></dd><dt><a name="DATASRC_MEM_NS_ENCOUNTERED"></a><span class="term">DATASRC_MEM_NS_ENCOUNTERED encountered a NS</span></dt><dd><p>
+Debug information. While searching for the requested domain, a NS was
+encountered on the way (a delegation). This may lead to stop of the search.
+</p></dd><dt><a name="DATASRC_MEM_NXRRSET"></a><span class="term">DATASRC_MEM_NXRRSET no such type '%1' at '%2'</span></dt><dd><p>
+Debug information. The domain exists, but it doesn't hold any record of the
+requested type.
+</p></dd><dt><a name="DATASRC_MEM_OUT_OF_ZONE"></a><span class="term">DATASRC_MEM_OUT_OF_ZONE domain '%1' doesn't belong to zone '%2'</span></dt><dd><p>
+It was attempted to add the domain into a zone that shouldn't have it
+(eg. the domain is not subdomain of the zone origin). This indicates a
+problem with provided data.
+</p></dd><dt><a name="DATASRC_MEM_RENAME"></a><span class="term">DATASRC_MEM_RENAME renaming RRset from '%1' to '%2'</span></dt><dd><p>
+Debug information. A RRset is being generated from a different RRset (most
+probably a wildcard). So it must be renamed to whatever the user asked for. In
+fact, it's impossible to rename RRsets with our libraries, so a new one is
+created and all resource records are copied over.
+</p></dd><dt><a name="DATASRC_MEM_SINGLETON"></a><span class="term">DATASRC_MEM_SINGLETON trying to add multiple RRs for domain '%1' and type '%2'</span></dt><dd><p>
+Some resource types are singletons -- only one is allowed in a domain
+(for example CNAME or SOA). This indicates a problem with provided data.
+</p></dd><dt><a name="DATASRC_MEM_SUCCESS"></a><span class="term">DATASRC_MEM_SUCCESS query for '%1/%2' successful</span></dt><dd><p>
+Debug information. The requested record was found.
+</p></dd><dt><a name="DATASRC_MEM_SUPER_STOP"></a><span class="term">DATASRC_MEM_SUPER_STOP stopped at superdomain '%1', domain '%2' is empty</span></dt><dd><p>
+Debug information. The search stopped at a superdomain of the requested
+domain. The domain is a empty nonterminal, therefore it is treated as NXRRSET
+case (eg. the domain exists, but it doesn't have the requested record type).
+</p></dd><dt><a name="DATASRC_MEM_SWAP"></a><span class="term">DATASRC_MEM_SWAP swapping contents of two zone representations ('%1' and '%2')</span></dt><dd><p>
+Debug information. The contents of two in-memory zones are being exchanged.
+This is usual practice to do some manipulation in exception-safe manner -- the
+new data are prepared in a different zone object and when it works, they are
+swapped. The old one contains the new data and the other one can be safely
+destroyed.
+</p></dd><dt><a name="DATASRC_MEM_WILDCARD_CANCEL"></a><span class="term">DATASRC_MEM_WILDCARD_CANCEL wildcard match canceled for '%1'</span></dt><dd><p>
+Debug information. A domain above wildcard was reached, but there's something
+below the requested domain. Therefore the wildcard doesn't apply here. This
+behaviour is specified by RFC 1034, section 4.3.3
+</p></dd><dt><a name="DATASRC_MEM_WILDCARD_DNAME"></a><span class="term">DATASRC_MEM_WILDCARD_DNAME dNAME record in wildcard domain '%1'</span></dt><dd><p>
+The software refuses to load DNAME records into a wildcard domain. It isn't
+explicitly forbidden, but the protocol is ambiguous about how this should
+behave and BIND 9 refuses that as well. Please describe your intention using
+different tools.
+</p></dd><dt><a name="DATASRC_MEM_WILDCARD_NS"></a><span class="term">DATASRC_MEM_WILDCARD_NS nS record in wildcard domain '%1'</span></dt><dd><p>
+The software refuses to load NS records into a wildcard domain. It isn't
+explicitly forbidden, but the protocol is ambiguous about how this should
+behave and BIND 9 refuses that as well. Please describe your intention using
+different tools.
+</p></dd><dt><a name="DATASRC_META_ADD"></a><span class="term">DATASRC_META_ADD adding a data source into meta data source</span></dt><dd><p>
+Debug information. Yet another data source is being added into the meta data
+source. (probably at startup or reconfiguration)
+</p></dd><dt><a name="DATASRC_META_ADD_CLASS_MISMATCH"></a><span class="term">DATASRC_META_ADD_CLASS_MISMATCH mismatch between classes '%1' and '%2'</span></dt><dd><p>
+It was attempted to add a data source into a meta data source. But their
+classes do not match.
+</p></dd><dt><a name="DATASRC_META_REMOVE"></a><span class="term">DATASRC_META_REMOVE removing data source from meta data source</span></dt><dd><p>
+Debug information. A data source is being removed from meta data source.
+</p></dd><dt><a name="DATASRC_QUERY_ADD_NSEC"></a><span class="term">DATASRC_QUERY_ADD_NSEC adding NSEC record for '%1'</span></dt><dd><p>
+Debug information. A NSEC record covering this zone is being added.
+</p></dd><dt><a name="DATASRC_QUERY_ADD_NSEC3"></a><span class="term">DATASRC_QUERY_ADD_NSEC3 adding NSEC3 record of zone '%1'</span></dt><dd><p>
+Debug information. A NSEC3 record for the given zone is being added to the
+response message.
+</p></dd><dt><a name="DATASRC_QUERY_ADD_RRSET"></a><span class="term">DATASRC_QUERY_ADD_RRSET adding RRset '%1/%2' to message</span></dt><dd><p>
+Debug information. An RRset is being added to the response message.
+</p></dd><dt><a name="DATASRC_QUERY_ADD_SOA"></a><span class="term">DATASRC_QUERY_ADD_SOA adding SOA of '%1'</span></dt><dd><p>
+Debug information. A SOA record of the given zone is being added to the
+authority section of the response message.
+</p></dd><dt><a name="DATASRC_QUERY_AUTH_FAIL"></a><span class="term">DATASRC_QUERY_AUTH_FAIL the underlying data source failed with %1</span></dt><dd><p>
+The underlying data source failed to answer the authoritative query. 1 means
+some error, 2 is not implemented. The data source should have logged the
+specific error already.
+</p></dd><dt><a name="DATASRC_QUERY_BAD_REFERRAL"></a><span class="term">DATASRC_QUERY_BAD_REFERRAL bad referral to '%1'</span></dt><dd><p>
+The domain lives in another zone. But it is not possible to generate referral
+information for it.
+</p></dd><dt><a name="DATASRC_QUERY_CACHED"></a><span class="term">DATASRC_QUERY_CACHED data for %1/%2 found in cache</span></dt><dd><p>
+Debug information. The requested data were found in the hotspot cache, so
+no query is sent to the real data source.
+</p></dd><dt><a name="DATASRC_QUERY_CHECK_CACHE"></a><span class="term">DATASRC_QUERY_CHECK_CACHE checking cache for '%1/%2'</span></dt><dd><p>
+Debug information. While processing a query, lookup to the hotspot cache
+is being made.
+</p></dd><dt><a name="DATASRC_QUERY_COPY_AUTH"></a><span class="term">DATASRC_QUERY_COPY_AUTH copying authoritative section into message</span></dt><dd><p>
+Debug information. The whole referral information is being copied into the
+response message.
+</p></dd><dt><a name="DATASRC_QUERY_DELEGATION"></a><span class="term">DATASRC_QUERY_DELEGATION looking for delegation on the path to '%1'</span></dt><dd><p>
+Debug information. The software is trying to identify delegation points on the
+way down to the given domain.
+</p></dd><dt><a name="DATASRC_QUERY_EMPTY_CNAME"></a><span class="term">DATASRC_QUERY_EMPTY_CNAME cNAME at '%1' is empty</span></dt><dd><p>
+There was an CNAME and it was being followed. But it contains no records,
+so there's nowhere to go. There will be no answer. This indicates a problem
+with supplied data.
+We tried to follow
+</p></dd><dt><a name="DATASRC_QUERY_EMPTY_DNAME"></a><span class="term">DATASRC_QUERY_EMPTY_DNAME the DNAME on '%1' is empty</span></dt><dd><p>
+During an attempt to synthesize CNAME from this DNAME it was discovered the
+DNAME is empty (it has no records). This indicates problem with supplied data.
+</p></dd><dt><a name="DATASRC_QUERY_FAIL"></a><span class="term">DATASRC_QUERY_FAIL query failed</span></dt><dd><p>
+Some subtask of query processing failed. The reason should have been reported
+already. We are returning SERVFAIL.
+</p></dd><dt><a name="DATASRC_QUERY_FOLLOW_CNAME"></a><span class="term">DATASRC_QUERY_FOLLOW_CNAME following CNAME at '%1'</span></dt><dd><p>
+Debug information. The domain is a CNAME (or a DNAME and we created a CNAME
+for it already), so it's being followed.
+</p></dd><dt><a name="DATASRC_QUERY_GET_MX_ADDITIONAL"></a><span class="term">DATASRC_QUERY_GET_MX_ADDITIONAL addition of A/AAAA for '%1' requested by MX '%2'</span></dt><dd><p>
+Debug information. While processing a query, a MX record was met. It
+references the mentioned address, so A/AAAA records for it are looked up
+and put it into the additional section.
+</p></dd><dt><a name="DATASRC_QUERY_GET_NS_ADDITIONAL"></a><span class="term">DATASRC_QUERY_GET_NS_ADDITIONAL addition of A/AAAA for '%1' requested by NS '%2'</span></dt><dd><p>
+Debug information. While processing a query, a NS record was met. It
+references the mentioned address, so A/AAAA records for it are looked up
+and put it into the additional section.
+</p></dd><dt><a name="DATASRC_QUERY_GLUE_FAIL"></a><span class="term">DATASRC_QUERY_GLUE_FAIL the underlying data source failed with %1</span></dt><dd><p>
+The underlying data source failed to answer the glue query. 1 means some error,
+2 is not implemented. The data source should have logged the specific error
+already.
+</p></dd><dt><a name="DATASRC_QUERY_INVALID_OP"></a><span class="term">DATASRC_QUERY_INVALID_OP invalid query operation requested</span></dt><dd><p>
+This indicates a programmer error. The DO_QUERY was called with unknown
+operation code.
+</p></dd><dt><a name="DATASRC_QUERY_IS_AUTH"></a><span class="term">DATASRC_QUERY_IS_AUTH auth query (%1/%2)</span></dt><dd><p>
+Debug information. The last DO_QUERY is an auth query.
+</p></dd><dt><a name="DATASRC_QUERY_IS_GLUE"></a><span class="term">DATASRC_QUERY_IS_GLUE glue query (%1/%2)</span></dt><dd><p>
+Debug information. The last DO_QUERY is query for glue addresses.
+</p></dd><dt><a name="DATASRC_QUERY_IS_NOGLUE"></a><span class="term">DATASRC_QUERY_IS_NOGLUE query for non-glue addresses (%1/%2)</span></dt><dd><p>
+Debug information. The last DO_QUERY is query for addresses that are not
+glue.
+</p></dd><dt><a name="DATASRC_QUERY_IS_REF"></a><span class="term">DATASRC_QUERY_IS_REF query for referral (%1/%2)</span></dt><dd><p>
+Debug information. The last DO_QUERY is query for referral information.
+</p></dd><dt><a name="DATASRC_QUERY_IS_SIMPLE"></a><span class="term">DATASRC_QUERY_IS_SIMPLE simple query (%1/%2)</span></dt><dd><p>
+Debug information. The last DO_QUERY is a simple query.
+</p></dd><dt><a name="DATASRC_QUERY_MISPLACED_TASK"></a><span class="term">DATASRC_QUERY_MISPLACED_TASK task of this type should not be here</span></dt><dd><p>
+This indicates a programming error. A task was found in the internal task
+queue, but this kind of task wasn't designed to be inside the queue (it should
+be handled right away, not queued).
+</p></dd><dt><a name="DATASRC_QUERY_MISSING_NS"></a><span class="term">DATASRC_QUERY_MISSING_NS missing NS records for '%1'</span></dt><dd><p>
+NS records should have been put into the authority section. However, this zone
+has none. This indicates problem with provided data.
+</p></dd><dt><a name="DATASRC_QUERY_MISSING_SOA"></a><span class="term">DATASRC_QUERY_MISSING_SOA the zone '%1' has no SOA</span></dt><dd><p>
+The answer should have been a negative one (eg. of nonexistence of something).
+To do so, a SOA record should be put into the authority section, but the zone
+does not have one. This indicates problem with provided data.
+</p></dd><dt><a name="DATASRC_QUERY_NOGLUE_FAIL"></a><span class="term">DATASRC_QUERY_NOGLUE_FAIL the underlying data source failed with %1</span></dt><dd><p>
+The underlying data source failed to answer the no-glue query. 1 means some
+error, 2 is not implemented. The data source should have logged the specific
+error already.
+</p></dd><dt><a name="DATASRC_QUERY_NO_CACHE_ANY_AUTH"></a><span class="term">DATASRC_QUERY_NO_CACHE_ANY_AUTH ignoring cache for ANY query (%1/%2 in %3 class)</span></dt><dd><p>
+Debug information. The hotspot cache is ignored for authoritative ANY queries
+for consistency reasons.
+</p></dd><dt><a name="DATASRC_QUERY_NO_CACHE_ANY_SIMPLE"></a><span class="term">DATASRC_QUERY_NO_CACHE_ANY_SIMPLE ignoring cache for ANY query (%1/%2 in %3 class)</span></dt><dd><p>
+Debug information. The hotspot cache is ignored for ANY queries for consistency
+reasons.
+</p></dd><dt><a name="DATASRC_QUERY_NO_DS_NSEC"></a><span class="term">DATASRC_QUERY_NO_DS_NSEC there's no DS record in the '%1' zone</span></dt><dd><p>
+An attempt to add a NSEC record into the message failed, because the zone does
+not have any DS record. This indicates problem with the provided data.
+</p></dd><dt><a name="DATASRC_QUERY_NO_DS_NSEC3"></a><span class="term">DATASRC_QUERY_NO_DS_NSEC3 there's no DS record in the '%1' zone</span></dt><dd><p>
+An attempt to add a NSEC3 record into the message failed, because the zone does
+not have any DS record. This indicates problem with the provided data.
+</p></dd><dt><a name="DATASRC_QUERY_NO_ZONE"></a><span class="term">DATASRC_QUERY_NO_ZONE no zone containing '%1' in class '%2'</span></dt><dd><p>
+Lookup of domain failed because the data have no zone that contain the
+domain. Maybe someone sent a query to the wrong server for some reason.
+</p></dd><dt><a name="DATASRC_QUERY_PROCESS"></a><span class="term">DATASRC_QUERY_PROCESS processing query '%1/%2' in the '%3' class</span></dt><dd><p>
+Debug information. A sure query is being processed now.
+</p></dd><dt><a name="DATASRC_QUERY_PROVENX_FAIL"></a><span class="term">DATASRC_QUERY_PROVENX_FAIL unable to prove nonexistence of '%1'</span></dt><dd><p>
+The user wants DNSSEC and we discovered the entity doesn't exist (either
+domain or the record). But there was an error getting NSEC/NSEC3 record
+to prove the nonexistence.
+</p></dd><dt><a name="DATASRC_QUERY_REF_FAIL"></a><span class="term">DATASRC_QUERY_REF_FAIL the underlying data source failed with %1</span></dt><dd><p>
+The underlying data source failed to answer the query for referral information.
+1 means some error, 2 is not implemented. The data source should have logged
+the specific error already.
+</p></dd><dt><a name="DATASRC_QUERY_RRSIG"></a><span class="term">DATASRC_QUERY_RRSIG unable to answer RRSIG query</span></dt><dd><p>
+The server is unable to answer a direct query for RRSIG type, but was asked
+to do so.
+</p></dd><dt><a name="DATASRC_QUERY_SIMPLE_FAIL"></a><span class="term">DATASRC_QUERY_SIMPLE_FAIL the underlying data source failed with %1</span></dt><dd><p>
+The underlying data source failed to answer the simple query. 1 means some
+error, 2 is not implemented. The data source should have logged the specific
+error already.
+</p></dd><dt><a name="DATASRC_QUERY_SYNTH_CNAME"></a><span class="term">DATASRC_QUERY_SYNTH_CNAME synthesizing CNAME from DNAME on '%1'</span></dt><dd><p>
+Debug information. While answering a query, a DNAME was met. The DNAME itself
+will be returned, but along with it a CNAME for clients which don't understand
+DNAMEs will be synthesized.
+</p></dd><dt><a name="DATASRC_QUERY_TASK_FAIL"></a><span class="term">DATASRC_QUERY_TASK_FAIL task failed with %1</span></dt><dd><p>
+The query subtask failed. The reason should have been reported by the subtask
+already. The code is 1 for error, 2 for not implemented.
+</p></dd><dt><a name="DATASRC_QUERY_TOO_MANY_CNAMES"></a><span class="term">DATASRC_QUERY_TOO_MANY_CNAMES cNAME chain limit exceeded at '%1'</span></dt><dd><p>
+A CNAME led to another CNAME and it led to another, and so on. After 16
+CNAMEs, the software gave up. Long CNAME chains are discouraged, and this
+might possibly be a loop as well. Note that some of the CNAMEs might have
+been synthesized from DNAMEs. This indicates problem with supplied data.
+</p></dd><dt><a name="DATASRC_QUERY_UNKNOWN_RESULT"></a><span class="term">DATASRC_QUERY_UNKNOWN_RESULT unknown result of subtask</span></dt><dd><p>
+This indicates a programmer error. The answer of subtask doesn't look like
+anything known.
+</p></dd><dt><a name="DATASRC_QUERY_WILDCARD"></a><span class="term">DATASRC_QUERY_WILDCARD looking for a wildcard covering '%1'</span></dt><dd><p>
+Debug information. A direct match wasn't found, so a wildcard covering the
+domain is being looked for now.
+</p></dd><dt><a name="DATASRC_QUERY_WILDCARD_FAIL"></a><span class="term">DATASRC_QUERY_WILDCARD_FAIL error processing wildcard for '%1'</span></dt><dd><p>
+During an attempt to cover the domain by a wildcard an error happened. The
+exact kind was hopefully already reported.
+</p></dd><dt><a name="DATASRC_QUERY_WILDCARD_PROVENX_FAIL"></a><span class="term">DATASRC_QUERY_WILDCARD_PROVENX_FAIL unable to prove nonexistence of '%1' (%2)</span></dt><dd><p>
+While processing a wildcard, it wasn't possible to prove nonexistence of the
+given domain or record. The code is 1 for error and 2 for not implemented.
+</p></dd><dt><a name="DATASRC_QUERY_WILDCARD_REFERRAL"></a><span class="term">DATASRC_QUERY_WILDCARD_REFERRAL unable to find referral info for '%1' (%2)</span></dt><dd><p>
+While processing a wildcard, a referral was met. But it wasn't possible to get
+enough information for it. The code is 1 for error, 2 for not implemented.
+</p></dd><dt><a name="DATASRC_SQLITE_CLOSE"></a><span class="term">DATASRC_SQLITE_CLOSE closing SQLite database</span></dt><dd><p>
+Debug information. The SQLite data source is closing the database file.
+</p></dd><dt><a name="DATASRC_SQLITE_CREATE"></a><span class="term">DATASRC_SQLITE_CREATE sQLite data source created</span></dt><dd><p>
+Debug information. An instance of SQLite data source is being created.
+</p></dd><dt><a name="DATASRC_SQLITE_DESTROY"></a><span class="term">DATASRC_SQLITE_DESTROY sQLite data source destroyed</span></dt><dd><p>
+Debug information. An instance of SQLite data source is being destroyed.
+</p></dd><dt><a name="DATASRC_SQLITE_ENCLOSURE"></a><span class="term">DATASRC_SQLITE_ENCLOSURE looking for zone containing '%1'</span></dt><dd><p>
+Debug information. The SQLite data source is trying to identify, which zone
+should hold this domain.
+</p></dd><dt><a name="DATASRC_SQLITE_ENCLOSURE_NOTFOUND"></a><span class="term">DATASRC_SQLITE_ENCLOSURE_NOTFOUND no zone contains it</span></dt><dd><p>
+Debug information. The last SQLITE_ENCLOSURE query was unsuccessful, there's
+no such zone in our data.
+</p></dd><dt><a name="DATASRC_SQLITE_FIND"></a><span class="term">DATASRC_SQLITE_FIND looking for RRset '%1/%2'</span></dt><dd><p>
+Debug information. The SQLite data source is looking up a resource record
+set.
+</p></dd><dt><a name="DATASRC_SQLITE_FINDADDRS"></a><span class="term">DATASRC_SQLITE_FINDADDRS looking for A/AAAA addresses for '%1'</span></dt><dd><p>
+Debug information. The data source is looking up the addresses for given
+domain name.
+</p></dd><dt><a name="DATASRC_SQLITE_FINDADDRS_BAD_CLASS"></a><span class="term">DATASRC_SQLITE_FINDADDRS_BAD_CLASS class mismatch looking for addresses ('%1' and '%2')</span></dt><dd><p>
+The SQLite data source was looking up A/AAAA addresses, but the data source
+contains different class than the query was for.
+</p></dd><dt><a name="DATASRC_SQLITE_FINDEXACT"></a><span class="term">DATASRC_SQLITE_FINDEXACT looking for exact RRset '%1/%2'</span></dt><dd><p>
+Debug information. The SQLite data source is looking up an exact resource
+record.
+</p></dd><dt><a name="DATASRC_SQLITE_FINDEXACT_BAD_CLASS"></a><span class="term">DATASRC_SQLITE_FINDEXACT_BAD_CLASS class mismatch looking for an RRset ('%1' and '%2')</span></dt><dd><p>
+The SQLite data source was looking up an exact RRset, but the data source
+contains different class than the query was for.
+</p></dd><dt><a name="DATASRC_SQLITE_FINDREC"></a><span class="term">DATASRC_SQLITE_FINDREC looking for record '%1/%2'</span></dt><dd><p>
+Debug information. The SQLite data source is looking up records of given name
+and type in the database.
+</p></dd><dt><a name="DATASRC_SQLITE_FINDREF"></a><span class="term">DATASRC_SQLITE_FINDREF looking for referral at '%1'</span></dt><dd><p>
+Debug information. The SQLite data source is identifying if this domain is
+a referral and where it goes.
+</p></dd><dt><a name="DATASRC_SQLITE_FINDREF_BAD_CLASS"></a><span class="term">DATASRC_SQLITE_FINDREF_BAD_CLASS class mismatch looking for referral ('%1' and '%2')</span></dt><dd><p>
+The SQLite data source was trying to identify, if there's a referral. But
+it contains different class than the query was for.
+</p></dd><dt><a name="DATASRC_SQLITE_FIND_BAD_CLASS"></a><span class="term">DATASRC_SQLITE_FIND_BAD_CLASS class mismatch looking for an RRset ('%1' and '%2')</span></dt><dd><p>
+The SQLite data source was looking up an RRset, but the data source contains
+different class than the query was for.
+</p></dd><dt><a name="DATASRC_SQLITE_FIND_NSEC3"></a><span class="term">DATASRC_SQLITE_FIND_NSEC3 looking for NSEC3 in zone '%1' for hash '%2'</span></dt><dd><p>
+Debug information. We're trying to look up a NSEC3 record in the SQLite data
+source.
+</p></dd><dt><a name="DATASRC_SQLITE_FIND_NSEC3_NO_ZONE"></a><span class="term">DATASRC_SQLITE_FIND_NSEC3_NO_ZONE no such zone '%1'</span></dt><dd><p>
+The SQLite data source was asked to provide a NSEC3 record for given zone.
+But it doesn't contain that zone.
+</p></dd><dt><a name="DATASRC_SQLITE_OPEN"></a><span class="term">DATASRC_SQLITE_OPEN opening SQLite database '%1'</span></dt><dd><p>
+Debug information. The SQLite data source is loading an SQLite database in
+the provided file.
+</p></dd><dt><a name="DATASRC_SQLITE_PREVIOUS"></a><span class="term">DATASRC_SQLITE_PREVIOUS looking for name previous to '%1'</span></dt><dd><p>
+Debug information. We're trying to look up name preceding the supplied one.
+</p></dd><dt><a name="DATASRC_SQLITE_PREVIOUS_NO_ZONE"></a><span class="term">DATASRC_SQLITE_PREVIOUS_NO_ZONE no zone containing '%1'</span></dt><dd><p>
+The SQLite data source tried to identify name preceding this one. But this
+one is not contained in any zone in the data source.
+</p></dd><dt><a name="DATASRC_SQLITE_SETUP"></a><span class="term">DATASRC_SQLITE_SETUP setting up SQLite database</span></dt><dd><p>
+The database for SQLite data source was found empty. It is assumed this is the
+first run and it is being initialized with current schema. It'll still contain
+no data, but it will be ready for use.
+</p></dd><dt><a name="DATASRC_STATIC_BAD_CLASS"></a><span class="term">DATASRC_STATIC_BAD_CLASS static data source can handle CH only</span></dt><dd><p>
+For some reason, someone asked the static data source a query that is not in
+the CH class.
+</p></dd><dt><a name="DATASRC_STATIC_CREATE"></a><span class="term">DATASRC_STATIC_CREATE creating the static datasource</span></dt><dd><p>
+Debug information. The static data source (the one holding stuff like
+version.bind) is being created.
+</p></dd><dt><a name="DATASRC_STATIC_FIND"></a><span class="term">DATASRC_STATIC_FIND looking for '%1/%2'</span></dt><dd><p>
+Debug information. This resource record set is being looked up in the static
+data source.
+</p></dd><dt><a name="DATASRC_UNEXPECTED_QUERY_STATE"></a><span class="term">DATASRC_UNEXPECTED_QUERY_STATE unexpected query state</span></dt><dd><p>
+This indicates a programming error. An internal task of unknown type was
+generated.
+</p></dd><dt><a name="LOGIMPL_ABOVEDBGMAX"></a><span class="term">LOGIMPL_ABOVEDBGMAX debug level of %1 is too high and will be set to the maximum of %2</span></dt><dd><p>
+A message from the underlying logger implementation code, the debug level
+(as set by the string DEBGUGn) is above the maximum allowed value and has
+been reduced to that value.
+</p></dd><dt><a name="LOGIMPL_BADDEBUG"></a><span class="term">LOGIMPL_BADDEBUG debug string is '%1': must be of the form DEBUGn</span></dt><dd><p>
+The string indicating the extended logging level (used by the underlying
+logger implementation code) is not of the stated form. In particular,
+it starts DEBUG but does not end with an integer.
+</p></dd><dt><a name="LOGIMPL_BELOWDBGMIN"></a><span class="term">LOGIMPL_BELOWDBGMIN debug level of %1 is too low and will be set to the minimum of %2</span></dt><dd><p>
+A message from the underlying logger implementation code, the debug level
+(as set by the string DEBGUGn) is below the minimum allowed value and has
+been increased to that value.
+</p></dd><dt><a name="MSG_BADDESTINATION"></a><span class="term">MSG_BADDESTINATION unrecognized log destination: %1</span></dt><dd><p>
+A logger destination value was given that was not recognized. The
+destination should be one of "console", "file", or "syslog".
+</p></dd><dt><a name="MSG_BADSEVERITY"></a><span class="term">MSG_BADSEVERITY unrecognized log severity: %1</span></dt><dd><p>
+A logger severity value was given that was not recognized. The severity
+should be one of "DEBUG", "INFO", "WARN", "ERROR", or "FATAL".
+</p></dd><dt><a name="MSG_BADSTREAM"></a><span class="term">MSG_BADSTREAM bad log console output stream: %1</span></dt><dd><p>
+A log console output stream was given that was not recognized. The
+output stream should be one of "stdout", or "stderr"
+</p></dd><dt><a name="MSG_DUPLNS"></a><span class="term">MSG_DUPLNS line %1: duplicate $NAMESPACE directive found</span></dt><dd><p>
+When reading a message file, more than one $NAMESPACE directive was found. In
+this version of the code, such a condition is regarded as an error and the
+read will be abandoned.
+</p></dd><dt><a name="MSG_DUPMSGID"></a><span class="term">MSG_DUPMSGID duplicate message ID (%1) in compiled code</span></dt><dd><p>
+Indicative of a programming error, when it started up, BIND10 detected that
+the given message ID had been registered by one or more modules. (All message
+IDs should be unique throughout BIND10.) This has no impact on the operation
+of the server other that erroneous messages may be logged. (When BIND10 loads
+the message IDs (and their associated text), if a duplicate ID is found it is
+discarded. However, when the module that supplied the duplicate ID logs that
+particular message, the text supplied by the module that added the original
+ID will be output - something that may bear no relation to the condition being
+logged.
+</p></dd><dt><a name="MSG_IDNOTFND"></a><span class="term">MSG_IDNOTFND could not replace message text for '%1': no such message</span></dt><dd><p>
+During start-up a local message file was read. A line with the listed
+message identification was found in the file, but the identification is not
+one contained in the compiled-in message dictionary. Either the message
+identification has been mis-spelled in the file, or the local file was used
+for an earlier version of the software and the message with that
+identification has been removed.
+</p><p>
+This message may appear a number of times in the file, once for every such
+unknown message identification.
+</p></dd><dt><a name="MSG_INVMSGID"></a><span class="term">MSG_INVMSGID line %1: invalid message identification '%2'</span></dt><dd><p>
+The concatenation of the prefix and the message identification is used as
+a symbol in the C++ module; as such it may only contain
+</p></dd><dt><a name="MSG_NOMSGID"></a><span class="term">MSG_NOMSGID line %1: message definition line found without a message ID</span></dt><dd><p>
+Message definition lines are lines starting with a "%". The rest of the line
+should comprise the message ID and text describing the message. This error
+indicates the message compiler found a line in the message file comprising
+just the "%" and nothing else.
+</p></dd><dt><a name="MSG_NOMSGTXT"></a><span class="term">MSG_NOMSGTXT line %1: line found containing a message ID ('%2') and no text</span></dt><dd><p>
+Message definition lines are lines starting with a "%". The rest of the line
+should comprise the message ID and text describing the message. This error
+is generated when a line is found in the message file that contains the
+leading "%" and the message identification but no text.
+</p></dd><dt><a name="MSG_NSEXTRARG"></a><span class="term">MSG_NSEXTRARG line %1: $NAMESPACE directive has too many arguments</span></dt><dd><p>
+The $NAMESPACE directive takes a single argument, a namespace in which all the
+generated symbol names are placed. This error is generated when the
+compiler finds a $NAMESPACE directive with more than one argument.
+</p></dd><dt><a name="MSG_NSINVARG"></a><span class="term">MSG_NSINVARG line %1: $NAMESPACE directive has an invalid argument ('%2')</span></dt><dd><p>
+The $NAMESPACE argument should be a valid C++ namespace. The reader does a
+cursory check on its validity, checking that the characters in the namespace
+are correct. The error is generated when the reader finds an invalid
+character. (Valid are alphanumeric characters, underscores and colons.)
+</p></dd><dt><a name="MSG_NSNOARG"></a><span class="term">MSG_NSNOARG line %1: no arguments were given to the $NAMESPACE directive</span></dt><dd><p>
+The $NAMESPACE directive takes a single argument, a namespace in which all the
+generated symbol names are placed. This error is generated when the
+compiler finds a $NAMESPACE directive with no arguments.
+</p></dd><dt><a name="MSG_OPENIN"></a><span class="term">MSG_OPENIN unable to open message file %1 for input: %2</span></dt><dd><p>
+The program was not able to open the specified input message file for the
+reason given.
+</p></dd><dt><a name="MSG_OPENOUT"></a><span class="term">MSG_OPENOUT unable to open %1 for output: %2</span></dt><dd><p>
+The program was not able to open the specified output file for the reason
+given.
+</p></dd><dt><a name="MSG_PRFEXTRARG"></a><span class="term">MSG_PRFEXTRARG line %1: $PREFIX directive has too many arguments</span></dt><dd><p>
+The $PREFIX directive takes a single argument, a prefix to be added to the
+symbol names when a C++ .h file is created. This error is generated when the
+compiler finds a $PREFIX directive with more than one argument.
+</p></dd><dt><a name="MSG_PRFINVARG"></a><span class="term">MSG_PRFINVARG line %1: $PREFIX directive has an invalid argument ('%2')</span></dt><dd><p>
+The $PREFIX argument is used in a symbol name in a C++ header file. As such,
+it must adhere to restrictions on C++ symbol names (e.g. may only contain
+alphanumeric characters or underscores, and may nor start with a digit).
+A $PREFIX directive was found with an argument (given in the message) that
+violates those restictions.
+</p></dd><dt><a name="MSG_RDLOCMES"></a><span class="term">MSG_RDLOCMES reading local message file %1</span></dt><dd><p>
+This is an informational message output by BIND10 when it starts to read a
+local message file. (A local message file may replace the text of one of more
+messages; the ID of the message will not be changed though.)
+</p></dd><dt><a name="MSG_READERR"></a><span class="term">MSG_READERR error reading from message file %1: %2</span></dt><dd><p>
+The specified error was encountered reading from the named message file.
+</p></dd><dt><a name="MSG_UNRECDIR"></a><span class="term">MSG_UNRECDIR line %1: unrecognised directive '%2'</span></dt><dd><p>
+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.
+</p></dd><dt><a name="MSG_WRITERR"></a><span class="term">MSG_WRITERR error writing to %1: %2</span></dt><dd><p>
+The specified error was encountered by the message compiler when writing to
+the named output file.
+</p></dd><dt><a name="NSAS_INVRESPSTR"></a><span class="term">NSAS_INVRESPSTR queried for %1 but got invalid response</span></dt><dd><p>
+This message indicates an internal error in the nameserver address store
+component (NSAS) of the resolver. The NSAS made a query for a RR for the
+specified nameserver but received an invalid response. Either the success
+function was called without a DNS message or the message was invalid on some
+way. (In the latter case, the error should have been picked up elsewhere in
+the processing logic, hence the raising of the error here.)
+</p></dd><dt><a name="NSAS_INVRESPTC"></a><span class="term">NSAS_INVRESPTC queried for %1 RR of type/class %2/%3, received response %4/%5</span></dt><dd><p>
+This message indicates an internal error in the nameserver address store
+component (NSAS) of the resolver. The NSAS made a query for the given RR
+type and class, but instead received an answer with the given type and class.
+</p></dd><dt><a name="NSAS_LOOKUPCANCEL"></a><span class="term">NSAS_LOOKUPCANCEL lookup for zone %1 has been cancelled</span></dt><dd><p>
+A debug message, this is output when a NSAS (nameserver address store -
+part of the resolver) lookup for a zone has been cancelled.
+</p></dd><dt><a name="NSAS_LOOKUPZONE"></a><span class="term">NSAS_LOOKUPZONE searching NSAS for nameservers for zone %1</span></dt><dd><p>
+A debug message, this is output when a call is made to the nameserver address
+store (part of the resolver) to obtain the nameservers for the specified zone.
+</p></dd><dt><a name="NSAS_NSADDR"></a><span class="term">NSAS_NSADDR asking resolver to obtain A and AAAA records for %1</span></dt><dd><p>
+A debug message, the NSAS (nameserver address store - part of the resolver) is
+making a callback into the resolver to retrieve the address records for the
+specified nameserver.
+</p></dd><dt><a name="NSAS_NSLKUPFAIL"></a><span class="term">NSAS_NSLKUPFAIL failed to lookup any %1 for %2</span></dt><dd><p>
+A debug message, the NSAS (nameserver address store - part of the resolver)
+has been unable to retrieve the specified resource record for the specified
+nameserver. This is not necessarily a problem - the nameserver may be
+unreachable, in which case the NSAS will try other nameservers in the zone.
+</p></dd><dt><a name="NSAS_NSLKUPSUCC"></a><span class="term">NSAS_NSLKUPSUCC found address %1 for %2</span></dt><dd><p>
+A debug message, the NSAS (nameserver address store - part of the resolver)
+has retrieved the given address for the specified nameserver through an
+external query.
+</p></dd><dt><a name="NSAS_SETRTT"></a><span class="term">NSAS_SETRTT reporting RTT for %1 as %2; new value is now %3</span></dt><dd><p>
+A NSAS (nameserver address store - part of the resolver) debug message
+reporting the round-trip time (RTT) for a query made to the specified
+nameserver. The RTT has been updated using the value given and the new RTT is
+displayed. (The RTT is subject to a calculation that damps out sudden
+changes. As a result, the new RTT is not necessarily equal to the RTT
+reported.)
+</p></dd><dt><a name="RESLIB_ANSWER"></a><span class="term">RESLIB_ANSWER answer received in response to query for <%1></span></dt><dd><p>
+A debug message recording that an answer has been received to an upstream
+query for the specified question. Previous debug messages will have indicated
+the server to which the question was sent.
+</p></dd><dt><a name="RESLIB_CNAME"></a><span class="term">RESLIB_CNAME CNAME received in response to query for <%1></span></dt><dd><p>
+A debug message recording that CNAME response has been received to an upstream
+query for the specified question. Previous debug messages will have indicated
+the server to which the question was sent.
+</p></dd><dt><a name="RESLIB_DEEPEST"></a><span class="term">RESLIB_DEEPEST did not find <%1> in cache, deepest delegation found is %2</span></dt><dd><p>
+A debug message, a cache lookup did not find the specified <name, class,
+type> tuple in the cache; instead, the deepest delegation found is indicated.
+</p></dd><dt><a name="RESLIB_FOLLOWCNAME"></a><span class="term">RESLIB_FOLLOWCNAME following CNAME chain to <%1></span></dt><dd><p>
+A debug message, a CNAME response was received and another query is being issued
+for the <name, class, type> tuple.
+</p></dd><dt><a name="RESLIB_LONGCHAIN"></a><span class="term">RESLIB_LONGCHAIN CNAME received in response to query for <%1>: CNAME chain length exceeded</span></dt><dd><p>
+A debug message recording that a CNAME response has been received to an upstream
+query for the specified question (Previous debug messages will have indicated
+the server to which the question was sent). However, receipt of this CNAME
+has meant that the resolver has exceeded the CNAME chain limit (a CNAME chain
+is where on CNAME points to another) and so an error is being returned.
+</p></dd><dt><a name="RESLIB_NONSRRSET"></a><span class="term">RESLIB_NONSRRSET no NS RRSet in referral response received to query for <%1></span></dt><dd><p>
+A debug message, this indicates that a response was received for the specified
+query and was categorised as a referral. However, the received message did
+not contain any NS RRsets. This may indicate a programming error in the
+response classification code.
+</p></dd><dt><a name="RESLIB_NSASLOOK"></a><span class="term">RESLIB_NSASLOOK looking up nameserver for zone %1 in the NSAS</span></dt><dd><p>
+A debug message, the RunningQuery object is querying the NSAS for the
+nameservers for the specified zone.
+</p></dd><dt><a name="RESLIB_NXDOMRR"></a><span class="term">RESLIB_NXDOMRR NXDOMAIN/NXRRSET received in response to query for <%1></span></dt><dd><p>
+A debug message recording that either a NXDOMAIN or an NXRRSET response has
+been received to an upstream query for the specified question. Previous debug
+messages will have indicated the server to which the question was sent.
+</p></dd><dt><a name="RESLIB_PROTOCOL"></a><span class="term">RESLIB_PROTOCOL protocol error in answer for %1: %3</span></dt><dd><p>
+A debug message indicating that a protocol error was received. As there
+are no retries left, an error will be reported.
+</p></dd><dt><a name="RESLIB_PROTOCOLRTRY"></a><span class="term">RESLIB_PROTOCOLRTRY protocol error in answer for %1: %2 (retries left: %3)</span></dt><dd><p>
+A debug message indicating that a protocol error was received and that
+the resolver is repeating the query to the same nameserver. After this
+repeated query, there will be the indicated number of retries left.
+</p></dd><dt><a name="RESLIB_RCODERR"></a><span class="term">RESLIB_RCODERR RCODE indicates error in response to query for <%1></span></dt><dd><p>
+A debug message, the response to the specified query indicated an error
+that is not covered by a specific code path. A SERVFAIL will be returned.
+</p></dd><dt><a name="RESLIB_REFERRAL"></a><span class="term">RESLIB_REFERRAL referral received in response to query for <%1></span></dt><dd><p>
+A debug message recording that a referral response has been received to an
+upstream query for the specified question. Previous debug messages will
+have indicated the server to which the question was sent.
+</p></dd><dt><a name="RESLIB_REFERZONE"></a><span class="term">RESLIB_REFERZONE referred to zone %1</span></dt><dd><p>
+A debug message indicating that the last referral message was to the specified
+zone.
+</p></dd><dt><a name="RESLIB_RESCAFND"></a><span class="term">RESLIB_RESCAFND found <%1> in the cache (resolve() instance %2)</span></dt><dd><p>
+This is a debug message and indicates that a RecursiveQuery object found the
+the specified <name, class, type> tuple in the cache. The instance number
+at the end of the message indicates which of the two resolve() methods has
+been called.
+</p></dd><dt><a name="RESLIB_RESCANOTFND"></a><span class="term">RESLIB_RESCANOTFND did not find <%1> in the cache, starting RunningQuery (resolve() instance %2)</span></dt><dd><p>
+This is a debug message and indicates that the look in the cache made by the
+RecursiveQuery::resolve() method did not find an answer, so a new RunningQuery
+object has been created to resolve the question. The instance number at
+the end of the message indicates which of the two resolve() methods has
+been called.
+</p></dd><dt><a name="RESLIB_RESOLVE"></a><span class="term">RESLIB_RESOLVE asked to resolve <%1> (resolve() instance %2)</span></dt><dd><p>
+A debug message, the RecursiveQuery::resolve method has been called to resolve
+the specified <name, class, type> tuple. The first action will be to lookup
+the specified tuple in the cache. The instance number at the end of the
+message indicates which of the two resolve() methods has been called.
+</p></dd><dt><a name="RESLIB_RRSETFND"></a><span class="term">RESLIB_RRSETFND found single RRset in the cache when querying for <%1> (resolve() instance %2)</span></dt><dd><p>
+A debug message, indicating that when RecursiveQuery::resolve queried the
+cache, a single RRset was found which was put in the answer. The instance
+number at the end of the message indicates which of the two resolve()
+methods has been called.
+</p></dd><dt><a name="RESLIB_RTT"></a><span class="term">RESLIB_RTT round-trip time of last query calculated as %1 ms</span></dt><dd><p>
+A debug message giving the round-trip time of the last query and response.
+</p></dd><dt><a name="RESLIB_RUNCAFND"></a><span class="term">RESLIB_RUNCAFND found <%1> in the cache</span></dt><dd><p>
+This is a debug message and indicates that a RunningQuery object found
+the specified <name, class, type> tuple in the cache.
+</p></dd><dt><a name="RESLIB_RUNCALOOK"></a><span class="term">RESLIB_RUNCALOOK looking up up <%1> in the cache</span></dt><dd><p>
+This is a debug message and indicates that a RunningQuery object has made
+a call to its doLookup() method to look up the specified <name, class, type>
+tuple, the first action of which will be to examine the cache.
+</p></dd><dt><a name="RESLIB_RUNQUFAIL"></a><span class="term">RESLIB_RUNQUFAIL failure callback - nameservers are unreachable</span></dt><dd><p>
+A debug message indicating that a RunningQuery's failure callback has been
+called because all nameservers for the zone in question are unreachable.
+</p></dd><dt><a name="RESLIB_RUNQUSUCC"></a><span class="term">RESLIB_RUNQUSUCC success callback - sending query to %1</span></dt><dd><p>
+A debug message indicating that a RunningQuery's success callback has been
+called because a nameserver has been found, and that a query is being sent
+to the specified nameserver.
+</p></dd><dt><a name="RESLIB_TESTSERV"></a><span class="term">RESLIB_TESTSERV setting test server to %1(%2)</span></dt><dd><p>
+This is an internal debugging message and is only generated in unit tests.
+It indicates that all upstream queries from the resolver are being routed to
+the specified server, regardless of the address of the nameserver to which
+the query would normally be routed. As it should never be seen in normal
+operation, it is a warning message instead of a debug message.
+</p></dd><dt><a name="RESLIB_TESTUPSTR"></a><span class="term">RESLIB_TESTUPSTR sending upstream query for <%1> to test server at %2</span></dt><dd><p>
+This is a debug message and should only be seen in unit tests. A query for
+the specified <name, class, type> tuple is being sent to a test nameserver
+whose address is given in the message.
+</p></dd><dt><a name="RESLIB_TIMEOUT"></a><span class="term">RESLIB_TIMEOUT query <%1> to %2 timed out</span></dt><dd><p>
+A debug message indicating that the specified query has timed out and as
+there are no retries left, an error will be reported.
+</p></dd><dt><a name="RESLIB_TIMEOUTRTRY"></a><span class="term">RESLIB_TIMEOUTRTRY query <%1> to %2 timed out, re-trying (retries left: %3)</span></dt><dd><p>
+A debug message indicating that the specified query has timed out and that
+the resolver is repeating the query to the same nameserver. After this
+repeated query, there will be the indicated number of retries left.
+</p></dd><dt><a name="RESLIB_TRUNCATED"></a><span class="term">RESLIB_TRUNCATED response to query for <%1> was truncated, re-querying over TCP</span></dt><dd><p>
+A debug message, this indicates that the response to the specified query was
+truncated and that the resolver will be re-querying over TCP. There are
+various reasons why responses may be truncated, so this message is normal and
+gives no cause for concern.
+</p></dd><dt><a name="RESLIB_UPSTREAM"></a><span class="term">RESLIB_UPSTREAM sending upstream query for <%1> to %2</span></dt><dd><p>
+A debug message indicating that a query for the specified <name, class, type>
+tuple is being sent to a nameserver whose address is given in the message.
+</p></dd><dt><a name="RESOLVER_AXFRTCP"></a><span class="term">RESOLVER_AXFRTCP AXFR request received over TCP</span></dt><dd><p>
+A debug message, the resolver received a NOTIFY message over TCP. The server
+cannot process it and will return an error message to the sender with the
+RCODE set to NOTIMP.
+</p></dd><dt><a name="RESOLVER_AXFRUDP"></a><span class="term">RESOLVER_AXFRUDP AXFR request received over UDP</span></dt><dd><p>
+A debug message, the resolver received a NOTIFY message over UDP. The server
+cannot process it (and in any case, an AXFR request should be sent over TCP)
+and will return an error message to the sender with the RCODE set to FORMERR.
+</p></dd><dt><a name="RESOLVER_CLTMOSMALL"></a><span class="term">RESOLVER_CLTMOSMALL client timeout of %1 is too small</span></dt><dd><p>
+An error indicating that the configuration value specified for the query
+timeout is too small.
+</p></dd><dt><a name="RESOLVER_CONFIGCHAN"></a><span class="term">RESOLVER_CONFIGCHAN configuration channel created</span></dt><dd><p>
+A debug message, output when the resolver has successfully established a
+connection to the configuration channel.
+</p></dd><dt><a name="RESOLVER_CONFIGERR"></a><span class="term">RESOLVER_CONFIGERR error in configuration: %1</span></dt><dd><p>
+An error was detected in a configuration update received by the resolver. This
+may be in the format of the configuration message (in which case this is a
+programming error) or it may be in the data supplied (in which case it is
+a user error). The reason for the error, given as a parameter in the message,
+will give more details.
+</p></dd><dt><a name="RESOLVER_CONFIGLOAD"></a><span class="term">RESOLVER_CONFIGLOAD configuration loaded</span></dt><dd><p>
+A debug message, output when the resolver configuration has been successfully
+loaded.
+</p></dd><dt><a name="RESOLVER_CONFIGUPD"></a><span class="term">RESOLVER_CONFIGUPD configuration updated: %1</span></dt><dd><p>
+A debug message, the configuration has been updated with the specified
+information.
+</p></dd><dt><a name="RESOLVER_CREATED"></a><span class="term">RESOLVER_CREATED main resolver object created</span></dt><dd><p>
+A debug message, output when the Resolver() object has been created.
+</p></dd><dt><a name="RESOLVER_DNSMSGRCVD"></a><span class="term">RESOLVER_DNSMSGRCVD DNS message received: %1</span></dt><dd><p>
+A debug message, this always precedes some other logging message and is the
+formatted contents of the DNS packet that the other message refers to.
+</p></dd><dt><a name="RESOLVER_DNSMSGSENT"></a><span class="term">RESOLVER_DNSMSGSENT DNS message of %1 bytes sent: %2</span></dt><dd><p>
+A debug message, this contains details of the response sent back to the querying
+system.
+</p></dd><dt><a name="RESOLVER_FAILED"></a><span class="term">RESOLVER_FAILED resolver failed, reason: %1</span></dt><dd><p>
+This is an error message output when an unhandled exception is caught by the
+resolver. All it can do is to shut down.
+</p></dd><dt><a name="RESOLVER_FWDADDR"></a><span class="term">RESOLVER_FWDADDR setting forward address %1(%2)</span></dt><dd><p>
+This message may appear multiple times during startup, and it lists the
+forward addresses used by the resolver when running in forwarding mode.
+</p></dd><dt><a name="RESOLVER_FWDQUERY"></a><span class="term">RESOLVER_FWDQUERY processing forward query</span></dt><dd><p>
+The received query has passed all checks and is being forwarded to upstream
+servers.
+</p></dd><dt><a name="RESOLVER_HDRERR"></a><span class="term">RESOLVER_HDRERR message received, exception when processing header: %1</span></dt><dd><p>
+A debug message noting that an exception occurred during the processing of
+a received packet. The packet has been dropped.
+</p></dd><dt><a name="RESOLVER_IXFR"></a><span class="term">RESOLVER_IXFR IXFR request received</span></dt><dd><p>
+The resolver received a NOTIFY message over TCP. The server cannot process it
+and will return an error message to the sender with the RCODE set to NOTIMP.
+</p></dd><dt><a name="RESOLVER_LKTMOSMALL"></a><span class="term">RESOLVER_LKTMOSMALL lookup timeout of %1 is too small</span></dt><dd><p>
+An error indicating that the configuration value specified for the lookup
+timeout is too small.
+</p></dd><dt><a name="RESOLVER_NFYNOTAUTH"></a><span class="term">RESOLVER_NFYNOTAUTH NOTIFY arrived but server is not authoritative</span></dt><dd><p>
+The resolver received a NOTIFY message. As the server is not authoritative it
+cannot process it, so it returns an error message to the sender with the RCODE
+set to NOTAUTH.
+</p></dd><dt><a name="RESOLVER_NORMQUERY"></a><span class="term">RESOLVER_NORMQUERY processing normal query</span></dt><dd><p>
+The received query has passed all checks and is being processed by the resolver.
+</p></dd><dt><a name="RESOLVER_NOROOTADDR"></a><span class="term">RESOLVER_NOROOTADDR no root addresses available</span></dt><dd><p>
+A warning message during startup, indicates that no root addresses have been
+set. This may be because the resolver will get them from a priming query.
+</p></dd><dt><a name="RESOLVER_NOTIN"></a><span class="term">RESOLVER_NOTIN non-IN class request received, returning REFUSED message</span></dt><dd><p>
+A debug message, the resolver has received a DNS packet that was not IN class.
+The resolver cannot handle such packets, so is returning a REFUSED response to
+the sender.
+</p></dd><dt><a name="RESOLVER_NOTONEQUES"></a><span class="term">RESOLVER_NOTONEQUES query contained %1 questions, exactly one question was expected</span></dt><dd><p>
+A debug message, the resolver received a query that contained the number of
+entires in the question section detailed in the message. This is a malformed
+message, as a DNS query must contain only one question. The resolver will
+return a message to the sender with the RCODE set to FORMERR.
+</p></dd><dt><a name="RESOLVER_OPCODEUNS"></a><span class="term">RESOLVER_OPCODEUNS opcode %1 not supported by the resolver</span></dt><dd><p>
+A debug message, the resolver received a message with an unsupported opcode
+(it can only process QUERY opcodes). It will return a message to the sender
+with the RCODE set to NOTIMP.
+</p></dd><dt><a name="RESOLVER_PARSEERR"></a><span class="term">RESOLVER_PARSEERR error parsing received message: %1 - returning %2</span></dt><dd><p>
+A debug message noting that the resolver received a message and the parsing
+of the body of the message failed due to some non-protocol related reason
+(although the parsing of the header succeeded). The message parameters give
+a textual description of the problem and the RCODE returned.
+</p></dd><dt><a name="RESOLVER_PRINTMSG"></a><span class="term">RESOLVER_PRINTMSG print message command, aeguments are: %1</span></dt><dd><p>
+This message is logged when a "print_message" command is received over the
+command channel.
+</p></dd><dt><a name="RESOLVER_PROTERR"></a><span class="term">RESOLVER_PROTERR protocol error parsing received message: %1 - returning %2</span></dt><dd><p>
+A debug message noting that the resolver received a message and the parsing
+of the body of the message failed due to some protocol error (although the
+parsing of the header succeeded). The message parameters give a textual
+description of the problem and the RCODE returned.
+</p></dd><dt><a name="RESOLVER_QUSETUP"></a><span class="term">RESOLVER_QUSETUP query setup</span></dt><dd><p>
+A debug message noting that the resolver is creating a RecursiveQuery object.
+</p></dd><dt><a name="RESOLVER_QUSHUT"></a><span class="term">RESOLVER_QUSHUT query shutdown</span></dt><dd><p>
+A debug message noting that the resolver is destroying a RecursiveQuery object.
+</p></dd><dt><a name="RESOLVER_QUTMOSMALL"></a><span class="term">RESOLVER_QUTMOSMALL query timeout of %1 is too small</span></dt><dd><p>
+An error indicating that the configuration value specified for the query
+timeout is too small.
+</p></dd><dt><a name="RESOLVER_RECURSIVE"></a><span class="term">RESOLVER_RECURSIVE running in recursive mode</span></dt><dd><p>
+This is an informational message that appears at startup noting that the
+resolver is running in recursive mode.
+</p></dd><dt><a name="RESOLVER_RECVMSG"></a><span class="term">RESOLVER_RECVMSG resolver has received a DNS message</span></dt><dd><p>
+A debug message indicating that the resolver has received a message. Depending
+on the debug settings, subsequent log output will indicate the nature of the
+message.
+</p></dd><dt><a name="RESOLVER_RETRYNEG"></a><span class="term">RESOLVER_RETRYNEG negative number of retries (%1) specified in the configuration</span></dt><dd><p>
+An error message indicating that the resolver configuration has specified a
+negative retry count. Only zero or positive values are valid.
+</p></dd><dt><a name="RESOLVER_ROOTADDR"></a><span class="term">RESOLVER_ROOTADDR setting root address %1(%2)</span></dt><dd><p>
+This message may appear multiple times during startup; it lists the root
+addresses used by the resolver.
+</p></dd><dt><a name="RESOLVER_SERVICE"></a><span class="term">RESOLVER_SERVICE service object created</span></dt><dd><p>
+A debug message, output when the main service object (which handles the
+received queries) is created.
+</p></dd><dt><a name="RESOLVER_SETPARAM"></a><span class="term">RESOLVER_SETPARAM query timeout: %1, client timeout: %2, lookup timeout: %3, retry count: %4</span></dt><dd><p>
+A debug message, lists the parameters associated with the message. These are:
+query timeout: the timeout (in ms) used for queries originated by the resolver
+to upstream servers. Client timeout: the interval to resolver a query by
+a client: after this time, the resolver sends back a SERVFAIL to the client
+whilst continuing to resolver the query. Lookup timeout: the time at which the
+resolver gives up trying to resolve a query. Retry count: the number of times
+the resolver will retry a query to an upstream server if it gets a timeout.
+</p><p>
+The client and lookup timeouts require a bit more explanation. The
+resolution of the clent query might require a large number of queries to
+upstream nameservers. Even if none of these queries timeout, the total time
+taken to perform all the queries may exceed the client timeout. When this
+happens, a SERVFAIL is returned to the client, but the resolver continues
+with the resolution process. Data received is added to the cache. However,
+there comes a time - the lookup timeout - when even the resolve gives up.
+At this point it will wait for pending upstream queries to complete or
+timeout and drop the query.
+</p></dd><dt><a name="RESOLVER_SHUTDOWN"></a><span class="term">RESOLVER_SHUTDOWN resolver shutdown complete</span></dt><dd><p>
+This information message is output when the resolver has shut down.
+</p></dd><dt><a name="RESOLVER_STARTED"></a><span class="term">RESOLVER_STARTED resolver started</span></dt><dd><p>
+This informational message is output by the resolver when all initialization
+has been completed and it is entering its main loop.
+</p></dd><dt><a name="RESOLVER_STARTING"></a><span class="term">RESOLVER_STARTING starting resolver with command line '%1'</span></dt><dd><p>
+An informational message, this is output when the resolver starts up.
+</p></dd><dt><a name="RESOLVER_UNEXRESP"></a><span class="term">RESOLVER_UNEXRESP received unexpected response, ignoring</span></dt><dd><p>
+A debug message noting that the server has received a response instead of a
+query and is ignoring it.
+</p></dd></dl></div><p>
+ </p></div></div></body></html>
diff --git a/doc/guide/bind10-messages.xml b/doc/guide/bind10-messages.xml
new file mode 100644
index 0000000..eaa8bb9
--- /dev/null
+++ b/doc/guide/bind10-messages.xml
@@ -0,0 +1,2018 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+<!ENTITY mdash "—" >
+<!ENTITY % version SYSTEM "version.ent">
+%version;
+]>
+<book>
+ <?xml-stylesheet href="bind10-guide.css" type="text/css"?>
+
+ <bookinfo>
+ <title>BIND 10 Messages Manual</title>
+
+ <copyright>
+ <year>2011</year><holder>Internet Systems Consortium, Inc.</holder>
+ </copyright>
+
+ <abstract>
+ <para>BIND 10 is a Domain Name System (DNS) suite managed by
+ Internet Systems Consortium (ISC). It includes DNS libraries
+ and modular components for controlling authoritative and
+ recursive DNS servers.
+ </para>
+ <para>
+ This is the messages manual for BIND 10 version &__VERSION__;.
+ The most up-to-date version of this document, along with
+ other documents for BIND 10, can be found at
+ <ulink url="http://bind10.isc.org/docs"/>.
+ </para>
+ </abstract>
+
+ <releaseinfo>This is the messages manual for BIND 10 version
+ &__VERSION__;.</releaseinfo>
+ </bookinfo>
+
+ <chapter id="intro">
+ <title>Introduction</title>
+ <para>
+ This document lists each message that can be logged by the
+ programs in the BIND 10 package. Each entry in this manual
+ is of the form:
+ <screen>IDENTIFICATION message-text</screen>
+ ... where "IDENTIFICATION" is the message identification included
+ in each message logged and "message-text" is the accompanying
+ message text. The "message-text" may include placeholders of the
+ form "%1", "%2" etc.; these parameters are replaced by relevant
+ values when the message is logged.
+ </para>
+ <para>
+ Each entry is also accompanied by a description giving more
+ information about the circumstances that result in the message
+ being logged.
+ </para>
+ <para>
+ For information on configuring and using BIND 10 logging,
+ refer to the <ulink url="bind10-guide.html">BIND 10 Guide</ulink>.
+ </para>
+ </chapter>
+
+ <chapter id="messages">
+ <title>BIND 10 Messages</title>
+ <para>
+ <variablelist>
+
+<varlistentry id="ASIODNS_FETCHCOMP">
+<term>ASIODNS_FETCHCOMP upstream fetch to %1(%2) has now completed</term>
+<listitem><para>
+A debug message, this records the the upstream fetch (a query made by the
+resolver on behalf of its client) to the specified address has completed.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="ASIODNS_FETCHSTOP">
+<term>ASIODNS_FETCHSTOP upstream fetch to %1(%2) has been stopped</term>
+<listitem><para>
+An external component has requested the halting of an upstream fetch. This
+is an allowed operation, and the message should only appear if debug is
+enabled.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="ASIODNS_OPENSOCK">
+<term>ASIODNS_OPENSOCK error %1 opening %2 socket to %3(%4)</term>
+<listitem><para>
+The asynchronous I/O code encountered an error when trying to open a socket
+of the specified protocol in order to send a message to the target address.
+The the number of the system error that cause the problem is given in the
+message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="ASIODNS_RECVSOCK">
+<term>ASIODNS_RECVSOCK error %1 reading %2 data from %3(%4)</term>
+<listitem><para>
+The asynchronous I/O code encountered an error when trying read data from
+the specified address on the given protocol. The the number of the system
+error that cause the problem is given in the message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="ASIODNS_RECVTMO">
+<term>ASIODNS_RECVTMO receive timeout while waiting for data from %1(%2)</term>
+<listitem><para>
+An upstream fetch from the specified address timed out. This may happen for
+any number of reasons and is most probably a problem at the remote server
+or a problem on the network. The message will only appear if debug is
+enabled.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="ASIODNS_SENDSOCK">
+<term>ASIODNS_SENDSOCK error %1 sending data using %2 to %3(%4)</term>
+<listitem><para>
+The asynchronous I/O code encountered an error when trying send data to
+the specified address on the given protocol. The the number of the system
+error that cause the problem is given in the message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="ASIODNS_UNKORIGIN">
+<term>ASIODNS_UNKORIGIN unknown origin for ASIO error code %1 (protocol: %2, address %3)</term>
+<listitem><para>
+This message should not appear and indicates an internal error if it does.
+Please enter a bug report.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="ASIODNS_UNKRESULT">
+<term>ASIODNS_UNKRESULT unknown result (%1) when IOFetch::stop() was executed for I/O to %2(%3)</term>
+<listitem><para>
+The termination method of the resolver's upstream fetch class was called with
+an unknown result code (which is given in the message). This message should
+not appear and may indicate an internal error. Please enter a bug report.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="CONFIG_CCSESSION_MSG">
+<term>CONFIG_CCSESSION_MSG error in CC session message: %1</term>
+<listitem><para>
+There was a problem with an incoming message on the command and control
+channel. The message does not appear to be a valid command, and is
+missing a required element or contains an unknown data format. This
+most likely means that another BIND10 module is sending a bad message.
+The message itself is ignored by this module.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="CONFIG_CCSESSION_MSG_INTERNAL">
+<term>CONFIG_CCSESSION_MSG_INTERNAL error handling CC session message: %1</term>
+<listitem><para>
+There was an internal problem handling an incoming message on the
+command and control channel. An unexpected exception was thrown. This
+most likely points to an internal inconsistency in the module code. The
+exception message is appended to the log error, and the module will
+continue to run, but will not send back an answer.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="CONFIG_FOPEN_ERR">
+<term>CONFIG_FOPEN_ERR error opening %1: %2</term>
+<listitem><para>
+There was an error opening the given file.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="CONFIG_JSON_PARSE">
+<term>CONFIG_JSON_PARSE JSON parse error in %1: %2</term>
+<listitem><para>
+There was a parse error in the JSON file. The given file does not appear
+to be in valid JSON format. Please verify that the filename is correct
+and that the contents are valid JSON.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="CONFIG_MANAGER_CONFIG">
+<term>CONFIG_MANAGER_CONFIG error getting configuration from cfgmgr: %1</term>
+<listitem><para>
+The configuration manager returned an error when this module requested
+the configuration. The full error message answer from the configuration
+manager is appended to the log error. The most likely cause is that
+the module is of a different (command specification) version than the
+running configuration manager.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="CONFIG_MANAGER_MOD_SPEC">
+<term>CONFIG_MANAGER_MOD_SPEC module specification not accepted by cfgmgr: %1</term>
+<listitem><para>
+The module specification file for this module was rejected by the
+configuration manager. The full error message answer from the
+configuration manager is appended to the log error. The most likely
+cause is that the module is of a different (specification file) version
+than the running configuration manager.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="CONFIG_MODULE_SPEC">
+<term>CONFIG_MODULE_SPEC module specification error in %1: %2</term>
+<listitem><para>
+The given file does not appear to be a valid specification file. Please
+verify that the filename is correct and that its contents are a valid
+BIND10 module specification.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_CACHE_CREATE">
+<term>DATASRC_CACHE_CREATE creating the hotspot cache</term>
+<listitem><para>
+Debug information that the hotspot cache was created at startup.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_CACHE_DESTROY">
+<term>DATASRC_CACHE_DESTROY destroying the hotspot cache</term>
+<listitem><para>
+Debug information. The hotspot cache is being destroyed.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_CACHE_DISABLE">
+<term>DATASRC_CACHE_DISABLE disabling the cache</term>
+<listitem><para>
+The hotspot cache is disabled from now on. It is not going to store
+information or return anything.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_CACHE_ENABLE">
+<term>DATASRC_CACHE_ENABLE enabling the cache</term>
+<listitem><para>
+The hotspot cache is enabled from now on.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_CACHE_EXPIRED">
+<term>DATASRC_CACHE_EXPIRED the item '%1' is expired</term>
+<listitem><para>
+Debug information. There was an attempt to look up an item in the hotspot
+cache. And the item was actually there, but it was too old, so it was removed
+instead and nothing is reported (the external behaviour is the same as with
+CACHE_NOT_FOUND).
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_CACHE_FOUND">
+<term>DATASRC_CACHE_FOUND the item '%1' was found</term>
+<listitem><para>
+Debug information. An item was successfully looked up in the hotspot cache.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_CACHE_FULL">
+<term>DATASRC_CACHE_FULL cache is full, dropping oldest</term>
+<listitem><para>
+Debug information. After inserting an item into the hotspot cache, the
+maximum number of items was exceeded, so the least recently used item will
+be dropped. This should be directly followed by CACHE_REMOVE.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_CACHE_INSERT">
+<term>DATASRC_CACHE_INSERT inserting item '%1' into the cache</term>
+<listitem><para>
+Debug information. It means a new item is being inserted into the hotspot
+cache.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_CACHE_NOT_FOUND">
+<term>DATASRC_CACHE_NOT_FOUND the item '%1' was not found</term>
+<listitem><para>
+Debug information. It was attempted to look up an item in the hotspot cache,
+but it is not there.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_CACHE_OLD_FOUND">
+<term>DATASRC_CACHE_OLD_FOUND older instance of cache item found, replacing</term>
+<listitem><para>
+Debug information. While inserting an item into the hotspot cache, an older
+instance of an item with the same name was found. The old instance will be
+removed. This should be directly followed by CACHE_REMOVE.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_CACHE_REMOVE">
+<term>DATASRC_CACHE_REMOVE removing '%1' from the cache</term>
+<listitem><para>
+Debug information. An item is being removed from the hotspot cache.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_CACHE_SLOTS">
+<term>DATASRC_CACHE_SLOTS setting the cache size to '%1', dropping '%2' items</term>
+<listitem><para>
+The maximum allowed number of items of the hotspot cache is set to the given
+number. If there are too many, some of them will be dropped. The size of 0
+means no limit.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_DO_QUERY">
+<term>DATASRC_DO_QUERY handling query for '%1/%2'</term>
+<listitem><para>
+Debug information. We're processing some internal query for given name and
+type.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_ADD_RRSET">
+<term>DATASRC_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3'</term>
+<listitem><para>
+Debug information. An RRset is being added to the in-memory data source.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_ADD_WILDCARD">
+<term>DATASRC_MEM_ADD_WILDCARD adding wildcards for '%1'</term>
+<listitem><para>
+Debug information. Some special marks above each * in wildcard name are needed.
+They are being added now for this name.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_ADD_ZONE">
+<term>DATASRC_MEM_ADD_ZONE adding zone '%1/%2'</term>
+<listitem><para>
+Debug information. A zone is being added into the in-memory data source.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_ANY_SUCCESS">
+<term>DATASRC_MEM_ANY_SUCCESS ANY query for '%1' successful</term>
+<listitem><para>
+Debug information. The domain was found and an ANY type query is being answered
+by providing everything found inside the domain.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_CNAME">
+<term>DATASRC_MEM_CNAME CNAME at the domain '%1'</term>
+<listitem><para>
+Debug information. The requested domain is an alias to a different domain,
+returning the CNAME instead.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_CNAME_COEXIST">
+<term>DATASRC_MEM_CNAME_COEXIST can't add data to CNAME in domain '%1'</term>
+<listitem><para>
+This is the same problem as in MEM_CNAME_TO_NONEMPTY, but it happened the
+other way around -- adding some outher data to CNAME.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_CNAME_TO_NONEMPTY">
+<term>DATASRC_MEM_CNAME_TO_NONEMPTY can't add CNAME to domain with other data in '%1'</term>
+<listitem><para>
+Someone or something tried to add a CNAME into a domain that already contains
+some other data. But the protocol forbids coexistence of CNAME with anything
+(RFC 1034, section 3.6.2). This indicates a problem with provided data.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_CREATE">
+<term>DATASRC_MEM_CREATE creating zone '%1' in '%2' class</term>
+<listitem><para>
+Debug information. A representation of a zone for the in-memory data source is
+being created.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_DELEG_FOUND">
+<term>DATASRC_MEM_DELEG_FOUND delegation found at '%1'</term>
+<listitem><para>
+Debug information. A delegation point was found above the requested record.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_DESTROY">
+<term>DATASRC_MEM_DESTROY destroying zone '%1' in '%2' class</term>
+<listitem><para>
+Debug information. A zone from in-memory data source is being destroyed.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_DNAME_ENCOUNTERED">
+<term>DATASRC_MEM_DNAME_ENCOUNTERED encountered a DNAME</term>
+<listitem><para>
+Debug information. While searching for the requested domain, a DNAME was
+encountered on the way. This may lead to redirection to a different domain and
+stop the search.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_DNAME_FOUND">
+<term>DATASRC_MEM_DNAME_FOUND DNAME found at '%1'</term>
+<listitem><para>
+Debug information. A DNAME was found instead of the requested information.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_DNAME_NS">
+<term>DATASRC_MEM_DNAME_NS dNAME and NS can't coexist in non-apex domain '%1'</term>
+<listitem><para>
+It was requested for DNAME and NS records to be put into the same domain
+which is not the apex (the top of the zone). This is forbidden by RFC
+2672, section 3. This indicates a problem with provided data.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_DOMAIN_EMPTY">
+<term>DATASRC_MEM_DOMAIN_EMPTY requested domain '%1' is empty</term>
+<listitem><para>
+Debug information. The requested domain exists in the tree of domains, but
+it is empty. Therefore it doesn't contain the requested resource type.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_DUP_RRSET">
+<term>DATASRC_MEM_DUP_RRSET duplicate RRset '%1/%2'</term>
+<listitem><para>
+An RRset is being inserted into in-memory data source for a second time. The
+original version must be removed first. Note that loading master files where an
+RRset is split into multiple locations is not supported yet.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_EXACT_DELEGATION">
+<term>DATASRC_MEM_EXACT_DELEGATION delegation at the exact domain '%1'</term>
+<listitem><para>
+Debug information. There's a NS record at the requested domain. This means
+this zone is not authoritative for the requested domain, but a delegation
+should be followed. The requested domain is an apex of some zone.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_FIND">
+<term>DATASRC_MEM_FIND find '%1/%2'</term>
+<listitem><para>
+Debug information. A search for the requested RRset is being started.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_FIND_ZONE">
+<term>DATASRC_MEM_FIND_ZONE looking for zone '%1'</term>
+<listitem><para>
+Debug information. A zone object for this zone is being searched for in the
+in-memory data source.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_LOAD">
+<term>DATASRC_MEM_LOAD loading zone '%1' from file '%2'</term>
+<listitem><para>
+Debug information. The content of master file is being loaded into the memory.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_NOTFOUND">
+<term>DATASRC_MEM_NOTFOUND requested domain '%1' not found</term>
+<listitem><para>
+Debug information. The requested domain does not exist.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_NS_ENCOUNTERED">
+<term>DATASRC_MEM_NS_ENCOUNTERED encountered a NS</term>
+<listitem><para>
+Debug information. While searching for the requested domain, a NS was
+encountered on the way (a delegation). This may lead to stop of the search.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_NXRRSET">
+<term>DATASRC_MEM_NXRRSET no such type '%1' at '%2'</term>
+<listitem><para>
+Debug information. The domain exists, but it doesn't hold any record of the
+requested type.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_OUT_OF_ZONE">
+<term>DATASRC_MEM_OUT_OF_ZONE domain '%1' doesn't belong to zone '%2'</term>
+<listitem><para>
+It was attempted to add the domain into a zone that shouldn't have it
+(eg. the domain is not subdomain of the zone origin). This indicates a
+problem with provided data.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_RENAME">
+<term>DATASRC_MEM_RENAME renaming RRset from '%1' to '%2'</term>
+<listitem><para>
+Debug information. A RRset is being generated from a different RRset (most
+probably a wildcard). So it must be renamed to whatever the user asked for. In
+fact, it's impossible to rename RRsets with our libraries, so a new one is
+created and all resource records are copied over.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_SINGLETON">
+<term>DATASRC_MEM_SINGLETON trying to add multiple RRs for domain '%1' and type '%2'</term>
+<listitem><para>
+Some resource types are singletons -- only one is allowed in a domain
+(for example CNAME or SOA). This indicates a problem with provided data.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_SUCCESS">
+<term>DATASRC_MEM_SUCCESS query for '%1/%2' successful</term>
+<listitem><para>
+Debug information. The requested record was found.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_SUPER_STOP">
+<term>DATASRC_MEM_SUPER_STOP stopped at superdomain '%1', domain '%2' is empty</term>
+<listitem><para>
+Debug information. The search stopped at a superdomain of the requested
+domain. The domain is a empty nonterminal, therefore it is treated as NXRRSET
+case (eg. the domain exists, but it doesn't have the requested record type).
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_SWAP">
+<term>DATASRC_MEM_SWAP swapping contents of two zone representations ('%1' and '%2')</term>
+<listitem><para>
+Debug information. The contents of two in-memory zones are being exchanged.
+This is usual practice to do some manipulation in exception-safe manner -- the
+new data are prepared in a different zone object and when it works, they are
+swapped. The old one contains the new data and the other one can be safely
+destroyed.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_WILDCARD_CANCEL">
+<term>DATASRC_MEM_WILDCARD_CANCEL wildcard match canceled for '%1'</term>
+<listitem><para>
+Debug information. A domain above wildcard was reached, but there's something
+below the requested domain. Therefore the wildcard doesn't apply here. This
+behaviour is specified by RFC 1034, section 4.3.3
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_WILDCARD_DNAME">
+<term>DATASRC_MEM_WILDCARD_DNAME dNAME record in wildcard domain '%1'</term>
+<listitem><para>
+The software refuses to load DNAME records into a wildcard domain. It isn't
+explicitly forbidden, but the protocol is ambiguous about how this should
+behave and BIND 9 refuses that as well. Please describe your intention using
+different tools.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_WILDCARD_NS">
+<term>DATASRC_MEM_WILDCARD_NS nS record in wildcard domain '%1'</term>
+<listitem><para>
+The software refuses to load NS records into a wildcard domain. It isn't
+explicitly forbidden, but the protocol is ambiguous about how this should
+behave and BIND 9 refuses that as well. Please describe your intention using
+different tools.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_META_ADD">
+<term>DATASRC_META_ADD adding a data source into meta data source</term>
+<listitem><para>
+Debug information. Yet another data source is being added into the meta data
+source. (probably at startup or reconfiguration)
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_META_ADD_CLASS_MISMATCH">
+<term>DATASRC_META_ADD_CLASS_MISMATCH mismatch between classes '%1' and '%2'</term>
+<listitem><para>
+It was attempted to add a data source into a meta data source. But their
+classes do not match.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_META_REMOVE">
+<term>DATASRC_META_REMOVE removing data source from meta data source</term>
+<listitem><para>
+Debug information. A data source is being removed from meta data source.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_ADD_NSEC">
+<term>DATASRC_QUERY_ADD_NSEC adding NSEC record for '%1'</term>
+<listitem><para>
+Debug information. A NSEC record covering this zone is being added.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_ADD_NSEC3">
+<term>DATASRC_QUERY_ADD_NSEC3 adding NSEC3 record of zone '%1'</term>
+<listitem><para>
+Debug information. A NSEC3 record for the given zone is being added to the
+response message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_ADD_RRSET">
+<term>DATASRC_QUERY_ADD_RRSET adding RRset '%1/%2' to message</term>
+<listitem><para>
+Debug information. An RRset is being added to the response message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_ADD_SOA">
+<term>DATASRC_QUERY_ADD_SOA adding SOA of '%1'</term>
+<listitem><para>
+Debug information. A SOA record of the given zone is being added to the
+authority section of the response message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_AUTH_FAIL">
+<term>DATASRC_QUERY_AUTH_FAIL the underlying data source failed with %1</term>
+<listitem><para>
+The underlying data source failed to answer the authoritative query. 1 means
+some error, 2 is not implemented. The data source should have logged the
+specific error already.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_BAD_REFERRAL">
+<term>DATASRC_QUERY_BAD_REFERRAL bad referral to '%1'</term>
+<listitem><para>
+The domain lives in another zone. But it is not possible to generate referral
+information for it.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_CACHED">
+<term>DATASRC_QUERY_CACHED data for %1/%2 found in cache</term>
+<listitem><para>
+Debug information. The requested data were found in the hotspot cache, so
+no query is sent to the real data source.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_CHECK_CACHE">
+<term>DATASRC_QUERY_CHECK_CACHE checking cache for '%1/%2'</term>
+<listitem><para>
+Debug information. While processing a query, lookup to the hotspot cache
+is being made.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_COPY_AUTH">
+<term>DATASRC_QUERY_COPY_AUTH copying authoritative section into message</term>
+<listitem><para>
+Debug information. The whole referral information is being copied into the
+response message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_DELEGATION">
+<term>DATASRC_QUERY_DELEGATION looking for delegation on the path to '%1'</term>
+<listitem><para>
+Debug information. The software is trying to identify delegation points on the
+way down to the given domain.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_EMPTY_CNAME">
+<term>DATASRC_QUERY_EMPTY_CNAME cNAME at '%1' is empty</term>
+<listitem><para>
+There was an CNAME and it was being followed. But it contains no records,
+so there's nowhere to go. There will be no answer. This indicates a problem
+with supplied data.
+We tried to follow
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_EMPTY_DNAME">
+<term>DATASRC_QUERY_EMPTY_DNAME the DNAME on '%1' is empty</term>
+<listitem><para>
+During an attempt to synthesize CNAME from this DNAME it was discovered the
+DNAME is empty (it has no records). This indicates problem with supplied data.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_FAIL">
+<term>DATASRC_QUERY_FAIL query failed</term>
+<listitem><para>
+Some subtask of query processing failed. The reason should have been reported
+already. We are returning SERVFAIL.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_FOLLOW_CNAME">
+<term>DATASRC_QUERY_FOLLOW_CNAME following CNAME at '%1'</term>
+<listitem><para>
+Debug information. The domain is a CNAME (or a DNAME and we created a CNAME
+for it already), so it's being followed.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_GET_MX_ADDITIONAL">
+<term>DATASRC_QUERY_GET_MX_ADDITIONAL addition of A/AAAA for '%1' requested by MX '%2'</term>
+<listitem><para>
+Debug information. While processing a query, a MX record was met. It
+references the mentioned address, so A/AAAA records for it are looked up
+and put it into the additional section.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_GET_NS_ADDITIONAL">
+<term>DATASRC_QUERY_GET_NS_ADDITIONAL addition of A/AAAA for '%1' requested by NS '%2'</term>
+<listitem><para>
+Debug information. While processing a query, a NS record was met. It
+references the mentioned address, so A/AAAA records for it are looked up
+and put it into the additional section.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_GLUE_FAIL">
+<term>DATASRC_QUERY_GLUE_FAIL the underlying data source failed with %1</term>
+<listitem><para>
+The underlying data source failed to answer the glue query. 1 means some error,
+2 is not implemented. The data source should have logged the specific error
+already.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_INVALID_OP">
+<term>DATASRC_QUERY_INVALID_OP invalid query operation requested</term>
+<listitem><para>
+This indicates a programmer error. The DO_QUERY was called with unknown
+operation code.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_IS_AUTH">
+<term>DATASRC_QUERY_IS_AUTH auth query (%1/%2)</term>
+<listitem><para>
+Debug information. The last DO_QUERY is an auth query.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_IS_GLUE">
+<term>DATASRC_QUERY_IS_GLUE glue query (%1/%2)</term>
+<listitem><para>
+Debug information. The last DO_QUERY is query for glue addresses.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_IS_NOGLUE">
+<term>DATASRC_QUERY_IS_NOGLUE query for non-glue addresses (%1/%2)</term>
+<listitem><para>
+Debug information. The last DO_QUERY is query for addresses that are not
+glue.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_IS_REF">
+<term>DATASRC_QUERY_IS_REF query for referral (%1/%2)</term>
+<listitem><para>
+Debug information. The last DO_QUERY is query for referral information.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_IS_SIMPLE">
+<term>DATASRC_QUERY_IS_SIMPLE simple query (%1/%2)</term>
+<listitem><para>
+Debug information. The last DO_QUERY is a simple query.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_MISPLACED_TASK">
+<term>DATASRC_QUERY_MISPLACED_TASK task of this type should not be here</term>
+<listitem><para>
+This indicates a programming error. A task was found in the internal task
+queue, but this kind of task wasn't designed to be inside the queue (it should
+be handled right away, not queued).
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_MISSING_NS">
+<term>DATASRC_QUERY_MISSING_NS missing NS records for '%1'</term>
+<listitem><para>
+NS records should have been put into the authority section. However, this zone
+has none. This indicates problem with provided data.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_MISSING_SOA">
+<term>DATASRC_QUERY_MISSING_SOA the zone '%1' has no SOA</term>
+<listitem><para>
+The answer should have been a negative one (eg. of nonexistence of something).
+To do so, a SOA record should be put into the authority section, but the zone
+does not have one. This indicates problem with provided data.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_NOGLUE_FAIL">
+<term>DATASRC_QUERY_NOGLUE_FAIL the underlying data source failed with %1</term>
+<listitem><para>
+The underlying data source failed to answer the no-glue query. 1 means some
+error, 2 is not implemented. The data source should have logged the specific
+error already.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_NO_CACHE_ANY_AUTH">
+<term>DATASRC_QUERY_NO_CACHE_ANY_AUTH ignoring cache for ANY query (%1/%2 in %3 class)</term>
+<listitem><para>
+Debug information. The hotspot cache is ignored for authoritative ANY queries
+for consistency reasons.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_NO_CACHE_ANY_SIMPLE">
+<term>DATASRC_QUERY_NO_CACHE_ANY_SIMPLE ignoring cache for ANY query (%1/%2 in %3 class)</term>
+<listitem><para>
+Debug information. The hotspot cache is ignored for ANY queries for consistency
+reasons.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_NO_DS_NSEC">
+<term>DATASRC_QUERY_NO_DS_NSEC there's no DS record in the '%1' zone</term>
+<listitem><para>
+An attempt to add a NSEC record into the message failed, because the zone does
+not have any DS record. This indicates problem with the provided data.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_NO_DS_NSEC3">
+<term>DATASRC_QUERY_NO_DS_NSEC3 there's no DS record in the '%1' zone</term>
+<listitem><para>
+An attempt to add a NSEC3 record into the message failed, because the zone does
+not have any DS record. This indicates problem with the provided data.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_NO_ZONE">
+<term>DATASRC_QUERY_NO_ZONE no zone containing '%1' in class '%2'</term>
+<listitem><para>
+Lookup of domain failed because the data have no zone that contain the
+domain. Maybe someone sent a query to the wrong server for some reason.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_PROCESS">
+<term>DATASRC_QUERY_PROCESS processing query '%1/%2' in the '%3' class</term>
+<listitem><para>
+Debug information. A sure query is being processed now.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_PROVENX_FAIL">
+<term>DATASRC_QUERY_PROVENX_FAIL unable to prove nonexistence of '%1'</term>
+<listitem><para>
+The user wants DNSSEC and we discovered the entity doesn't exist (either
+domain or the record). But there was an error getting NSEC/NSEC3 record
+to prove the nonexistence.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_REF_FAIL">
+<term>DATASRC_QUERY_REF_FAIL the underlying data source failed with %1</term>
+<listitem><para>
+The underlying data source failed to answer the query for referral information.
+1 means some error, 2 is not implemented. The data source should have logged
+the specific error already.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_RRSIG">
+<term>DATASRC_QUERY_RRSIG unable to answer RRSIG query</term>
+<listitem><para>
+The server is unable to answer a direct query for RRSIG type, but was asked
+to do so.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_SIMPLE_FAIL">
+<term>DATASRC_QUERY_SIMPLE_FAIL the underlying data source failed with %1</term>
+<listitem><para>
+The underlying data source failed to answer the simple query. 1 means some
+error, 2 is not implemented. The data source should have logged the specific
+error already.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_SYNTH_CNAME">
+<term>DATASRC_QUERY_SYNTH_CNAME synthesizing CNAME from DNAME on '%1'</term>
+<listitem><para>
+Debug information. While answering a query, a DNAME was met. The DNAME itself
+will be returned, but along with it a CNAME for clients which don't understand
+DNAMEs will be synthesized.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_TASK_FAIL">
+<term>DATASRC_QUERY_TASK_FAIL task failed with %1</term>
+<listitem><para>
+The query subtask failed. The reason should have been reported by the subtask
+already. The code is 1 for error, 2 for not implemented.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_TOO_MANY_CNAMES">
+<term>DATASRC_QUERY_TOO_MANY_CNAMES cNAME chain limit exceeded at '%1'</term>
+<listitem><para>
+A CNAME led to another CNAME and it led to another, and so on. After 16
+CNAMEs, the software gave up. Long CNAME chains are discouraged, and this
+might possibly be a loop as well. Note that some of the CNAMEs might have
+been synthesized from DNAMEs. This indicates problem with supplied data.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_UNKNOWN_RESULT">
+<term>DATASRC_QUERY_UNKNOWN_RESULT unknown result of subtask</term>
+<listitem><para>
+This indicates a programmer error. The answer of subtask doesn't look like
+anything known.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_WILDCARD">
+<term>DATASRC_QUERY_WILDCARD looking for a wildcard covering '%1'</term>
+<listitem><para>
+Debug information. A direct match wasn't found, so a wildcard covering the
+domain is being looked for now.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_WILDCARD_FAIL">
+<term>DATASRC_QUERY_WILDCARD_FAIL error processing wildcard for '%1'</term>
+<listitem><para>
+During an attempt to cover the domain by a wildcard an error happened. The
+exact kind was hopefully already reported.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_WILDCARD_PROVENX_FAIL">
+<term>DATASRC_QUERY_WILDCARD_PROVENX_FAIL unable to prove nonexistence of '%1' (%2)</term>
+<listitem><para>
+While processing a wildcard, it wasn't possible to prove nonexistence of the
+given domain or record. The code is 1 for error and 2 for not implemented.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_QUERY_WILDCARD_REFERRAL">
+<term>DATASRC_QUERY_WILDCARD_REFERRAL unable to find referral info for '%1' (%2)</term>
+<listitem><para>
+While processing a wildcard, a referral was met. But it wasn't possible to get
+enough information for it. The code is 1 for error, 2 for not implemented.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_CLOSE">
+<term>DATASRC_SQLITE_CLOSE closing SQLite database</term>
+<listitem><para>
+Debug information. The SQLite data source is closing the database file.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_CREATE">
+<term>DATASRC_SQLITE_CREATE sQLite data source created</term>
+<listitem><para>
+Debug information. An instance of SQLite data source is being created.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_DESTROY">
+<term>DATASRC_SQLITE_DESTROY sQLite data source destroyed</term>
+<listitem><para>
+Debug information. An instance of SQLite data source is being destroyed.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_ENCLOSURE">
+<term>DATASRC_SQLITE_ENCLOSURE looking for zone containing '%1'</term>
+<listitem><para>
+Debug information. The SQLite data source is trying to identify, which zone
+should hold this domain.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_ENCLOSURE_NOTFOUND">
+<term>DATASRC_SQLITE_ENCLOSURE_NOTFOUND no zone contains it</term>
+<listitem><para>
+Debug information. The last SQLITE_ENCLOSURE query was unsuccessful, there's
+no such zone in our data.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_FIND">
+<term>DATASRC_SQLITE_FIND looking for RRset '%1/%2'</term>
+<listitem><para>
+Debug information. The SQLite data source is looking up a resource record
+set.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_FINDADDRS">
+<term>DATASRC_SQLITE_FINDADDRS looking for A/AAAA addresses for '%1'</term>
+<listitem><para>
+Debug information. The data source is looking up the addresses for given
+domain name.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_FINDADDRS_BAD_CLASS">
+<term>DATASRC_SQLITE_FINDADDRS_BAD_CLASS class mismatch looking for addresses ('%1' and '%2')</term>
+<listitem><para>
+The SQLite data source was looking up A/AAAA addresses, but the data source
+contains different class than the query was for.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_FINDEXACT">
+<term>DATASRC_SQLITE_FINDEXACT looking for exact RRset '%1/%2'</term>
+<listitem><para>
+Debug information. The SQLite data source is looking up an exact resource
+record.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_FINDEXACT_BAD_CLASS">
+<term>DATASRC_SQLITE_FINDEXACT_BAD_CLASS class mismatch looking for an RRset ('%1' and '%2')</term>
+<listitem><para>
+The SQLite data source was looking up an exact RRset, but the data source
+contains different class than the query was for.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_FINDREC">
+<term>DATASRC_SQLITE_FINDREC looking for record '%1/%2'</term>
+<listitem><para>
+Debug information. The SQLite data source is looking up records of given name
+and type in the database.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_FINDREF">
+<term>DATASRC_SQLITE_FINDREF looking for referral at '%1'</term>
+<listitem><para>
+Debug information. The SQLite data source is identifying if this domain is
+a referral and where it goes.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_FINDREF_BAD_CLASS">
+<term>DATASRC_SQLITE_FINDREF_BAD_CLASS class mismatch looking for referral ('%1' and '%2')</term>
+<listitem><para>
+The SQLite data source was trying to identify, if there's a referral. But
+it contains different class than the query was for.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_FIND_BAD_CLASS">
+<term>DATASRC_SQLITE_FIND_BAD_CLASS class mismatch looking for an RRset ('%1' and '%2')</term>
+<listitem><para>
+The SQLite data source was looking up an RRset, but the data source contains
+different class than the query was for.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_FIND_NSEC3">
+<term>DATASRC_SQLITE_FIND_NSEC3 looking for NSEC3 in zone '%1' for hash '%2'</term>
+<listitem><para>
+Debug information. We're trying to look up a NSEC3 record in the SQLite data
+source.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_FIND_NSEC3_NO_ZONE">
+<term>DATASRC_SQLITE_FIND_NSEC3_NO_ZONE no such zone '%1'</term>
+<listitem><para>
+The SQLite data source was asked to provide a NSEC3 record for given zone.
+But it doesn't contain that zone.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_OPEN">
+<term>DATASRC_SQLITE_OPEN opening SQLite database '%1'</term>
+<listitem><para>
+Debug information. The SQLite data source is loading an SQLite database in
+the provided file.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_PREVIOUS">
+<term>DATASRC_SQLITE_PREVIOUS looking for name previous to '%1'</term>
+<listitem><para>
+Debug information. We're trying to look up name preceding the supplied one.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_PREVIOUS_NO_ZONE">
+<term>DATASRC_SQLITE_PREVIOUS_NO_ZONE no zone containing '%1'</term>
+<listitem><para>
+The SQLite data source tried to identify name preceding this one. But this
+one is not contained in any zone in the data source.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_SQLITE_SETUP">
+<term>DATASRC_SQLITE_SETUP setting up SQLite database</term>
+<listitem><para>
+The database for SQLite data source was found empty. It is assumed this is the
+first run and it is being initialized with current schema. It'll still contain
+no data, but it will be ready for use.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_STATIC_BAD_CLASS">
+<term>DATASRC_STATIC_BAD_CLASS static data source can handle CH only</term>
+<listitem><para>
+For some reason, someone asked the static data source a query that is not in
+the CH class.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_STATIC_CREATE">
+<term>DATASRC_STATIC_CREATE creating the static datasource</term>
+<listitem><para>
+Debug information. The static data source (the one holding stuff like
+version.bind) is being created.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_STATIC_FIND">
+<term>DATASRC_STATIC_FIND looking for '%1/%2'</term>
+<listitem><para>
+Debug information. This resource record set is being looked up in the static
+data source.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_UNEXPECTED_QUERY_STATE">
+<term>DATASRC_UNEXPECTED_QUERY_STATE unexpected query state</term>
+<listitem><para>
+This indicates a programming error. An internal task of unknown type was
+generated.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LOGIMPL_ABOVEDBGMAX">
+<term>LOGIMPL_ABOVEDBGMAX debug level of %1 is too high and will be set to the maximum of %2</term>
+<listitem><para>
+A message from the underlying logger implementation code, the debug level
+(as set by the string DEBGUGn) is above the maximum allowed value and has
+been reduced to that value.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LOGIMPL_BADDEBUG">
+<term>LOGIMPL_BADDEBUG debug string is '%1': must be of the form DEBUGn</term>
+<listitem><para>
+The string indicating the extended logging level (used by the underlying
+logger implementation code) is not of the stated form. In particular,
+it starts DEBUG but does not end with an integer.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LOGIMPL_BELOWDBGMIN">
+<term>LOGIMPL_BELOWDBGMIN debug level of %1 is too low and will be set to the minimum of %2</term>
+<listitem><para>
+A message from the underlying logger implementation code, the debug level
+(as set by the string DEBGUGn) is below the minimum allowed value and has
+been increased to that value.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_BADDESTINATION">
+<term>MSG_BADDESTINATION unrecognized log destination: %1</term>
+<listitem><para>
+A logger destination value was given that was not recognized. The
+destination should be one of "console", "file", or "syslog".
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_BADSEVERITY">
+<term>MSG_BADSEVERITY unrecognized log severity: %1</term>
+<listitem><para>
+A logger severity value was given that was not recognized. The severity
+should be one of "DEBUG", "INFO", "WARN", "ERROR", or "FATAL".
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_BADSTREAM">
+<term>MSG_BADSTREAM bad log console output stream: %1</term>
+<listitem><para>
+A log console output stream was given that was not recognized. The
+output stream should be one of "stdout", or "stderr"
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_DUPLNS">
+<term>MSG_DUPLNS line %1: duplicate $NAMESPACE directive found</term>
+<listitem><para>
+When reading a message file, more than one $NAMESPACE directive was found. In
+this version of the code, such a condition is regarded as an error and the
+read will be abandoned.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_DUPMSGID">
+<term>MSG_DUPMSGID duplicate message ID (%1) in compiled code</term>
+<listitem><para>
+Indicative of a programming error, when it started up, BIND10 detected that
+the given message ID had been registered by one or more modules. (All message
+IDs should be unique throughout BIND10.) This has no impact on the operation
+of the server other that erroneous messages may be logged. (When BIND10 loads
+the message IDs (and their associated text), if a duplicate ID is found it is
+discarded. However, when the module that supplied the duplicate ID logs that
+particular message, the text supplied by the module that added the original
+ID will be output - something that may bear no relation to the condition being
+logged.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_IDNOTFND">
+<term>MSG_IDNOTFND could not replace message text for '%1': no such message</term>
+<listitem><para>
+During start-up a local message file was read. A line with the listed
+message identification was found in the file, but the identification is not
+one contained in the compiled-in message dictionary. Either the message
+identification has been mis-spelled in the file, or the local file was used
+for an earlier version of the software and the message with that
+identification has been removed.
+</para><para>
+This message may appear a number of times in the file, once for every such
+unknown message identification.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_INVMSGID">
+<term>MSG_INVMSGID line %1: invalid message identification '%2'</term>
+<listitem><para>
+The concatenation of the prefix and the message identification is used as
+a symbol in the C++ module; as such it may only contain
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_NOMSGID">
+<term>MSG_NOMSGID line %1: message definition line found without a message ID</term>
+<listitem><para>
+Message definition lines are lines starting with a "%". The rest of the line
+should comprise the message ID and text describing the message. This error
+indicates the message compiler found a line in the message file comprising
+just the "%" and nothing else.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_NOMSGTXT">
+<term>MSG_NOMSGTXT line %1: line found containing a message ID ('%2') and no text</term>
+<listitem><para>
+Message definition lines are lines starting with a "%". The rest of the line
+should comprise the message ID and text describing the message. This error
+is generated when a line is found in the message file that contains the
+leading "%" and the message identification but no text.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_NSEXTRARG">
+<term>MSG_NSEXTRARG line %1: $NAMESPACE directive has too many arguments</term>
+<listitem><para>
+The $NAMESPACE directive takes a single argument, a namespace in which all the
+generated symbol names are placed. This error is generated when the
+compiler finds a $NAMESPACE directive with more than one argument.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_NSINVARG">
+<term>MSG_NSINVARG line %1: $NAMESPACE directive has an invalid argument ('%2')</term>
+<listitem><para>
+The $NAMESPACE argument should be a valid C++ namespace. The reader does a
+cursory check on its validity, checking that the characters in the namespace
+are correct. The error is generated when the reader finds an invalid
+character. (Valid are alphanumeric characters, underscores and colons.)
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_NSNOARG">
+<term>MSG_NSNOARG line %1: no arguments were given to the $NAMESPACE directive</term>
+<listitem><para>
+The $NAMESPACE directive takes a single argument, a namespace in which all the
+generated symbol names are placed. This error is generated when the
+compiler finds a $NAMESPACE directive with no arguments.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_OPENIN">
+<term>MSG_OPENIN unable to open message file %1 for input: %2</term>
+<listitem><para>
+The program was not able to open the specified input message file for the
+reason given.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_OPENOUT">
+<term>MSG_OPENOUT unable to open %1 for output: %2</term>
+<listitem><para>
+The program was not able to open the specified output file for the reason
+given.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_PRFEXTRARG">
+<term>MSG_PRFEXTRARG line %1: $PREFIX directive has too many arguments</term>
+<listitem><para>
+The $PREFIX directive takes a single argument, a prefix to be added to the
+symbol names when a C++ .h file is created. This error is generated when the
+compiler finds a $PREFIX directive with more than one argument.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_PRFINVARG">
+<term>MSG_PRFINVARG line %1: $PREFIX directive has an invalid argument ('%2')</term>
+<listitem><para>
+The $PREFIX argument is used in a symbol name in a C++ header file. As such,
+it must adhere to restrictions on C++ symbol names (e.g. may only contain
+alphanumeric characters or underscores, and may nor start with a digit).
+A $PREFIX directive was found with an argument (given in the message) that
+violates those restictions.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_RDLOCMES">
+<term>MSG_RDLOCMES reading local message file %1</term>
+<listitem><para>
+This is an informational message output by BIND10 when it starts to read a
+local message file. (A local message file may replace the text of one of more
+messages; the ID of the message will not be changed though.)
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_READERR">
+<term>MSG_READERR error reading from message file %1: %2</term>
+<listitem><para>
+The specified error was encountered reading from the named message file.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_UNRECDIR">
+<term>MSG_UNRECDIR line %1: unrecognised directive '%2'</term>
+<listitem><para>
+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.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="MSG_WRITERR">
+<term>MSG_WRITERR error writing to %1: %2</term>
+<listitem><para>
+The specified error was encountered by the message compiler when writing to
+the named output file.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="NSAS_INVRESPSTR">
+<term>NSAS_INVRESPSTR queried for %1 but got invalid response</term>
+<listitem><para>
+This message indicates an internal error in the nameserver address store
+component (NSAS) of the resolver. The NSAS made a query for a RR for the
+specified nameserver but received an invalid response. Either the success
+function was called without a DNS message or the message was invalid on some
+way. (In the latter case, the error should have been picked up elsewhere in
+the processing logic, hence the raising of the error here.)
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="NSAS_INVRESPTC">
+<term>NSAS_INVRESPTC queried for %1 RR of type/class %2/%3, received response %4/%5</term>
+<listitem><para>
+This message indicates an internal error in the nameserver address store
+component (NSAS) of the resolver. The NSAS made a query for the given RR
+type and class, but instead received an answer with the given type and class.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="NSAS_LOOKUPCANCEL">
+<term>NSAS_LOOKUPCANCEL lookup for zone %1 has been cancelled</term>
+<listitem><para>
+A debug message, this is output when a NSAS (nameserver address store -
+part of the resolver) lookup for a zone has been cancelled.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="NSAS_LOOKUPZONE">
+<term>NSAS_LOOKUPZONE searching NSAS for nameservers for zone %1</term>
+<listitem><para>
+A debug message, this is output when a call is made to the nameserver address
+store (part of the resolver) to obtain the nameservers for the specified zone.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="NSAS_NSADDR">
+<term>NSAS_NSADDR asking resolver to obtain A and AAAA records for %1</term>
+<listitem><para>
+A debug message, the NSAS (nameserver address store - part of the resolver) is
+making a callback into the resolver to retrieve the address records for the
+specified nameserver.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="NSAS_NSLKUPFAIL">
+<term>NSAS_NSLKUPFAIL failed to lookup any %1 for %2</term>
+<listitem><para>
+A debug message, the NSAS (nameserver address store - part of the resolver)
+has been unable to retrieve the specified resource record for the specified
+nameserver. This is not necessarily a problem - the nameserver may be
+unreachable, in which case the NSAS will try other nameservers in the zone.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="NSAS_NSLKUPSUCC">
+<term>NSAS_NSLKUPSUCC found address %1 for %2</term>
+<listitem><para>
+A debug message, the NSAS (nameserver address store - part of the resolver)
+has retrieved the given address for the specified nameserver through an
+external query.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="NSAS_SETRTT">
+<term>NSAS_SETRTT reporting RTT for %1 as %2; new value is now %3</term>
+<listitem><para>
+A NSAS (nameserver address store - part of the resolver) debug message
+reporting the round-trip time (RTT) for a query made to the specified
+nameserver. The RTT has been updated using the value given and the new RTT is
+displayed. (The RTT is subject to a calculation that damps out sudden
+changes. As a result, the new RTT is not necessarily equal to the RTT
+reported.)
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_ANSWER">
+<term>RESLIB_ANSWER answer received in response to query for <%1></term>
+<listitem><para>
+A debug message recording that an answer has been received to an upstream
+query for the specified question. Previous debug messages will have indicated
+the server to which the question was sent.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_CNAME">
+<term>RESLIB_CNAME CNAME received in response to query for <%1></term>
+<listitem><para>
+A debug message recording that CNAME response has been received to an upstream
+query for the specified question. Previous debug messages will have indicated
+the server to which the question was sent.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_DEEPEST">
+<term>RESLIB_DEEPEST did not find <%1> in cache, deepest delegation found is %2</term>
+<listitem><para>
+A debug message, a cache lookup did not find the specified <name, class,
+type> tuple in the cache; instead, the deepest delegation found is indicated.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_FOLLOWCNAME">
+<term>RESLIB_FOLLOWCNAME following CNAME chain to <%1></term>
+<listitem><para>
+A debug message, a CNAME response was received and another query is being issued
+for the <name, class, type> tuple.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_LONGCHAIN">
+<term>RESLIB_LONGCHAIN CNAME received in response to query for <%1>: CNAME chain length exceeded</term>
+<listitem><para>
+A debug message recording that a CNAME response has been received to an upstream
+query for the specified question (Previous debug messages will have indicated
+the server to which the question was sent). However, receipt of this CNAME
+has meant that the resolver has exceeded the CNAME chain limit (a CNAME chain
+is where on CNAME points to another) and so an error is being returned.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_NONSRRSET">
+<term>RESLIB_NONSRRSET no NS RRSet in referral response received to query for <%1></term>
+<listitem><para>
+A debug message, this indicates that a response was received for the specified
+query and was categorised as a referral. However, the received message did
+not contain any NS RRsets. This may indicate a programming error in the
+response classification code.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_NSASLOOK">
+<term>RESLIB_NSASLOOK looking up nameserver for zone %1 in the NSAS</term>
+<listitem><para>
+A debug message, the RunningQuery object is querying the NSAS for the
+nameservers for the specified zone.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_NXDOMRR">
+<term>RESLIB_NXDOMRR NXDOMAIN/NXRRSET received in response to query for <%1></term>
+<listitem><para>
+A debug message recording that either a NXDOMAIN or an NXRRSET response has
+been received to an upstream query for the specified question. Previous debug
+messages will have indicated the server to which the question was sent.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_PROTOCOL">
+<term>RESLIB_PROTOCOL protocol error in answer for %1: %3</term>
+<listitem><para>
+A debug message indicating that a protocol error was received. As there
+are no retries left, an error will be reported.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_PROTOCOLRTRY">
+<term>RESLIB_PROTOCOLRTRY protocol error in answer for %1: %2 (retries left: %3)</term>
+<listitem><para>
+A debug message indicating that a protocol error was received and that
+the resolver is repeating the query to the same nameserver. After this
+repeated query, there will be the indicated number of retries left.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_RCODERR">
+<term>RESLIB_RCODERR RCODE indicates error in response to query for <%1></term>
+<listitem><para>
+A debug message, the response to the specified query indicated an error
+that is not covered by a specific code path. A SERVFAIL will be returned.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_REFERRAL">
+<term>RESLIB_REFERRAL referral received in response to query for <%1></term>
+<listitem><para>
+A debug message recording that a referral response has been received to an
+upstream query for the specified question. Previous debug messages will
+have indicated the server to which the question was sent.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_REFERZONE">
+<term>RESLIB_REFERZONE referred to zone %1</term>
+<listitem><para>
+A debug message indicating that the last referral message was to the specified
+zone.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_RESCAFND">
+<term>RESLIB_RESCAFND found <%1> in the cache (resolve() instance %2)</term>
+<listitem><para>
+This is a debug message and indicates that a RecursiveQuery object found the
+the specified <name, class, type> tuple in the cache. The instance number
+at the end of the message indicates which of the two resolve() methods has
+been called.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_RESCANOTFND">
+<term>RESLIB_RESCANOTFND did not find <%1> in the cache, starting RunningQuery (resolve() instance %2)</term>
+<listitem><para>
+This is a debug message and indicates that the look in the cache made by the
+RecursiveQuery::resolve() method did not find an answer, so a new RunningQuery
+object has been created to resolve the question. The instance number at
+the end of the message indicates which of the two resolve() methods has
+been called.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_RESOLVE">
+<term>RESLIB_RESOLVE asked to resolve <%1> (resolve() instance %2)</term>
+<listitem><para>
+A debug message, the RecursiveQuery::resolve method has been called to resolve
+the specified <name, class, type> tuple. The first action will be to lookup
+the specified tuple in the cache. The instance number at the end of the
+message indicates which of the two resolve() methods has been called.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_RRSETFND">
+<term>RESLIB_RRSETFND found single RRset in the cache when querying for <%1> (resolve() instance %2)</term>
+<listitem><para>
+A debug message, indicating that when RecursiveQuery::resolve queried the
+cache, a single RRset was found which was put in the answer. The instance
+number at the end of the message indicates which of the two resolve()
+methods has been called.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_RTT">
+<term>RESLIB_RTT round-trip time of last query calculated as %1 ms</term>
+<listitem><para>
+A debug message giving the round-trip time of the last query and response.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_RUNCAFND">
+<term>RESLIB_RUNCAFND found <%1> in the cache</term>
+<listitem><para>
+This is a debug message and indicates that a RunningQuery object found
+the specified <name, class, type> tuple in the cache.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_RUNCALOOK">
+<term>RESLIB_RUNCALOOK looking up up <%1> in the cache</term>
+<listitem><para>
+This is a debug message and indicates that a RunningQuery object has made
+a call to its doLookup() method to look up the specified <name, class, type>
+tuple, the first action of which will be to examine the cache.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_RUNQUFAIL">
+<term>RESLIB_RUNQUFAIL failure callback - nameservers are unreachable</term>
+<listitem><para>
+A debug message indicating that a RunningQuery's failure callback has been
+called because all nameservers for the zone in question are unreachable.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_RUNQUSUCC">
+<term>RESLIB_RUNQUSUCC success callback - sending query to %1</term>
+<listitem><para>
+A debug message indicating that a RunningQuery's success callback has been
+called because a nameserver has been found, and that a query is being sent
+to the specified nameserver.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_TESTSERV">
+<term>RESLIB_TESTSERV setting test server to %1(%2)</term>
+<listitem><para>
+This is an internal debugging message and is only generated in unit tests.
+It indicates that all upstream queries from the resolver are being routed to
+the specified server, regardless of the address of the nameserver to which
+the query would normally be routed. As it should never be seen in normal
+operation, it is a warning message instead of a debug message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_TESTUPSTR">
+<term>RESLIB_TESTUPSTR sending upstream query for <%1> to test server at %2</term>
+<listitem><para>
+This is a debug message and should only be seen in unit tests. A query for
+the specified <name, class, type> tuple is being sent to a test nameserver
+whose address is given in the message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_TIMEOUT">
+<term>RESLIB_TIMEOUT query <%1> to %2 timed out</term>
+<listitem><para>
+A debug message indicating that the specified query has timed out and as
+there are no retries left, an error will be reported.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_TIMEOUTRTRY">
+<term>RESLIB_TIMEOUTRTRY query <%1> to %2 timed out, re-trying (retries left: %3)</term>
+<listitem><para>
+A debug message indicating that the specified query has timed out and that
+the resolver is repeating the query to the same nameserver. After this
+repeated query, there will be the indicated number of retries left.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_TRUNCATED">
+<term>RESLIB_TRUNCATED response to query for <%1> was truncated, re-querying over TCP</term>
+<listitem><para>
+A debug message, this indicates that the response to the specified query was
+truncated and that the resolver will be re-querying over TCP. There are
+various reasons why responses may be truncated, so this message is normal and
+gives no cause for concern.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESLIB_UPSTREAM">
+<term>RESLIB_UPSTREAM sending upstream query for <%1> to %2</term>
+<listitem><para>
+A debug message indicating that a query for the specified <name, class, type>
+tuple is being sent to a nameserver whose address is given in the message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_AXFRTCP">
+<term>RESOLVER_AXFRTCP AXFR request received over TCP</term>
+<listitem><para>
+A debug message, the resolver received a NOTIFY message over TCP. The server
+cannot process it and will return an error message to the sender with the
+RCODE set to NOTIMP.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_AXFRUDP">
+<term>RESOLVER_AXFRUDP AXFR request received over UDP</term>
+<listitem><para>
+A debug message, the resolver received a NOTIFY message over UDP. The server
+cannot process it (and in any case, an AXFR request should be sent over TCP)
+and will return an error message to the sender with the RCODE set to FORMERR.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_CLTMOSMALL">
+<term>RESOLVER_CLTMOSMALL client timeout of %1 is too small</term>
+<listitem><para>
+An error indicating that the configuration value specified for the query
+timeout is too small.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_CONFIGCHAN">
+<term>RESOLVER_CONFIGCHAN configuration channel created</term>
+<listitem><para>
+A debug message, output when the resolver has successfully established a
+connection to the configuration channel.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_CONFIGERR">
+<term>RESOLVER_CONFIGERR error in configuration: %1</term>
+<listitem><para>
+An error was detected in a configuration update received by the resolver. This
+may be in the format of the configuration message (in which case this is a
+programming error) or it may be in the data supplied (in which case it is
+a user error). The reason for the error, given as a parameter in the message,
+will give more details.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_CONFIGLOAD">
+<term>RESOLVER_CONFIGLOAD configuration loaded</term>
+<listitem><para>
+A debug message, output when the resolver configuration has been successfully
+loaded.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_CONFIGUPD">
+<term>RESOLVER_CONFIGUPD configuration updated: %1</term>
+<listitem><para>
+A debug message, the configuration has been updated with the specified
+information.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_CREATED">
+<term>RESOLVER_CREATED main resolver object created</term>
+<listitem><para>
+A debug message, output when the Resolver() object has been created.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_DNSMSGRCVD">
+<term>RESOLVER_DNSMSGRCVD DNS message received: %1</term>
+<listitem><para>
+A debug message, this always precedes some other logging message and is the
+formatted contents of the DNS packet that the other message refers to.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_DNSMSGSENT">
+<term>RESOLVER_DNSMSGSENT DNS message of %1 bytes sent: %2</term>
+<listitem><para>
+A debug message, this contains details of the response sent back to the querying
+system.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_FAILED">
+<term>RESOLVER_FAILED resolver failed, reason: %1</term>
+<listitem><para>
+This is an error message output when an unhandled exception is caught by the
+resolver. All it can do is to shut down.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_FWDADDR">
+<term>RESOLVER_FWDADDR setting forward address %1(%2)</term>
+<listitem><para>
+This message may appear multiple times during startup, and it lists the
+forward addresses used by the resolver when running in forwarding mode.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_FWDQUERY">
+<term>RESOLVER_FWDQUERY processing forward query</term>
+<listitem><para>
+The received query has passed all checks and is being forwarded to upstream
+servers.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_HDRERR">
+<term>RESOLVER_HDRERR message received, exception when processing header: %1</term>
+<listitem><para>
+A debug message noting that an exception occurred during the processing of
+a received packet. The packet has been dropped.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_IXFR">
+<term>RESOLVER_IXFR IXFR request received</term>
+<listitem><para>
+The resolver received a NOTIFY message over TCP. The server cannot process it
+and will return an error message to the sender with the RCODE set to NOTIMP.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_LKTMOSMALL">
+<term>RESOLVER_LKTMOSMALL lookup timeout of %1 is too small</term>
+<listitem><para>
+An error indicating that the configuration value specified for the lookup
+timeout is too small.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_NFYNOTAUTH">
+<term>RESOLVER_NFYNOTAUTH NOTIFY arrived but server is not authoritative</term>
+<listitem><para>
+The resolver received a NOTIFY message. As the server is not authoritative it
+cannot process it, so it returns an error message to the sender with the RCODE
+set to NOTAUTH.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_NORMQUERY">
+<term>RESOLVER_NORMQUERY processing normal query</term>
+<listitem><para>
+The received query has passed all checks and is being processed by the resolver.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_NOROOTADDR">
+<term>RESOLVER_NOROOTADDR no root addresses available</term>
+<listitem><para>
+A warning message during startup, indicates that no root addresses have been
+set. This may be because the resolver will get them from a priming query.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_NOTIN">
+<term>RESOLVER_NOTIN non-IN class request received, returning REFUSED message</term>
+<listitem><para>
+A debug message, the resolver has received a DNS packet that was not IN class.
+The resolver cannot handle such packets, so is returning a REFUSED response to
+the sender.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_NOTONEQUES">
+<term>RESOLVER_NOTONEQUES query contained %1 questions, exactly one question was expected</term>
+<listitem><para>
+A debug message, the resolver received a query that contained the number of
+entires in the question section detailed in the message. This is a malformed
+message, as a DNS query must contain only one question. The resolver will
+return a message to the sender with the RCODE set to FORMERR.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_OPCODEUNS">
+<term>RESOLVER_OPCODEUNS opcode %1 not supported by the resolver</term>
+<listitem><para>
+A debug message, the resolver received a message with an unsupported opcode
+(it can only process QUERY opcodes). It will return a message to the sender
+with the RCODE set to NOTIMP.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_PARSEERR">
+<term>RESOLVER_PARSEERR error parsing received message: %1 - returning %2</term>
+<listitem><para>
+A debug message noting that the resolver received a message and the parsing
+of the body of the message failed due to some non-protocol related reason
+(although the parsing of the header succeeded). The message parameters give
+a textual description of the problem and the RCODE returned.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_PRINTMSG">
+<term>RESOLVER_PRINTMSG print message command, aeguments are: %1</term>
+<listitem><para>
+This message is logged when a "print_message" command is received over the
+command channel.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_PROTERR">
+<term>RESOLVER_PROTERR protocol error parsing received message: %1 - returning %2</term>
+<listitem><para>
+A debug message noting that the resolver received a message and the parsing
+of the body of the message failed due to some protocol error (although the
+parsing of the header succeeded). The message parameters give a textual
+description of the problem and the RCODE returned.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_QUSETUP">
+<term>RESOLVER_QUSETUP query setup</term>
+<listitem><para>
+A debug message noting that the resolver is creating a RecursiveQuery object.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_QUSHUT">
+<term>RESOLVER_QUSHUT query shutdown</term>
+<listitem><para>
+A debug message noting that the resolver is destroying a RecursiveQuery object.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_QUTMOSMALL">
+<term>RESOLVER_QUTMOSMALL query timeout of %1 is too small</term>
+<listitem><para>
+An error indicating that the configuration value specified for the query
+timeout is too small.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_RECURSIVE">
+<term>RESOLVER_RECURSIVE running in recursive mode</term>
+<listitem><para>
+This is an informational message that appears at startup noting that the
+resolver is running in recursive mode.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_RECVMSG">
+<term>RESOLVER_RECVMSG resolver has received a DNS message</term>
+<listitem><para>
+A debug message indicating that the resolver has received a message. Depending
+on the debug settings, subsequent log output will indicate the nature of the
+message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_RETRYNEG">
+<term>RESOLVER_RETRYNEG negative number of retries (%1) specified in the configuration</term>
+<listitem><para>
+An error message indicating that the resolver configuration has specified a
+negative retry count. Only zero or positive values are valid.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_ROOTADDR">
+<term>RESOLVER_ROOTADDR setting root address %1(%2)</term>
+<listitem><para>
+This message may appear multiple times during startup; it lists the root
+addresses used by the resolver.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_SERVICE">
+<term>RESOLVER_SERVICE service object created</term>
+<listitem><para>
+A debug message, output when the main service object (which handles the
+received queries) is created.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_SETPARAM">
+<term>RESOLVER_SETPARAM query timeout: %1, client timeout: %2, lookup timeout: %3, retry count: %4</term>
+<listitem><para>
+A debug message, lists the parameters associated with the message. These are:
+query timeout: the timeout (in ms) used for queries originated by the resolver
+to upstream servers. Client timeout: the interval to resolver a query by
+a client: after this time, the resolver sends back a SERVFAIL to the client
+whilst continuing to resolver the query. Lookup timeout: the time at which the
+resolver gives up trying to resolve a query. Retry count: the number of times
+the resolver will retry a query to an upstream server if it gets a timeout.
+</para><para>
+The client and lookup timeouts require a bit more explanation. The
+resolution of the clent query might require a large number of queries to
+upstream nameservers. Even if none of these queries timeout, the total time
+taken to perform all the queries may exceed the client timeout. When this
+happens, a SERVFAIL is returned to the client, but the resolver continues
+with the resolution process. Data received is added to the cache. However,
+there comes a time - the lookup timeout - when even the resolve gives up.
+At this point it will wait for pending upstream queries to complete or
+timeout and drop the query.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_SHUTDOWN">
+<term>RESOLVER_SHUTDOWN resolver shutdown complete</term>
+<listitem><para>
+This information message is output when the resolver has shut down.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_STARTED">
+<term>RESOLVER_STARTED resolver started</term>
+<listitem><para>
+This informational message is output by the resolver when all initialization
+has been completed and it is entering its main loop.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_STARTING">
+<term>RESOLVER_STARTING starting resolver with command line '%1'</term>
+<listitem><para>
+An informational message, this is output when the resolver starts up.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="RESOLVER_UNEXRESP">
+<term>RESOLVER_UNEXRESP received unexpected response, ignoring</term>
+<listitem><para>
+A debug message noting that the server has received a response instead of a
+query and is ignoring it.
+</para></listitem>
+</varlistentry>
+ </variablelist>
+ </para>
+ </chapter>
+</book>
diff --git a/ext/asio/README b/ext/asio/README
index c581e8e..da12a9d 100644
--- a/ext/asio/README
+++ b/ext/asio/README
@@ -1,5 +1,10 @@
ASIO library header files
-Version 1.4.5 (2010-05-12)
+Version 1.4.8 (2011-04-19)
Downloaded from http://sourceforge.net/projects/asio/files
Project page: http://think-async.com/Asio
-No local modifications.
+
+Local modifications:
+Added ASIO_DECL to a number of function definitions
+git commit c32718be9f5409b6f72d98ddcd0b1ccd4c5c2293
+See also the bug report at:
+http://sourceforge.net/tracker/?func=detail&aid=3291113&group_id=122478&atid=694037
diff --git a/ext/asio/asio.hpp b/ext/asio/asio.hpp
index d8acd58..b0ebc96 100644
--- a/ext/asio/asio.hpp
+++ b/ext/asio/asio.hpp
@@ -2,7 +2,7 @@
// asio.hpp
// ~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/ext/asio/asio/basic_datagram_socket.hpp b/ext/asio/asio/basic_datagram_socket.hpp
index cb149f9..2cbb354 100644
--- a/ext/asio/asio/basic_datagram_socket.hpp
+++ b/ext/asio/asio/basic_datagram_socket.hpp
@@ -2,7 +2,7 @@
// basic_datagram_socket.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/basic_socket.hpp"
#include "asio/datagram_socket_service.hpp"
-#include "asio/error.hpp"
#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
diff --git a/ext/asio/asio/basic_deadline_timer.hpp b/ext/asio/asio/basic_deadline_timer.hpp
index 0d183f7..27781e7 100644
--- a/ext/asio/asio/basic_deadline_timer.hpp
+++ b/ext/asio/asio/basic_deadline_timer.hpp
@@ -2,7 +2,7 @@
// basic_deadline_timer.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/basic_io_object.hpp"
#include "asio/deadline_timer_service.hpp"
-#include "asio/error.hpp"
#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
diff --git a/ext/asio/asio/basic_io_object.hpp b/ext/asio/asio/basic_io_object.hpp
index 092170d..da35462 100644
--- a/ext/asio/asio/basic_io_object.hpp
+++ b/ext/asio/asio/basic_io_object.hpp
@@ -2,7 +2,7 @@
// basic_io_object.hpp
// ~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,10 +15,11 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/io_service.hpp"
+#include "asio/detail/config.hpp"
#include "asio/detail/noncopyable.hpp"
+#include "asio/io_service.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
diff --git a/ext/asio/asio/basic_raw_socket.hpp b/ext/asio/asio/basic_raw_socket.hpp
index 9d93b97..7a55c4a 100644
--- a/ext/asio/asio/basic_raw_socket.hpp
+++ b/ext/asio/asio/basic_raw_socket.hpp
@@ -2,7 +2,7 @@
// basic_raw_socket.hpp
// ~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/basic_socket.hpp"
-#include "asio/raw_socket_service.hpp"
-#include "asio/error.hpp"
#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+#include "asio/raw_socket_service.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
diff --git a/ext/asio/asio/basic_serial_port.hpp b/ext/asio/asio/basic_serial_port.hpp
index edb7bb0..38fe5e9 100644
--- a/ext/asio/asio/basic_serial_port.hpp
+++ b/ext/asio/asio/basic_serial_port.hpp
@@ -2,7 +2,7 @@
// basic_serial_port.hpp
// ~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
// Copyright (c) 2008 Rep Invariant Systems, Inc. (info at repinvariant.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
@@ -16,21 +16,19 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
-#include <string>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HAS_SERIAL_PORT) \
+ || defined(GENERATING_DOCUMENTATION)
+#include <string>
#include "asio/basic_io_object.hpp"
+#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/serial_port_base.hpp"
#include "asio/serial_port_service.hpp"
-#include "asio/detail/throw_error.hpp"
-#if defined(ASIO_HAS_SERIAL_PORT) \
- || defined(GENERATING_DOCUMENTATION)
+#include "asio/detail/push_options.hpp"
namespace asio {
@@ -614,9 +612,9 @@ public:
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#endif // defined(ASIO_HAS_SERIAL_PORT)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_BASIC_SERIAL_PORT_HPP
diff --git a/ext/asio/asio/basic_socket.hpp b/ext/asio/asio/basic_socket.hpp
index 8b3f1d7..5c66b4a 100644
--- a/ext/asio/asio/basic_socket.hpp
+++ b/ext/asio/asio/basic_socket.hpp
@@ -2,7 +2,7 @@
// basic_socket.hpp
// ~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/basic_io_object.hpp"
+#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/socket_base.hpp"
-#include "asio/detail/throw_error.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
diff --git a/ext/asio/asio/basic_socket_acceptor.hpp b/ext/asio/asio/basic_socket_acceptor.hpp
index 97fa56b..15f9220 100644
--- a/ext/asio/asio/basic_socket_acceptor.hpp
+++ b/ext/asio/asio/basic_socket_acceptor.hpp
@@ -2,7 +2,7 @@
// basic_socket_acceptor.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,14 +15,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/basic_io_object.hpp"
#include "asio/basic_socket.hpp"
+#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/socket_acceptor_service.hpp"
#include "asio/socket_base.hpp"
-#include "asio/detail/throw_error.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
diff --git a/ext/asio/asio/basic_socket_iostream.hpp b/ext/asio/asio/basic_socket_iostream.hpp
index 361ecb8..efcd51e 100644
--- a/ext/asio/asio/basic_socket_iostream.hpp
+++ b/ext/asio/asio/basic_socket_iostream.hpp
@@ -2,7 +2,7 @@
// basic_socket_iostream.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,22 +15,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if !defined(BOOST_NO_IOSTREAM)
-#include "asio/detail/push_options.hpp"
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/utility/base_from_member.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/basic_socket_streambuf.hpp"
#include "asio/stream_socket_service.hpp"
@@ -79,6 +72,8 @@
} \
/**/
+#include "asio/detail/push_options.hpp"
+
namespace asio {
/// Iostream interface for a socket.
@@ -146,11 +141,11 @@ public:
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#undef ASIO_PRIVATE_CTR_DEF
#undef ASIO_PRIVATE_CONNECT_DEF
#endif // defined(BOOST_NO_IOSTREAM)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_BASIC_SOCKET_IOSTREAM_HPP
diff --git a/ext/asio/asio/basic_socket_streambuf.hpp b/ext/asio/asio/basic_socket_streambuf.hpp
index ae0264e..05fc865 100644
--- a/ext/asio/asio/basic_socket_streambuf.hpp
+++ b/ext/asio/asio/basic_socket_streambuf.hpp
@@ -2,7 +2,7 @@
// basic_socket_streambuf.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,15 +15,10 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if !defined(BOOST_NO_IOSTREAM)
-#include "asio/detail/push_options.hpp"
#include <streambuf>
#include <boost/array.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
@@ -31,12 +26,10 @@
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/utility/base_from_member.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/basic_socket.hpp"
+#include "asio/detail/throw_error.hpp"
#include "asio/io_service.hpp"
#include "asio/stream_socket_service.hpp"
-#include "asio/detail/throw_error.hpp"
#if !defined(ASIO_SOCKET_STREAMBUF_MAX_ARITY)
#define ASIO_SOCKET_STREAMBUF_MAX_ARITY 5
@@ -74,6 +67,8 @@
} \
/**/
+#include "asio/detail/push_options.hpp"
+
namespace asio {
/// Iostream streambuf for a socket.
@@ -286,10 +281,10 @@ private:
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#undef ASIO_PRIVATE_CONNECT_DEF
#endif // !defined(BOOST_NO_IOSTREAM)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_BASIC_SOCKET_STREAMBUF_HPP
diff --git a/ext/asio/asio/basic_stream_socket.hpp b/ext/asio/asio/basic_stream_socket.hpp
index b7b4f06..41543fc 100644
--- a/ext/asio/asio/basic_stream_socket.hpp
+++ b/ext/asio/asio/basic_stream_socket.hpp
@@ -2,7 +2,7 @@
// basic_stream_socket.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/basic_socket.hpp"
+#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/stream_socket_service.hpp"
-#include "asio/detail/throw_error.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
diff --git a/ext/asio/asio/basic_streambuf.hpp b/ext/asio/asio/basic_streambuf.hpp
index b34b3fe..17157da 100644
--- a/ext/asio/asio/basic_streambuf.hpp
+++ b/ext/asio/asio/basic_streambuf.hpp
@@ -2,7 +2,7 @@
// basic_streambuf.hpp
// ~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,28 +15,23 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if !defined(BOOST_NO_IOSTREAM)
-#include "asio/detail/push_options.hpp"
#include <algorithm>
#include <cstring>
-#include <memory>
#include <stdexcept>
#include <streambuf>
#include <vector>
#include <boost/limits.hpp>
#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
-
+#include "asio/basic_streambuf_fwd.hpp"
#include "asio/buffer.hpp"
#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
/// Automatically resizable buffer class based on std::streambuf.
@@ -107,7 +102,11 @@ namespace asio {
* is >> s;
* @endcode
*/
+#if defined(GENERATING_DOCUMENTATION)
template <typename Allocator = std::allocator<char> >
+#else
+template <typename Allocator>
+#endif
class basic_streambuf
: public std::streambuf,
private noncopyable
@@ -337,8 +336,27 @@ protected:
private:
std::size_t max_size_;
std::vector<char_type, Allocator> buffer_;
+
+ // Helper function to get the preferred size for reading data.
+ friend std::size_t read_size_helper(
+ basic_streambuf& sb, std::size_t max_size)
+ {
+ return std::min<std::size_t>(
+ std::max<std::size_t>(512, sb.buffer_.capacity() - sb.size()),
+ std::min<std::size_t>(max_size, sb.max_size() - sb.size()));
+ }
};
+// Helper function to get the preferred size for reading data. Used for any
+// user-provided specialisations of basic_streambuf.
+template <typename Allocator>
+inline std::size_t read_size_helper(
+ basic_streambuf<Allocator>& sb, std::size_t max_size)
+{
+ return std::min<std::size_t>(512,
+ std::min<std::size_t>(max_size, sb.max_size() - sb.size()));
+}
+
} // namespace asio
#include "asio/detail/pop_options.hpp"
diff --git a/ext/asio/asio/basic_streambuf_fwd.hpp b/ext/asio/asio/basic_streambuf_fwd.hpp
new file mode 100644
index 0000000..e1d81ac
--- /dev/null
+++ b/ext/asio/asio/basic_streambuf_fwd.hpp
@@ -0,0 +1,33 @@
+//
+// basic_streambuf_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_BASIC_STREAMBUF_FWD_HPP
+#define ASIO_BASIC_STREAMBUF_FWD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if !defined(BOOST_NO_IOSTREAM)
+
+#include <memory>
+
+namespace asio {
+
+template <typename Allocator = std::allocator<char> >
+class basic_streambuf;
+
+} // namespace asio
+
+#endif // !defined(BOOST_NO_IOSTREAM)
+
+#endif // ASIO_BASIC_STREAMBUF_FWD_HPP
diff --git a/ext/asio/asio/buffer.hpp b/ext/asio/asio/buffer.hpp
index 43b475c..6810fca 100644
--- a/ext/asio/asio/buffer.hpp
+++ b/ext/asio/asio/buffer.hpp
@@ -2,7 +2,7 @@
// buffer.hpp
// ~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
-#include <boost/array.hpp>
-#include <boost/type_traits/is_const.hpp>
#include <string>
#include <vector>
-#include "asio/detail/pop_options.hpp"
+#include <boost/detail/workaround.hpp>
+#include "asio/detail/array_fwd.hpp"
#if defined(BOOST_MSVC)
# if defined(_HAS_ITERATOR_DEBUGGING) && (_HAS_ITERATOR_DEBUGGING != 0)
@@ -43,11 +39,17 @@
#endif // defined(__GNUC__)
#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
-# include "asio/detail/push_options.hpp"
# include <boost/function.hpp>
-# include "asio/detail/pop_options.hpp"
#endif // ASIO_ENABLE_BUFFER_DEBUGGING
+#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) \
+ || BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590))
+# include <boost/type_traits/is_const.hpp>
+#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582))
+ // || BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590))
+
+#include "asio/detail/push_options.hpp"
+
namespace asio {
class mutable_buffer;
diff --git a/ext/asio/asio/buffered_read_stream.hpp b/ext/asio/asio/buffered_read_stream.hpp
index afd22ce..d9dc311 100644
--- a/ext/asio/asio/buffered_read_stream.hpp
+++ b/ext/asio/asio/buffered_read_stream.hpp
@@ -2,7 +2,7 @@
// buffered_read_stream.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,23 +15,20 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
#include <cstring>
-#include <boost/config.hpp>
#include <boost/type_traits/remove_reference.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/buffered_read_stream_fwd.hpp"
#include "asio/buffer.hpp"
-#include "asio/error.hpp"
-#include "asio/io_service.hpp"
#include "asio/detail/bind_handler.hpp"
#include "asio/detail/buffer_resize_guard.hpp"
#include "asio/detail/buffered_stream_storage.hpp"
#include "asio/detail/noncopyable.hpp"
+#include "asio/error.hpp"
+#include "asio/io_service.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
diff --git a/ext/asio/asio/buffered_read_stream_fwd.hpp b/ext/asio/asio/buffered_read_stream_fwd.hpp
index 5078775..0e8495a 100644
--- a/ext/asio/asio/buffered_read_stream_fwd.hpp
+++ b/ext/asio/asio/buffered_read_stream_fwd.hpp
@@ -2,7 +2,7 @@
// buffered_read_stream_fwd.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,8 +15,6 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
namespace asio {
template <typename Stream>
@@ -24,6 +22,4 @@ class buffered_read_stream;
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_BUFFERED_READ_STREAM_FWD_HPP
diff --git a/ext/asio/asio/buffered_stream.hpp b/ext/asio/asio/buffered_stream.hpp
index 23dc9c3..11b99f6 100644
--- a/ext/asio/asio/buffered_stream.hpp
+++ b/ext/asio/asio/buffered_stream.hpp
@@ -2,7 +2,7 @@
// buffered_stream.hpp
// ~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,19 +15,16 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/buffered_read_stream.hpp"
#include "asio/buffered_write_stream.hpp"
#include "asio/buffered_stream_fwd.hpp"
+#include "asio/detail/noncopyable.hpp"
#include "asio/error.hpp"
#include "asio/io_service.hpp"
-#include "asio/detail/noncopyable.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
diff --git a/ext/asio/asio/buffered_stream_fwd.hpp b/ext/asio/asio/buffered_stream_fwd.hpp
index f26cd43..a8e958e 100644
--- a/ext/asio/asio/buffered_stream_fwd.hpp
+++ b/ext/asio/asio/buffered_stream_fwd.hpp
@@ -2,7 +2,7 @@
// buffered_stream_fwd.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,8 +15,6 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
namespace asio {
template <typename Stream>
@@ -24,6 +22,4 @@ class buffered_stream;
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_BUFFERED_STREAM_FWD_HPP
diff --git a/ext/asio/asio/buffered_write_stream.hpp b/ext/asio/asio/buffered_write_stream.hpp
index 30a92ce..ee0ec77 100644
--- a/ext/asio/asio/buffered_write_stream.hpp
+++ b/ext/asio/asio/buffered_write_stream.hpp
@@ -2,7 +2,7 @@
// buffered_write_stream.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,24 +15,21 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
#include <cstring>
-#include <boost/config.hpp>
#include <boost/type_traits/remove_reference.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/buffered_write_stream_fwd.hpp"
#include "asio/buffer.hpp"
#include "asio/completion_condition.hpp"
-#include "asio/error.hpp"
-#include "asio/io_service.hpp"
-#include "asio/write.hpp"
#include "asio/detail/bind_handler.hpp"
#include "asio/detail/buffered_stream_storage.hpp"
#include "asio/detail/noncopyable.hpp"
+#include "asio/error.hpp"
+#include "asio/io_service.hpp"
+#include "asio/write.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
diff --git a/ext/asio/asio/buffered_write_stream_fwd.hpp b/ext/asio/asio/buffered_write_stream_fwd.hpp
index dc0e014..cb09fe8 100644
--- a/ext/asio/asio/buffered_write_stream_fwd.hpp
+++ b/ext/asio/asio/buffered_write_stream_fwd.hpp
@@ -2,7 +2,7 @@
// buffered_write_stream_fwd.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,8 +15,6 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
namespace asio {
template <typename Stream>
@@ -24,6 +22,4 @@ class buffered_write_stream;
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_BUFFERED_WRITE_STREAM_FWD_HPP
diff --git a/ext/asio/asio/buffers_iterator.hpp b/ext/asio/asio/buffers_iterator.hpp
index 7da55f2..dcca2d3 100644
--- a/ext/asio/asio/buffers_iterator.hpp
+++ b/ext/asio/asio/buffers_iterator.hpp
@@ -2,7 +2,7 @@
// buffers_iterator.hpp
// ~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,20 +15,17 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
#include <boost/assert.hpp>
-#include <boost/config.hpp>
#include <boost/detail/workaround.hpp>
#include <boost/iterator.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/type_traits/add_const.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/buffer.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail
@@ -210,6 +207,15 @@ public:
return tmp;
}
+ /// Addition operator.
+ friend buffers_iterator operator+(std::ptrdiff_t difference,
+ const buffers_iterator& iter)
+ {
+ buffers_iterator tmp(iter);
+ tmp.advance(difference);
+ return tmp;
+ }
+
/// Subtraction operator.
friend buffers_iterator operator-(const buffers_iterator& iter,
std::ptrdiff_t difference)
diff --git a/ext/asio/asio/completion_condition.hpp b/ext/asio/asio/completion_condition.hpp
index d4e631d..dd1fb27 100644
--- a/ext/asio/asio/completion_condition.hpp
+++ b/ext/asio/asio/completion_condition.hpp
@@ -2,7 +2,7 @@
// completion_condition.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,12 +15,10 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+#include <cstddef>
#include "asio/detail/push_options.hpp"
-#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
namespace asio {
diff --git a/ext/asio/asio/datagram_socket_service.hpp b/ext/asio/asio/datagram_socket_service.hpp
index 7d135ee..87821c3 100644
--- a/ext/asio/asio/datagram_socket_service.hpp
+++ b/ext/asio/asio/datagram_socket_service.hpp
@@ -2,7 +2,7 @@
// datagram_socket_service.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,10 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/error.hpp"
#include "asio/io_service.hpp"
-#include "asio/detail/service_base.hpp"
#if defined(ASIO_HAS_IOCP)
# include "asio/detail/win_iocp_socket_service.hpp"
@@ -32,6 +26,8 @@
# include "asio/detail/reactive_socket_service.hpp"
#endif
+#include "asio/detail/push_options.hpp"
+
namespace asio {
/// Default service implementation for a datagram socket.
diff --git a/ext/asio/asio/deadline_timer.hpp b/ext/asio/asio/deadline_timer.hpp
index e0905f2..fd0d5dc 100644
--- a/ext/asio/asio/deadline_timer.hpp
+++ b/ext/asio/asio/deadline_timer.hpp
@@ -2,7 +2,7 @@
// deadline_timer.hpp
// ~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/detail/socket_types.hpp" // Must come before posix_time.
+#include "asio/basic_deadline_timer.hpp"
#include "asio/detail/push_options.hpp"
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include "asio/detail/pop_options.hpp"
-#include "asio/basic_deadline_timer.hpp"
-
namespace asio {
/// Typedef for the typical usage of timer. Uses a UTC clock.
@@ -32,6 +30,4 @@ typedef basic_deadline_timer<boost::posix_time::ptime> deadline_timer;
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DEADLINE_TIMER_HPP
diff --git a/ext/asio/asio/deadline_timer_service.hpp b/ext/asio/asio/deadline_timer_service.hpp
index 284a690..60d898b 100644
--- a/ext/asio/asio/deadline_timer_service.hpp
+++ b/ext/asio/asio/deadline_timer_service.hpp
@@ -2,7 +2,7 @@
// deadline_timer_service.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
+#include "asio/detail/deadline_timer_service.hpp"
#include "asio/io_service.hpp"
#include "asio/time_traits.hpp"
-#include "asio/detail/deadline_timer_service.hpp"
-#include "asio/detail/service_base.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
diff --git a/ext/asio/asio/detail/array_fwd.hpp b/ext/asio/asio/detail/array_fwd.hpp
new file mode 100644
index 0000000..12c295c
--- /dev/null
+++ b/ext/asio/asio/detail/array_fwd.hpp
@@ -0,0 +1,25 @@
+//
+// detail/array_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_ARRAY_FWD_HPP
+#define ASIO_DETAIL_ARRAY_FWD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+namespace boost {
+
+template<class T, std::size_t N>
+class array;
+
+} // namespace boost
+
+#endif // ASIO_DETAIL_ARRAY_FWD_HPP
diff --git a/ext/asio/asio/detail/base_from_completion_cond.hpp b/ext/asio/asio/detail/base_from_completion_cond.hpp
index 90a11f9..93cc8c4 100644
--- a/ext/asio/asio/detail/base_from_completion_cond.hpp
+++ b/ext/asio/asio/detail/base_from_completion_cond.hpp
@@ -1,8 +1,8 @@
//
-// base_from_completion_cond.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/base_from_completion_cond.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,10 +15,11 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/completion_condition.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -31,7 +32,8 @@ protected:
{
}
- std::size_t check(const asio::error_code& ec,
+ std::size_t check_for_completion(
+ const asio::error_code& ec,
std::size_t total_transferred)
{
return detail::adapt_completion_condition_result(
@@ -50,7 +52,8 @@ protected:
{
}
- static std::size_t check(const asio::error_code& ec,
+ static std::size_t check_for_completion(
+ const asio::error_code& ec,
std::size_t total_transferred)
{
return transfer_all_t()(ec, total_transferred);
diff --git a/ext/asio/asio/detail/bind_handler.hpp b/ext/asio/asio/detail/bind_handler.hpp
index 5d9eb00..f663a5b 100644
--- a/ext/asio/asio/detail/bind_handler.hpp
+++ b/ext/asio/asio/detail/bind_handler.hpp
@@ -1,8 +1,8 @@
//
-// bind_handler.hpp
-// ~~~~~~~~~~~~~~~~
+// detail/bind_handler.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,11 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/detail/handler_alloc_helpers.hpp"
#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -35,7 +36,7 @@ public:
void operator()()
{
- handler_(arg1_);
+ handler_(static_cast<const Arg1&>(arg1_));
}
void operator()() const
@@ -92,7 +93,8 @@ public:
void operator()()
{
- handler_(arg1_, arg2_);
+ handler_(static_cast<const Arg1&>(arg1_),
+ static_cast<const Arg2&>(arg2_));
}
void operator()() const
@@ -152,7 +154,9 @@ public:
void operator()()
{
- handler_(arg1_, arg2_, arg3_);
+ handler_(static_cast<const Arg1&>(arg1_),
+ static_cast<const Arg2&>(arg2_),
+ static_cast<const Arg3&>(arg3_));
}
void operator()() const
@@ -216,7 +220,10 @@ public:
void operator()()
{
- handler_(arg1_, arg2_, arg3_, arg4_);
+ handler_(static_cast<const Arg1&>(arg1_),
+ static_cast<const Arg2&>(arg2_),
+ static_cast<const Arg3&>(arg3_),
+ static_cast<const Arg4&>(arg4_));
}
void operator()() const
@@ -287,7 +294,11 @@ public:
void operator()()
{
- handler_(arg1_, arg2_, arg3_, arg4_, arg5_);
+ handler_(static_cast<const Arg1&>(arg1_),
+ static_cast<const Arg2&>(arg2_),
+ static_cast<const Arg3&>(arg3_),
+ static_cast<const Arg4&>(arg4_),
+ static_cast<const Arg5&>(arg5_));
}
void operator()() const
diff --git a/ext/asio/asio/detail/buffer_resize_guard.hpp b/ext/asio/asio/detail/buffer_resize_guard.hpp
index 368de32..4bf6a8e 100644
--- a/ext/asio/asio/detail/buffer_resize_guard.hpp
+++ b/ext/asio/asio/detail/buffer_resize_guard.hpp
@@ -1,8 +1,8 @@
//
-// buffer_resize_guard.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~
+// detail/buffer_resize_guard.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,12 +15,10 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+#include <boost/limits.hpp>
#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include <boost/limits.hpp>
-#include "asio/detail/pop_options.hpp"
namespace asio {
namespace detail {
diff --git a/ext/asio/asio/detail/buffer_sequence_adapter.hpp b/ext/asio/asio/detail/buffer_sequence_adapter.hpp
index 6269d6a..17044b7 100644
--- a/ext/asio/asio/detail/buffer_sequence_adapter.hpp
+++ b/ext/asio/asio/detail/buffer_sequence_adapter.hpp
@@ -1,8 +1,8 @@
//
-// buffer_sequence_adapter.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/buffer_sequence_adapter.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,9 +15,11 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/buffer.hpp"
+#include "asio/detail/socket_types.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
@@ -32,14 +34,14 @@ protected:
const asio::mutable_buffer& buffer)
{
buf.buf = asio::buffer_cast<char*>(buffer);
- buf.len = asio::buffer_size(buffer);
+ buf.len = static_cast<ULONG>(asio::buffer_size(buffer));
}
static void init_native_buffer(WSABUF& buf,
const asio::const_buffer& buffer)
{
buf.buf = const_cast<char*>(asio::buffer_cast<const char*>(buffer));
- buf.len = asio::buffer_size(buffer);
+ buf.len = static_cast<ULONG>(asio::buffer_size(buffer));
}
#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
typedef iovec native_buffer_type;
@@ -158,7 +160,7 @@ public:
explicit buffer_sequence_adapter(
const asio::mutable_buffers_1& buffers)
{
- init_native_buffer(buffer_, buffers);
+ init_native_buffer(buffer_, Buffer(buffers));
total_buffer_size_ = asio::buffer_size(buffers);
}
@@ -205,7 +207,7 @@ public:
explicit buffer_sequence_adapter(
const asio::const_buffers_1& buffers)
{
- init_native_buffer(buffer_, buffers);
+ init_native_buffer(buffer_, Buffer(buffers));
total_buffer_size_ = asio::buffer_size(buffers);
}
diff --git a/ext/asio/asio/detail/buffered_stream_storage.hpp b/ext/asio/asio/detail/buffered_stream_storage.hpp
index 51f6556..7e1b746 100644
--- a/ext/asio/asio/detail/buffered_stream_storage.hpp
+++ b/ext/asio/asio/detail/buffered_stream_storage.hpp
@@ -1,8 +1,8 @@
//
-// buffered_stream_storage.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/buffered_stream_storage.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,15 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
+#include "asio/detail/config.hpp"
#include <cassert>
#include <cstddef>
#include <cstring>
#include <vector>
-#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
diff --git a/ext/asio/asio/detail/call_stack.hpp b/ext/asio/asio/detail/call_stack.hpp
index 0e9ddaf..7ad5dc5 100644
--- a/ext/asio/asio/detail/call_stack.hpp
+++ b/ext/asio/asio/detail/call_stack.hpp
@@ -1,8 +1,8 @@
//
-// call_stack.hpp
-// ~~~~~~~~~~~~~~
+// detail/call_stack.hpp
+// ~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,11 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/tss_ptr.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
diff --git a/ext/asio/asio/detail/completion_handler.hpp b/ext/asio/asio/detail/completion_handler.hpp
index 16167df..0224d7e 100644
--- a/ext/asio/asio/detail/completion_handler.hpp
+++ b/ext/asio/asio/detail/completion_handler.hpp
@@ -1,8 +1,8 @@
//
-// completion_handler.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~
+// detail/completion_handler.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,13 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/detail/fenced_block.hpp"
#include "asio/detail/handler_alloc_helpers.hpp"
#include "asio/detail/handler_invoke_helpers.hpp"
#include "asio/detail/operation.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -29,6 +30,8 @@ template <typename Handler>
class completion_handler : public operation
{
public:
+ ASIO_DEFINE_HANDLER_PTR(completion_handler);
+
completion_handler(Handler h)
: operation(&completion_handler::do_complete),
handler_(h)
@@ -40,20 +43,21 @@ public:
{
// Take ownership of the handler object.
completion_handler* h(static_cast<completion_handler*>(base));
- typedef handler_alloc_traits<Handler, completion_handler> alloc_traits;
- handler_ptr<alloc_traits> ptr(h->handler_, h);
+ ptr p = { boost::addressof(h->handler_), h, h };
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ Handler handler(h->handler_);
+ p.h = boost::addressof(handler);
+ p.reset();
// Make the upcall if required.
if (owner)
{
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- Handler handler(h->handler_);
- ptr.reset();
asio::detail::fenced_block b;
asio_handler_invoke_helpers::invoke(handler, handler);
}
diff --git a/ext/asio/asio/detail/config.hpp b/ext/asio/asio/detail/config.hpp
new file mode 100644
index 0000000..8328d76
--- /dev/null
+++ b/ext/asio/asio/detail/config.hpp
@@ -0,0 +1,205 @@
+//
+// detail/config.hpp
+// ~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_CONFIG_HPP
+#define ASIO_DETAIL_CONFIG_HPP
+
+#include <boost/config.hpp>
+
+// Default to a header-only implementation. The user must specifically request
+// separate compilation by defining either ASIO_SEPARATE_COMPILATION or
+// ASIO_DYN_LINK (as a DLL/shared library implies separate compilation).
+#if !defined(ASIO_HEADER_ONLY)
+# if !defined(ASIO_SEPARATE_COMPILATION)
+# if !defined(ASIO_DYN_LINK)
+# define ASIO_HEADER_ONLY
+# endif // !defined(ASIO_DYN_LINK)
+# endif // !defined(ASIO_SEPARATE_COMPILATION)
+#endif // !defined(ASIO_HEADER_ONLY)
+
+#if defined(ASIO_HEADER_ONLY)
+# define ASIO_DECL inline
+#else // defined(ASIO_HEADER_ONLY)
+# if defined(BOOST_HAS_DECLSPEC)
+// We need to import/export our code only if the user has specifically asked
+// for it by defining ASIO_DYN_LINK.
+# if defined(ASIO_DYN_LINK)
+// Export if this is our own source, otherwise import.
+# if defined(ASIO_SOURCE)
+# define ASIO_DECL __declspec(dllexport)
+# else // defined(ASIO_SOURCE)
+# define ASIO_DECL __declspec(dllimport)
+# endif // defined(ASIO_SOURCE)
+# endif // defined(ASIO_DYN_LINK)
+# endif // defined(BOOST_HAS_DECLSPEC)
+#endif // defined(ASIO_HEADER_ONLY)
+
+// If ASIO_DECL isn't defined yet define it now.
+#if !defined(ASIO_DECL)
+# define ASIO_DECL
+#endif // !defined(ASIO_DECL)
+
+// Windows: target OS version.
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# if !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS)
+# if defined(_MSC_VER) || defined(__BORLANDC__)
+# pragma message( \
+ "Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. For example:\n"\
+ "- add -D_WIN32_WINNT=0x0501 to the compiler command line; or\n"\
+ "- add _WIN32_WINNT=0x0501 to your project's Preprocessor Definitions.\n"\
+ "Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target).")
+# else // defined(_MSC_VER) || defined(__BORLANDC__)
+# warning Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately.
+# warning For example, add -D_WIN32_WINNT=0x0501 to the compiler command line.
+# warning Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target).
+# endif // defined(_MSC_VER) || defined(__BORLANDC__)
+# define _WIN32_WINNT 0x0501
+# endif // !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS)
+# if defined(_MSC_VER)
+# if defined(_WIN32) && !defined(WIN32)
+# if !defined(_WINSOCK2API_)
+# define WIN32 // Needed for correct types in winsock2.h
+# else // !defined(_WINSOCK2API_)
+# error Please define the macro WIN32 in your compiler options
+# endif // !defined(_WINSOCK2API_)
+# endif // defined(_WIN32) && !defined(WIN32)
+# endif // defined(_MSC_VER)
+# if defined(__BORLANDC__)
+# if defined(__WIN32__) && !defined(WIN32)
+# if !defined(_WINSOCK2API_)
+# define WIN32 // Needed for correct types in winsock2.h
+# else // !defined(_WINSOCK2API_)
+# error Please define the macro WIN32 in your compiler options
+# endif // !defined(_WINSOCK2API_)
+# endif // defined(__WIN32__) && !defined(WIN32)
+# endif // defined(__BORLANDC__)
+# if defined(__CYGWIN__)
+# if !defined(__USE_W32_SOCKETS)
+# error You must add -D__USE_W32_SOCKETS to your compiler options.
+# endif // !defined(__USE_W32_SOCKETS)
+# endif // defined(__CYGWIN__)
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+// Windows: minimise header inclusion.
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# if !defined(ASIO_NO_WIN32_LEAN_AND_MEAN)
+# if !defined(WIN32_LEAN_AND_MEAN)
+# define WIN32_LEAN_AND_MEAN
+# endif // !defined(WIN32_LEAN_AND_MEAN)
+# endif // !defined(ASIO_NO_WIN32_LEAN_AND_MEAN)
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+// Windows: suppress definition of "min" and "max" macros.
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# if !defined(ASIO_NO_NOMINMAX)
+# if !defined(NOMINMAX)
+# define NOMINMAX 1
+# endif // !defined(NOMINMAX)
+# endif // !defined(ASIO_NO_NOMINMAX)
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+// Windows: IO Completion Ports.
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400)
+# if !defined(UNDER_CE)
+# if !defined(ASIO_DISABLE_IOCP)
+# define ASIO_HAS_IOCP 1
+# endif // !defined(ASIO_DISABLE_IOCP)
+# endif // !defined(UNDER_CE)
+# endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400)
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+// Linux: epoll, eventfd and timerfd.
+#if defined(__linux__)
+# include <linux/version.h>
+# if !defined(ASIO_DISABLE_EPOLL)
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45)
+# define ASIO_HAS_EPOLL 1
+# endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45)
+# endif // !defined(ASIO_DISABLE_EVENTFD)
+# if !defined(ASIO_DISABLE_EVENTFD)
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
+# define ASIO_HAS_EVENTFD 1
+# endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
+# endif // !defined(ASIO_DISABLE_EVENTFD)
+# if defined(ASIO_HAS_EPOLL)
+# if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)
+# define ASIO_HAS_TIMERFD 1
+# endif // (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)
+# endif // defined(ASIO_HAS_EPOLL)
+#endif // defined(__linux__)
+
+// Mac OS X, FreeBSD, NetBSD, OpenBSD: kqueue.
+#if (defined(__MACH__) && defined(__APPLE__)) \
+ || defined(__FreeBSD__) \
+ || defined(__NetBSD__) \
+ || defined(__OpenBSD__)
+# if !defined(ASIO_DISABLE_KQUEUE)
+# define ASIO_HAS_KQUEUE 1
+# endif // !defined(ASIO_DISABLE_KQUEUE)
+#endif // (defined(__MACH__) && defined(__APPLE__))
+ // || defined(__FreeBSD__)
+ // || defined(__NetBSD__)
+ // || defined(__OpenBSD__)
+
+// Solaris: /dev/poll.
+#if defined(__sun)
+# if !defined(ASIO_DISABLE_DEV_POLL)
+# define ASIO_HAS_DEV_POLL 1
+# endif // !defined(ASIO_DISABLE_DEV_POLL)
+#endif // defined(__sun)
+
+// Serial ports.
+#if defined(ASIO_HAS_IOCP) \
+ || !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+# if !defined(__SYMBIAN32__)
+# if !defined(ASIO_DISABLE_SERIAL_PORT)
+# define ASIO_HAS_SERIAL_PORT 1
+# endif // !defined(ASIO_DISABLE_SERIAL_PORT)
+# endif // !defined(__SYMBIAN32__)
+#endif // defined(ASIO_HAS_IOCP)
+ // || !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
+// Windows: stream handles.
+#if !defined(ASIO_DISABLE_WINDOWS_STREAM_HANDLE)
+# if defined(ASIO_HAS_IOCP)
+# define ASIO_HAS_WINDOWS_STREAM_HANDLE 1
+# endif // defined(ASIO_HAS_IOCP)
+#endif // !defined(ASIO_DISABLE_WINDOWS_STREAM_HANDLE)
+
+// Windows: random access handles.
+#if !defined(ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE)
+# if defined(ASIO_HAS_IOCP)
+# define ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE 1
+# endif // defined(ASIO_HAS_IOCP)
+#endif // !defined(ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE)
+
+// Windows: OVERLAPPED wrapper.
+#if !defined(ASIO_DISABLE_WINDOWS_OVERLAPPED_PTR)
+# if defined(ASIO_HAS_IOCP)
+# define ASIO_HAS_WINDOWS_OVERLAPPED_PTR 1
+# endif // defined(ASIO_HAS_IOCP)
+#endif // !defined(ASIO_DISABLE_WINDOWS_OVERLAPPED_PTR)
+
+// POSIX: stream-oriented file descriptors.
+#if !defined(ASIO_DISABLE_POSIX_STREAM_DESCRIPTOR)
+# if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+# define ASIO_HAS_POSIX_STREAM_DESCRIPTOR 1
+# endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+#endif // !defined(ASIO_DISABLE_POSIX_STREAM_DESCRIPTOR)
+
+// UNIX domain sockets.
+#if !defined(ASIO_DISABLE_LOCAL_SOCKETS)
+# if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+# define ASIO_HAS_LOCAL_SOCKETS 1
+# endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+#endif // !defined(ASIO_DISABLE_LOCAL_SOCKETS)
+
+#endif // ASIO_DETAIL_CONFIG_HPP
diff --git a/ext/asio/asio/detail/consuming_buffers.hpp b/ext/asio/asio/detail/consuming_buffers.hpp
index 80b6a8e..8969ae4 100644
--- a/ext/asio/asio/detail/consuming_buffers.hpp
+++ b/ext/asio/asio/detail/consuming_buffers.hpp
@@ -1,8 +1,8 @@
//
-// consuming_buffers.hpp
-// ~~~~~~~~~~~~~~~~~~~~~
+// detail/consuming_buffers.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,18 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <algorithm>
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
#include <boost/iterator.hpp>
#include <boost/limits.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/buffer.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -156,12 +152,14 @@ public:
consuming_buffers(const Buffers& buffers)
: buffers_(buffers),
at_end_(buffers_.begin() == buffers_.end()),
- first_(*buffers_.begin()),
begin_remainder_(buffers_.begin()),
max_size_((std::numeric_limits<std::size_t>::max)())
{
if (!at_end_)
+ {
+ first_ = *buffers_.begin();
++begin_remainder_;
+ }
}
// Copy constructor.
diff --git a/ext/asio/asio/detail/deadline_timer_service.hpp b/ext/asio/asio/detail/deadline_timer_service.hpp
index 6daf7ac..782f5c3 100644
--- a/ext/asio/asio/detail/deadline_timer_service.hpp
+++ b/ext/asio/asio/detail/deadline_timer_service.hpp
@@ -1,8 +1,8 @@
//
-// deadline_timer_service.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/deadline_timer_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,14 +15,8 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/error.hpp"
#include "asio/io_service.hpp"
#include "asio/detail/bind_handler.hpp"
@@ -33,6 +27,13 @@
#include "asio/detail/timer_op.hpp"
#include "asio/detail/timer_queue.hpp"
#include "asio/detail/timer_scheduler.hpp"
+#include "asio/detail/wait_handler.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
@@ -54,6 +55,7 @@ public:
{
time_type expiry;
bool might_have_pending_waits;
+ typename timer_queue<Time_Traits>::per_timer_data timer_data;
};
// Constructor.
@@ -97,7 +99,7 @@ public:
ec = asio::error_code();
return 0;
}
- std::size_t count = scheduler_.cancel_timer(timer_queue_, &impl);
+ std::size_t count = scheduler_.cancel_timer(timer_queue_, impl.timer_data);
impl.might_have_pending_waits = false;
ec = asio::error_code();
return count;
@@ -151,59 +153,21 @@ public:
ec = asio::error_code();
}
- template <typename Handler>
- class wait_handler : public timer_op
- {
- public:
- wait_handler(Handler handler)
- : timer_op(&wait_handler::do_complete),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
- {
- // Take ownership of the handler object.
- wait_handler* h(static_cast<wait_handler*>(base));
- typedef handler_alloc_traits<Handler, wait_handler> alloc_traits;
- handler_ptr<alloc_traits> ptr(h->handler_, h);
-
- // Make the upcall if required.
- if (owner)
- {
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder1<Handler, asio::error_code>
- handler(h->handler_, h->ec_);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- Handler handler_;
- };
-
// Start an asynchronous wait on the timer.
template <typename Handler>
void async_wait(implementation_type& impl, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef wait_handler<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, handler);
+ typedef wait_handler<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(handler);
impl.might_have_pending_waits = true;
- scheduler_.schedule_timer(timer_queue_, impl.expiry, ptr.get(), &impl);
- ptr.release();
+ scheduler_.schedule_timer(timer_queue_, impl.expiry, impl.timer_data, p.p);
+ p.v = p.p = 0;
}
private:
diff --git a/ext/asio/asio/detail/descriptor_ops.hpp b/ext/asio/asio/detail/descriptor_ops.hpp
index ea3731e..5cac91d 100644
--- a/ext/asio/asio/detail/descriptor_ops.hpp
+++ b/ext/asio/asio/detail/descriptor_ops.hpp
@@ -1,8 +1,8 @@
//
-// descriptor_ops.hpp
-// ~~~~~~~~~~~~~~~~~~
+// detail/descriptor_ops.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,27 +15,34 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include <cerrno>
-#include "asio/detail/pop_options.hpp"
+#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
-#include "asio/error.hpp"
+#include <cstddef>
+#include "asio/error_code.hpp"
#include "asio/detail/socket_types.hpp"
-#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
namespace descriptor_ops {
-inline void clear_error(asio::error_code& ec)
+// Descriptor state bits.
+enum
{
- errno = 0;
- ec = asio::error_code();
-}
+ // The user wants a non-blocking descriptor.
+ user_set_non_blocking = 1,
+
+ // The descriptor has been set non-blocking.
+ internal_non_blocking = 2,
+
+ // Helper "state" used to determine whether the descriptor is non-blocking.
+ non_blocking = user_set_non_blocking | internal_non_blocking
+};
+
+typedef unsigned char state_type;
template <typename ReturnType>
inline ReturnType error_wrapper(ReturnType return_value,
@@ -46,131 +53,53 @@ inline ReturnType error_wrapper(ReturnType return_value,
return return_value;
}
-inline int open(const char* path, int flags, asio::error_code& ec)
-{
- clear_error(ec);
- int result = error_wrapper(::open(path, flags), ec);
- if (result >= 0)
- clear_error(ec);
- return result;
-}
+ASIO_DECL int open(const char* path, int flags,
+ asio::error_code& ec);
-inline int close(int d, asio::error_code& ec)
-{
- clear_error(ec);
- int result = error_wrapper(::close(d), ec);
- if (result == 0)
- clear_error(ec);
- return result;
-}
+ASIO_DECL int close(int d, state_type& state,
+ asio::error_code& ec);
-inline void init_buf_iov_base(void*& base, void* addr)
-{
- base = addr;
-}
-
-template <typename T>
-inline void init_buf_iov_base(T& base, void* addr)
-{
- base = static_cast<T>(addr);
-}
+ASIO_DECL bool set_internal_non_blocking(int d,
+ state_type& state, asio::error_code& ec);
typedef iovec buf;
-inline void init_buf(buf& b, void* data, size_t size)
-{
- init_buf_iov_base(b.iov_base, data);
- b.iov_len = size;
-}
+ASIO_DECL std::size_t sync_read(int d, state_type state, buf* bufs,
+ std::size_t count, bool all_empty, asio::error_code& ec);
-inline void init_buf(buf& b, const void* data, size_t size)
-{
- init_buf_iov_base(b.iov_base, const_cast<void*>(data));
- b.iov_len = size;
-}
+ASIO_DECL bool non_blocking_read(int d, buf* bufs, std::size_t count,
+ asio::error_code& ec, std::size_t& bytes_transferred);
-inline int scatter_read(int d, buf* bufs, size_t count,
- asio::error_code& ec)
-{
- clear_error(ec);
- int result = error_wrapper(::readv(d, bufs, static_cast<int>(count)), ec);
- if (result >= 0)
- clear_error(ec);
- return result;
-}
+ASIO_DECL std::size_t sync_write(int d, state_type state,
+ const buf* bufs, std::size_t count, bool all_empty,
+ asio::error_code& ec);
-inline int gather_write(int d, const buf* bufs, size_t count,
- asio::error_code& ec)
-{
- clear_error(ec);
- int result = error_wrapper(::writev(d, bufs, static_cast<int>(count)), ec);
- if (result >= 0)
- clear_error(ec);
- return result;
-}
+ASIO_DECL bool non_blocking_write(int d,
+ const buf* bufs, std::size_t count,
+ asio::error_code& ec, std::size_t& bytes_transferred);
-inline int ioctl(int d, long cmd, ioctl_arg_type* arg,
- asio::error_code& ec)
-{
- clear_error(ec);
- int result = error_wrapper(::ioctl(d, cmd, arg), ec);
- if (result >= 0)
- clear_error(ec);
- return result;
-}
+ASIO_DECL int ioctl(int d, state_type& state, long cmd,
+ ioctl_arg_type* arg, asio::error_code& ec);
-inline int fcntl(int d, long cmd, asio::error_code& ec)
-{
- clear_error(ec);
- int result = error_wrapper(::fcntl(d, cmd), ec);
- if (result != -1)
- clear_error(ec);
- return result;
-}
+ASIO_DECL int fcntl(int d, long cmd, asio::error_code& ec);
-inline int fcntl(int d, long cmd, long arg, asio::error_code& ec)
-{
- clear_error(ec);
- int result = error_wrapper(::fcntl(d, cmd, arg), ec);
- if (result != -1)
- clear_error(ec);
- return result;
-}
+ASIO_DECL int fcntl(int d, long cmd,
+ long arg, asio::error_code& ec);
-inline int poll_read(int d, asio::error_code& ec)
-{
- clear_error(ec);
- pollfd fds;
- fds.fd = d;
- fds.events = POLLIN;
- fds.revents = 0;
- clear_error(ec);
- int result = error_wrapper(::poll(&fds, 1, -1), ec);
- if (result >= 0)
- clear_error(ec);
- return result;
-}
+ASIO_DECL int poll_read(int d, asio::error_code& ec);
-inline int poll_write(int d, asio::error_code& ec)
-{
- clear_error(ec);
- pollfd fds;
- fds.fd = d;
- fds.events = POLLOUT;
- fds.revents = 0;
- clear_error(ec);
- int result = error_wrapper(::poll(&fds, 1, -1), ec);
- if (result >= 0)
- clear_error(ec);
- return result;
-}
+ASIO_DECL int poll_write(int d, asio::error_code& ec);
} // namespace descriptor_ops
} // namespace detail
} // namespace asio
-#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
-
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/descriptor_ops.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
#endif // ASIO_DETAIL_DESCRIPTOR_OPS_HPP
diff --git a/ext/asio/asio/detail/descriptor_read_op.hpp b/ext/asio/asio/detail/descriptor_read_op.hpp
new file mode 100644
index 0000000..0d45167
--- /dev/null
+++ b/ext/asio/asio/detail/descriptor_read_op.hpp
@@ -0,0 +1,114 @@
+//
+// detail/descriptor_read_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_DESCRIPTOR_READ_OP_HPP
+#define ASIO_DETAIL_DESCRIPTOR_READ_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/descriptor_ops.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/reactor_op.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename MutableBufferSequence>
+class descriptor_read_op_base : public reactor_op
+{
+public:
+ descriptor_read_op_base(int descriptor,
+ const MutableBufferSequence& buffers, func_type complete_func)
+ : reactor_op(&descriptor_read_op_base::do_perform, complete_func),
+ descriptor_(descriptor),
+ buffers_(buffers)
+ {
+ }
+
+ static bool do_perform(reactor_op* base)
+ {
+ descriptor_read_op_base* o(static_cast<descriptor_read_op_base*>(base));
+
+ buffer_sequence_adapter<asio::mutable_buffer,
+ MutableBufferSequence> bufs(o->buffers_);
+
+ return descriptor_ops::non_blocking_read(o->descriptor_,
+ bufs.buffers(), bufs.count(), o->ec_, o->bytes_transferred_);
+ }
+
+private:
+ int descriptor_;
+ MutableBufferSequence buffers_;
+};
+
+template <typename MutableBufferSequence, typename Handler>
+class descriptor_read_op
+ : public descriptor_read_op_base<MutableBufferSequence>
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(descriptor_read_op);
+
+ descriptor_read_op(int descriptor,
+ const MutableBufferSequence& buffers, Handler handler)
+ : descriptor_read_op_base<MutableBufferSequence>(
+ descriptor, buffers, &descriptor_read_op::do_complete),
+ handler_(handler)
+ {
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
+ {
+ // Take ownership of the handler object.
+ descriptor_read_op* o(static_cast<descriptor_read_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder2<Handler, asio::error_code, std::size_t>
+ handler(o->handler_, o->ec_, o->bytes_transferred_);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
+#endif // ASIO_DETAIL_DESCRIPTOR_READ_OP_HPP
diff --git a/ext/asio/asio/detail/descriptor_write_op.hpp b/ext/asio/asio/detail/descriptor_write_op.hpp
new file mode 100644
index 0000000..9caa283
--- /dev/null
+++ b/ext/asio/asio/detail/descriptor_write_op.hpp
@@ -0,0 +1,114 @@
+//
+// detail/descriptor_write_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_DESCRIPTOR_WRITE_OP_HPP
+#define ASIO_DETAIL_DESCRIPTOR_WRITE_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/descriptor_ops.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/reactor_op.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename ConstBufferSequence>
+class descriptor_write_op_base : public reactor_op
+{
+public:
+ descriptor_write_op_base(int descriptor,
+ const ConstBufferSequence& buffers, func_type complete_func)
+ : reactor_op(&descriptor_write_op_base::do_perform, complete_func),
+ descriptor_(descriptor),
+ buffers_(buffers)
+ {
+ }
+
+ static bool do_perform(reactor_op* base)
+ {
+ descriptor_write_op_base* o(static_cast<descriptor_write_op_base*>(base));
+
+ buffer_sequence_adapter<asio::const_buffer,
+ ConstBufferSequence> bufs(o->buffers_);
+
+ return descriptor_ops::non_blocking_write(o->descriptor_,
+ bufs.buffers(), bufs.count(), o->ec_, o->bytes_transferred_);
+ }
+
+private:
+ int descriptor_;
+ ConstBufferSequence buffers_;
+};
+
+template <typename ConstBufferSequence, typename Handler>
+class descriptor_write_op
+ : public descriptor_write_op_base<ConstBufferSequence>
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(descriptor_write_op);
+
+ descriptor_write_op(int descriptor,
+ const ConstBufferSequence& buffers, Handler handler)
+ : descriptor_write_op_base<ConstBufferSequence>(
+ descriptor, buffers, &descriptor_write_op::do_complete),
+ handler_(handler)
+ {
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
+ {
+ // Take ownership of the handler object.
+ descriptor_write_op* o(static_cast<descriptor_write_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder2<Handler, asio::error_code, std::size_t>
+ handler(o->handler_, o->ec_, o->bytes_transferred_);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
+#endif // ASIO_DETAIL_DESCRIPTOR_WRITE_OP_HPP
diff --git a/ext/asio/asio/detail/dev_poll_reactor.hpp b/ext/asio/asio/detail/dev_poll_reactor.hpp
index 1367b71..6bbf1b2 100644
--- a/ext/asio/asio/detail/dev_poll_reactor.hpp
+++ b/ext/asio/asio/detail/dev_poll_reactor.hpp
@@ -1,8 +1,8 @@
//
-// dev_poll_reactor.hpp
-// ~~~~~~~~~~~~~~~~~~~~
+// detail/dev_poll_reactor.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,36 +15,28 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/dev_poll_reactor_fwd.hpp"
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_DEV_POLL)
-#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <vector>
-#include <boost/config.hpp>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
-#include <boost/throw_exception.hpp>
#include <sys/devpoll.h>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error.hpp"
-#include "asio/io_service.hpp"
-#include "asio/system_error.hpp"
+#include "asio/detail/dev_poll_reactor_fwd.hpp"
#include "asio/detail/hash_map.hpp"
#include "asio/detail/mutex.hpp"
#include "asio/detail/op_queue.hpp"
#include "asio/detail/reactor_op.hpp"
#include "asio/detail/reactor_op_queue.hpp"
#include "asio/detail/select_interrupter.hpp"
-#include "asio/detail/service_base.hpp"
#include "asio/detail/socket_types.hpp"
#include "asio/detail/timer_op.hpp"
#include "asio/detail/timer_queue_base.hpp"
#include "asio/detail/timer_queue_fwd.hpp"
#include "asio/detail/timer_queue_set.hpp"
+#include "asio/io_service.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
@@ -62,358 +54,93 @@ public:
};
// Constructor.
- dev_poll_reactor(asio::io_service& io_service)
- : asio::detail::service_base<dev_poll_reactor>(io_service),
- io_service_(use_service<io_service_impl>(io_service)),
- mutex_(),
- dev_poll_fd_(do_dev_poll_create()),
- interrupter_(),
- shutdown_(false)
- {
- // Add the interrupter's descriptor to /dev/poll.
- ::pollfd ev = { 0 };
- ev.fd = interrupter_.read_descriptor();
- ev.events = POLLIN | POLLERR;
- ev.revents = 0;
- ::write(dev_poll_fd_, &ev, sizeof(ev));
- }
+ ASIO_DECL dev_poll_reactor(asio::io_service& io_service);
// Destructor.
- ~dev_poll_reactor()
- {
- shutdown_service();
- ::close(dev_poll_fd_);
- }
+ ASIO_DECL ~dev_poll_reactor();
// Destroy all user-defined handler objects owned by the service.
- void shutdown_service()
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- shutdown_ = true;
- lock.unlock();
-
- op_queue<operation> ops;
-
- for (int i = 0; i < max_ops; ++i)
- op_queue_[i].get_all_operations(ops);
-
- timer_queues_.get_all_timers(ops);
- }
+ ASIO_DECL void shutdown_service();
// Initialise the task.
- void init_task()
- {
- io_service_.init_task();
- }
+ ASIO_DECL void init_task();
// Register a socket with the reactor. Returns 0 on success, system error
// code on failure.
- int register_descriptor(socket_type, per_descriptor_data&)
+ ASIO_DECL int register_descriptor(socket_type, per_descriptor_data&);
+
+ // Post a reactor operation for immediate completion.
+ void post_immediate_completion(reactor_op* op)
{
- return 0;
+ io_service_.post_immediate_completion(op);
}
// Start a new operation. The reactor operation will be performed when the
// given descriptor is flagged as ready, or an error has occurred.
- void start_op(int op_type, socket_type descriptor,
- per_descriptor_data&, reactor_op* op, bool allow_speculative)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
-
- if (shutdown_)
- return;
-
- if (allow_speculative)
- {
- if (op_type != read_op || !op_queue_[except_op].has_operation(descriptor))
- {
- if (!op_queue_[op_type].has_operation(descriptor))
- {
- if (op->perform())
- {
- lock.unlock();
- io_service_.post_immediate_completion(op);
- return;
- }
- }
- }
- }
-
- bool first = op_queue_[op_type].enqueue_operation(descriptor, op);
- io_service_.work_started();
- if (first)
- {
- ::pollfd& ev = add_pending_event_change(descriptor);
- ev.events = POLLERR | POLLHUP;
- if (op_type == read_op
- || op_queue_[read_op].has_operation(descriptor))
- ev.events |= POLLIN;
- if (op_type == write_op
- || op_queue_[write_op].has_operation(descriptor))
- ev.events |= POLLOUT;
- if (op_type == except_op
- || op_queue_[except_op].has_operation(descriptor))
- ev.events |= POLLPRI;
- interrupter_.interrupt();
- }
- }
+ ASIO_DECL void start_op(int op_type, socket_type descriptor,
+ per_descriptor_data&, reactor_op* op, bool allow_speculative);
// Cancel all operations associated with the given descriptor. The
// handlers associated with the descriptor will be invoked with the
// operation_aborted error.
- void cancel_ops(socket_type descriptor, per_descriptor_data&)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- cancel_ops_unlocked(descriptor, asio::error::operation_aborted);
- }
+ ASIO_DECL void cancel_ops(socket_type descriptor, per_descriptor_data&);
// Cancel any operations that are running against the descriptor and remove
// its registration from the reactor.
- void close_descriptor(socket_type descriptor, per_descriptor_data&)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
-
- // Remove the descriptor from /dev/poll.
- ::pollfd& ev = add_pending_event_change(descriptor);
- ev.events = POLLREMOVE;
- interrupter_.interrupt();
-
- // Cancel any outstanding operations associated with the descriptor.
- cancel_ops_unlocked(descriptor, asio::error::operation_aborted);
- }
+ ASIO_DECL void close_descriptor(
+ socket_type descriptor, per_descriptor_data&);
// Add a new timer queue to the reactor.
template <typename Time_Traits>
- void add_timer_queue(timer_queue<Time_Traits>& timer_queue)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- timer_queues_.insert(&timer_queue);
- }
+ void add_timer_queue(timer_queue<Time_Traits>& queue);
// Remove a timer queue from the reactor.
template <typename Time_Traits>
- void remove_timer_queue(timer_queue<Time_Traits>& timer_queue)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- timer_queues_.erase(&timer_queue);
- }
+ void remove_timer_queue(timer_queue<Time_Traits>& queue);
// Schedule a new operation in the given timer queue to expire at the
// specified absolute time.
template <typename Time_Traits>
- void schedule_timer(timer_queue<Time_Traits>& timer_queue,
- const typename Time_Traits::time_type& time, timer_op* op, void* token)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- if (!shutdown_)
- {
- bool earliest = timer_queue.enqueue_timer(time, op, token);
- io_service_.work_started();
- if (earliest)
- interrupter_.interrupt();
- }
- }
+ void schedule_timer(timer_queue<Time_Traits>& queue,
+ const typename Time_Traits::time_type& time,
+ typename timer_queue<Time_Traits>::per_timer_data& timer, timer_op* op);
// Cancel the timer operations associated with the given token. Returns the
// number of operations that have been posted or dispatched.
template <typename Time_Traits>
- std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- op_queue<operation> ops;
- std::size_t n = timer_queue.cancel_timer(token, ops);
- lock.unlock();
- io_service_.post_deferred_completions(ops);
- return n;
- }
+ std::size_t cancel_timer(timer_queue<Time_Traits>& queue,
+ typename timer_queue<Time_Traits>::per_timer_data& timer);
// Run /dev/poll once until interrupted or events are ready to be dispatched.
- void run(bool block, op_queue<operation>& ops)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
-
- // We can return immediately if there's no work to do and the reactor is
- // not supposed to block.
- if (!block && op_queue_[read_op].empty() && op_queue_[write_op].empty()
- && op_queue_[except_op].empty() && timer_queues_.all_empty())
- return;
-
- // Write the pending event registration changes to the /dev/poll descriptor.
- std::size_t events_size = sizeof(::pollfd) * pending_event_changes_.size();
- if (events_size > 0)
- {
- errno = 0;
- int result = ::write(dev_poll_fd_,
- &pending_event_changes_[0], events_size);
- if (result != static_cast<int>(events_size))
- {
- asio::error_code ec = asio::error_code(
- errno, asio::error::get_system_category());
- for (std::size_t i = 0; i < pending_event_changes_.size(); ++i)
- {
- int descriptor = pending_event_changes_[i].fd;
- for (int j = 0; j < max_ops; ++j)
- op_queue_[j].cancel_operations(descriptor, ops, ec);
- }
- }
- pending_event_changes_.clear();
- pending_event_change_index_.clear();
- }
-
- int timeout = block ? get_timeout() : 0;
- lock.unlock();
-
- // Block on the /dev/poll descriptor.
- ::pollfd events[128] = { { 0 } };
- ::dvpoll dp = { 0 };
- dp.dp_fds = events;
- dp.dp_nfds = 128;
- dp.dp_timeout = timeout;
- int num_events = ::ioctl(dev_poll_fd_, DP_POLL, &dp);
-
- lock.lock();
-
- // Dispatch the waiting events.
- for (int i = 0; i < num_events; ++i)
- {
- int descriptor = events[i].fd;
- if (descriptor == interrupter_.read_descriptor())
- {
- interrupter_.reset();
- }
- else
- {
- bool more_reads = false;
- bool more_writes = false;
- bool more_except = false;
-
- // Exception operations must be processed first to ensure that any
- // out-of-band data is read before normal data.
- if (events[i].events & (POLLPRI | POLLERR | POLLHUP))
- more_except =
- op_queue_[except_op].perform_operations(descriptor, ops);
- else
- more_except = op_queue_[except_op].has_operation(descriptor);
-
- if (events[i].events & (POLLIN | POLLERR | POLLHUP))
- more_reads = op_queue_[read_op].perform_operations(descriptor, ops);
- else
- more_reads = op_queue_[read_op].has_operation(descriptor);
-
- if (events[i].events & (POLLOUT | POLLERR | POLLHUP))
- more_writes = op_queue_[write_op].perform_operations(descriptor, ops);
- else
- more_writes = op_queue_[write_op].has_operation(descriptor);
-
- if ((events[i].events & (POLLERR | POLLHUP)) != 0
- && !more_except && !more_reads && !more_writes)
- {
- // If we have an event and no operations associated with the
- // descriptor then we need to delete the descriptor from /dev/poll.
- // The poll operation can produce POLLHUP or POLLERR events when there
- // is no operation pending, so if we do not remove the descriptor we
- // can end up in a tight polling loop.
- ::pollfd ev = { 0 };
- ev.fd = descriptor;
- ev.events = POLLREMOVE;
- ev.revents = 0;
- ::write(dev_poll_fd_, &ev, sizeof(ev));
- }
- else
- {
- ::pollfd ev = { 0 };
- ev.fd = descriptor;
- ev.events = POLLERR | POLLHUP;
- if (more_reads)
- ev.events |= POLLIN;
- if (more_writes)
- ev.events |= POLLOUT;
- if (more_except)
- ev.events |= POLLPRI;
- ev.revents = 0;
- int result = ::write(dev_poll_fd_, &ev, sizeof(ev));
- if (result != sizeof(ev))
- {
- asio::error_code ec(errno,
- asio::error::get_system_category());
- for (int j = 0; j < max_ops; ++j)
- op_queue_[j].cancel_operations(descriptor, ops, ec);
- }
- }
- }
- }
- timer_queues_.get_ready_timers(ops);
- }
+ ASIO_DECL void run(bool block, op_queue<operation>& ops);
// Interrupt the select loop.
- void interrupt()
- {
- interrupter_.interrupt();
- }
+ ASIO_DECL void interrupt();
private:
// Create the /dev/poll file descriptor. Throws an exception if the descriptor
// cannot be created.
- static int do_dev_poll_create()
- {
- int fd = ::open("/dev/poll", O_RDWR);
- if (fd == -1)
- {
- boost::throw_exception(
- asio::system_error(
- asio::error_code(errno,
- asio::error::get_system_category()),
- "/dev/poll"));
- }
- return fd;
- }
+ ASIO_DECL static int do_dev_poll_create();
+
+ // Helper function to add a new timer queue.
+ ASIO_DECL void do_add_timer_queue(timer_queue_base& queue);
+
+ // Helper function to remove a timer queue.
+ ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue);
// Get the timeout value for the /dev/poll DP_POLL operation. The timeout
// value is returned as a number of milliseconds. A return value of -1
// indicates that the poll should block indefinitely.
- int get_timeout()
- {
- // By default we will wait no longer than 5 minutes. This will ensure that
- // any changes to the system clock are detected after no longer than this.
- return timer_queues_.wait_duration_msec(5 * 60 * 1000);
- }
+ ASIO_DECL int get_timeout();
// Cancel all operations associated with the given descriptor. The do_cancel
// function of the handler objects will be invoked. This function does not
// acquire the dev_poll_reactor's mutex.
- void cancel_ops_unlocked(socket_type descriptor,
- const asio::error_code& ec)
- {
- bool need_interrupt = false;
- op_queue<operation> ops;
- for (int i = 0; i < max_ops; ++i)
- need_interrupt = op_queue_[i].cancel_operations(
- descriptor, ops, ec) || need_interrupt;
- io_service_.post_deferred_completions(ops);
- if (need_interrupt)
- interrupter_.interrupt();
- }
+ ASIO_DECL void cancel_ops_unlocked(socket_type descriptor,
+ const asio::error_code& ec);
// Add a pending event entry for the given descriptor.
- ::pollfd& add_pending_event_change(int descriptor)
- {
- hash_map<int, std::size_t>::iterator iter
- = pending_event_change_index_.find(descriptor);
- if (iter == pending_event_change_index_.end())
- {
- std::size_t index = pending_event_changes_.size();
- pending_event_changes_.reserve(pending_event_changes_.size() + 1);
- pending_event_change_index_.insert(std::make_pair(descriptor, index));
- pending_event_changes_.push_back(::pollfd());
- pending_event_changes_[index].fd = descriptor;
- pending_event_changes_[index].revents = 0;
- return pending_event_changes_[index];
- }
- else
- {
- return pending_event_changes_[iter->second];
- }
- }
+ ASIO_DECL ::pollfd& add_pending_event_change(int descriptor);
// The io_service implementation used to post completions.
io_service_impl& io_service_;
@@ -446,8 +173,13 @@ private:
} // namespace detail
} // namespace asio
-#endif // defined(ASIO_HAS_DEV_POLL)
-
#include "asio/detail/pop_options.hpp"
+#include "asio/detail/impl/dev_poll_reactor.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/dev_poll_reactor.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(ASIO_HAS_DEV_POLL)
+
#endif // ASIO_DETAIL_DEV_POLL_REACTOR_HPP
diff --git a/ext/asio/asio/detail/dev_poll_reactor_fwd.hpp b/ext/asio/asio/detail/dev_poll_reactor_fwd.hpp
index f7f1aeb..c54f5e1 100644
--- a/ext/asio/asio/detail/dev_poll_reactor_fwd.hpp
+++ b/ext/asio/asio/detail/dev_poll_reactor_fwd.hpp
@@ -1,8 +1,8 @@
//
-// dev_poll_reactor_fwd.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/dev_poll_reactor_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,13 +15,9 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#if !defined(ASIO_DISABLE_DEV_POLL)
-#if defined(__sun) // This service is only supported on Solaris.
-
-// Define this to indicate that /dev/poll is supported on the target platform.
-#define ASIO_HAS_DEV_POLL 1
+#if defined(ASIO_HAS_DEV_POLL)
namespace asio {
namespace detail {
@@ -31,9 +27,6 @@ class dev_poll_reactor;
} // namespace detail
} // namespace asio
-#endif // defined(__sun)
-#endif // !defined(ASIO_DISABLE_DEV_POLL)
-
-#include "asio/detail/pop_options.hpp"
+#endif // defined(ASIO_HAS_DEV_POLL)
#endif // ASIO_DETAIL_DEV_POLL_REACTOR_FWD_HPP
diff --git a/ext/asio/asio/detail/epoll_reactor.hpp b/ext/asio/asio/detail/epoll_reactor.hpp
index 0a1eac0..a085766 100644
--- a/ext/asio/asio/detail/epoll_reactor.hpp
+++ b/ext/asio/asio/detail/epoll_reactor.hpp
@@ -1,8 +1,8 @@
//
-// epoll_reactor.hpp
-// ~~~~~~~~~~~~~~~~~
+// detail/epoll_reactor.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,43 +15,24 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/epoll_reactor_fwd.hpp"
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_EPOLL)
-#include "asio/detail/push_options.hpp"
-#include <cstddef>
-#include <sys/epoll.h>
-#include <boost/config.hpp>
-#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error.hpp"
#include "asio/io_service.hpp"
-#include "asio/system_error.hpp"
-#include "asio/detail/hash_map.hpp"
+#include "asio/detail/epoll_reactor_fwd.hpp"
#include "asio/detail/mutex.hpp"
+#include "asio/detail/object_pool.hpp"
#include "asio/detail/op_queue.hpp"
#include "asio/detail/reactor_op.hpp"
#include "asio/detail/select_interrupter.hpp"
-#include "asio/detail/service_base.hpp"
#include "asio/detail/socket_types.hpp"
#include "asio/detail/timer_op.hpp"
#include "asio/detail/timer_queue_base.hpp"
#include "asio/detail/timer_queue_fwd.hpp"
#include "asio/detail/timer_queue_set.hpp"
-#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)
-# define ASIO_HAS_TIMERFD 1
-#endif // (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)
-
-#if defined(ASIO_HAS_TIMERFD)
-# include "asio/detail/push_options.hpp"
-# include <sys/timerfd.h>
-# include "asio/detail/pop_options.hpp"
-#endif // defined(ASIO_HAS_TIMERFD)
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
@@ -64,357 +45,86 @@ public:
connect_op = 1, except_op = 2, max_ops = 3 };
// Per-descriptor queues.
- struct descriptor_state
+ class descriptor_state
{
- descriptor_state() {}
- descriptor_state(const descriptor_state&) {}
- void operator=(const descriptor_state&) {}
-
+ friend class epoll_reactor;
+ friend class object_pool_access;
mutex mutex_;
op_queue<reactor_op> op_queue_[max_ops];
bool shutdown_;
+ descriptor_state* next_;
+ descriptor_state* prev_;
};
// Per-descriptor data.
typedef descriptor_state* per_descriptor_data;
// Constructor.
- epoll_reactor(asio::io_service& io_service)
- : asio::detail::service_base<epoll_reactor>(io_service),
- io_service_(use_service<io_service_impl>(io_service)),
- mutex_(),
- epoll_fd_(do_epoll_create()),
-#if defined(ASIO_HAS_TIMERFD)
- timer_fd_(timerfd_create(CLOCK_MONOTONIC, 0)),
-#else // defined(ASIO_HAS_TIMERFD)
- timer_fd_(-1),
-#endif // defined(ASIO_HAS_TIMERFD)
- interrupter_(),
- shutdown_(false)
- {
- // Add the interrupter's descriptor to epoll.
- epoll_event ev = { 0, { 0 } };
- ev.events = EPOLLIN | EPOLLERR | EPOLLET;
- ev.data.ptr = &interrupter_;
- epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev);
- interrupter_.interrupt();
-
- // Add the timer descriptor to epoll.
- if (timer_fd_ != -1)
- {
- ev.events = EPOLLIN | EPOLLERR;
- ev.data.ptr = &timer_fd_;
- epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, timer_fd_, &ev);
- }
- }
+ ASIO_DECL epoll_reactor(asio::io_service& io_service);
// Destructor.
- ~epoll_reactor()
- {
- close(epoll_fd_);
- if (timer_fd_ != -1)
- close(timer_fd_);
- }
+ ASIO_DECL ~epoll_reactor();
// Destroy all user-defined handler objects owned by the service.
- void shutdown_service()
- {
- mutex::scoped_lock lock(mutex_);
- shutdown_ = true;
- lock.unlock();
-
- op_queue<operation> ops;
-
- descriptor_map::iterator iter = registered_descriptors_.begin();
- descriptor_map::iterator end = registered_descriptors_.end();
- while (iter != end)
- {
- for (int i = 0; i < max_ops; ++i)
- ops.push(iter->second.op_queue_[i]);
- iter->second.shutdown_ = true;
- ++iter;
- }
-
- timer_queues_.get_all_timers(ops);
- }
+ ASIO_DECL void shutdown_service();
// Initialise the task.
- void init_task()
- {
- io_service_.init_task();
- }
+ ASIO_DECL void init_task();
// Register a socket with the reactor. Returns 0 on success, system error
// code on failure.
- int register_descriptor(socket_type descriptor,
- per_descriptor_data& descriptor_data)
- {
- mutex::scoped_lock lock(registered_descriptors_mutex_);
-
- descriptor_map::iterator new_entry = registered_descriptors_.insert(
- std::make_pair(descriptor, descriptor_state())).first;
- descriptor_data = &new_entry->second;
-
- epoll_event ev = { 0, { 0 } };
- ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLOUT | EPOLLPRI | EPOLLET;
- ev.data.ptr = descriptor_data;
- int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev);
- if (result != 0)
- return errno;
-
- descriptor_data->shutdown_ = false;
+ ASIO_DECL int register_descriptor(socket_type descriptor,
+ per_descriptor_data& descriptor_data);
- return 0;
+ // Post a reactor operation for immediate completion.
+ void post_immediate_completion(reactor_op* op)
+ {
+ io_service_.post_immediate_completion(op);
}
// Start a new operation. The reactor operation will be performed when the
// given descriptor is flagged as ready, or an error has occurred.
- void start_op(int op_type, socket_type descriptor,
+ ASIO_DECL void start_op(int op_type, socket_type descriptor,
per_descriptor_data& descriptor_data,
- reactor_op* op, bool allow_speculative)
- {
- mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
- if (descriptor_data->shutdown_)
- return;
-
- if (descriptor_data->op_queue_[op_type].empty())
- {
- if (allow_speculative
- && (op_type != read_op
- || descriptor_data->op_queue_[except_op].empty()))
- {
- if (op->perform())
- {
- descriptor_lock.unlock();
- io_service_.post_immediate_completion(op);
- return;
- }
- }
- else
- {
- epoll_event ev = { 0, { 0 } };
- ev.events = EPOLLIN | EPOLLERR | EPOLLHUP
- | EPOLLOUT | EPOLLPRI | EPOLLET;
- ev.data.ptr = descriptor_data;
- epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
- }
- }
-
- descriptor_data->op_queue_[op_type].push(op);
- io_service_.work_started();
- }
+ reactor_op* op, bool allow_speculative);
// Cancel all operations associated with the given descriptor. The
// handlers associated with the descriptor will be invoked with the
// operation_aborted error.
- void cancel_ops(socket_type, per_descriptor_data& descriptor_data)
- {
- mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
-
- op_queue<operation> ops;
- for (int i = 0; i < max_ops; ++i)
- {
- while (reactor_op* op = descriptor_data->op_queue_[i].front())
- {
- op->ec_ = asio::error::operation_aborted;
- descriptor_data->op_queue_[i].pop();
- ops.push(op);
- }
- }
-
- descriptor_lock.unlock();
-
- io_service_.post_deferred_completions(ops);
- }
+ ASIO_DECL void cancel_ops(socket_type descriptor,
+ per_descriptor_data& descriptor_data);
// Cancel any operations that are running against the descriptor and remove
// its registration from the reactor.
- void close_descriptor(socket_type descriptor,
- per_descriptor_data& descriptor_data)
- {
- mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
- mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_);
-
- // Remove the descriptor from the set of known descriptors. The descriptor
- // will be automatically removed from the epoll set when it is closed.
- descriptor_data->shutdown_ = true;
-
- op_queue<operation> ops;
- for (int i = 0; i < max_ops; ++i)
- {
- while (reactor_op* op = descriptor_data->op_queue_[i].front())
- {
- op->ec_ = asio::error::operation_aborted;
- descriptor_data->op_queue_[i].pop();
- ops.push(op);
- }
- }
-
- descriptor_lock.unlock();
-
- registered_descriptors_.erase(descriptor);
-
- descriptors_lock.unlock();
-
- io_service_.post_deferred_completions(ops);
- }
+ ASIO_DECL void close_descriptor(socket_type descriptor,
+ per_descriptor_data& descriptor_data);
// Add a new timer queue to the reactor.
template <typename Time_Traits>
- void add_timer_queue(timer_queue<Time_Traits>& timer_queue)
- {
- mutex::scoped_lock lock(mutex_);
- timer_queues_.insert(&timer_queue);
- }
+ void add_timer_queue(timer_queue<Time_Traits>& timer_queue);
// Remove a timer queue from the reactor.
template <typename Time_Traits>
- void remove_timer_queue(timer_queue<Time_Traits>& timer_queue)
- {
- mutex::scoped_lock lock(mutex_);
- timer_queues_.erase(&timer_queue);
- }
+ void remove_timer_queue(timer_queue<Time_Traits>& timer_queue);
// Schedule a new operation in the given timer queue to expire at the
// specified absolute time.
template <typename Time_Traits>
- void schedule_timer(timer_queue<Time_Traits>& timer_queue,
- const typename Time_Traits::time_type& time, timer_op* op, void* token)
- {
- mutex::scoped_lock lock(mutex_);
- if (!shutdown_)
- {
- bool earliest = timer_queue.enqueue_timer(time, op, token);
- io_service_.work_started();
- if (earliest)
- {
-#if defined(ASIO_HAS_TIMERFD)
- if (timer_fd_ != -1)
- {
- itimerspec new_timeout;
- itimerspec old_timeout;
- int flags = get_timeout(new_timeout);
- timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout);
- return;
- }
-#endif // defined(ASIO_HAS_TIMERFD)
- interrupter_.interrupt();
- }
- }
- }
+ void schedule_timer(timer_queue<Time_Traits>& queue,
+ const typename Time_Traits::time_type& time,
+ typename timer_queue<Time_Traits>::per_timer_data& timer, timer_op* op);
// Cancel the timer operations associated with the given token. Returns the
// number of operations that have been posted or dispatched.
template <typename Time_Traits>
- std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token)
- {
- mutex::scoped_lock lock(mutex_);
- op_queue<operation> ops;
- std::size_t n = timer_queue.cancel_timer(token, ops);
- lock.unlock();
- io_service_.post_deferred_completions(ops);
- return n;
- }
+ std::size_t cancel_timer(timer_queue<Time_Traits>& queue,
+ typename timer_queue<Time_Traits>::per_timer_data& timer);
// Run epoll once until interrupted or events are ready to be dispatched.
- void run(bool block, op_queue<operation>& ops)
- {
- // Calculate a timeout only if timerfd is not used.
- int timeout;
- if (timer_fd_ != -1)
- timeout = block ? -1 : 0;
- else
- {
- mutex::scoped_lock lock(mutex_);
- timeout = block ? get_timeout() : 0;
- }
-
- // Block on the epoll descriptor.
- epoll_event events[128];
- int num_events = epoll_wait(epoll_fd_, events, 128, timeout);
-
-#if defined(ASIO_HAS_TIMERFD)
- bool check_timers = (timer_fd_ == -1);
-#else // defined(ASIO_HAS_TIMERFD)
- bool check_timers = true;
-#endif // defined(ASIO_HAS_TIMERFD)
-
- // Dispatch the waiting events.
- for (int i = 0; i < num_events; ++i)
- {
- void* ptr = events[i].data.ptr;
- if (ptr == &interrupter_)
- {
- // No need to reset the interrupter since we're leaving the descriptor
- // in a ready-to-read state and relying on edge-triggered notifications
- // to make it so that we only get woken up when the descriptor's epoll
- // registration is updated.
-
-#if defined(ASIO_HAS_TIMERFD)
- if (timer_fd_ == -1)
- check_timers = true;
-#else // defined(ASIO_HAS_TIMERFD)
- check_timers = true;
-#endif // defined(ASIO_HAS_TIMERFD)
- }
-#if defined(ASIO_HAS_TIMERFD)
- else if (ptr == &timer_fd_)
- {
- check_timers = true;
- }
-#endif // defined(ASIO_HAS_TIMERFD)
- else
- {
- descriptor_state* descriptor_data = static_cast<descriptor_state*>(ptr);
- mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
-
- // Exception operations must be processed first to ensure that any
- // out-of-band data is read before normal data.
- static const int flag[max_ops] = { EPOLLIN, EPOLLOUT, EPOLLPRI };
- for (int j = max_ops - 1; j >= 0; --j)
- {
- if (events[i].events & (flag[j] | EPOLLERR | EPOLLHUP))
- {
- while (reactor_op* op = descriptor_data->op_queue_[j].front())
- {
- if (op->perform())
- {
- descriptor_data->op_queue_[j].pop();
- ops.push(op);
- }
- else
- break;
- }
- }
- }
- }
- }
-
- if (check_timers)
- {
- mutex::scoped_lock common_lock(mutex_);
- timer_queues_.get_ready_timers(ops);
-
-#if defined(ASIO_HAS_TIMERFD)
- if (timer_fd_ != -1)
- {
- itimerspec new_timeout;
- itimerspec old_timeout;
- int flags = get_timeout(new_timeout);
- timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout);
- }
-#endif // defined(ASIO_HAS_TIMERFD)
- }
- }
+ ASIO_DECL void run(bool block, op_queue<operation>& ops);
// Interrupt the select loop.
- void interrupt()
- {
- epoll_event ev = { 0, { 0 } };
- ev.events = EPOLLIN | EPOLLERR | EPOLLET;
- ev.data.ptr = &interrupter_;
- epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, interrupter_.read_descriptor(), &ev);
- }
+ ASIO_DECL void interrupt();
private:
// The hint to pass to epoll_create to size its data structures.
@@ -422,44 +132,26 @@ private:
// Create the epoll file descriptor. Throws an exception if the descriptor
// cannot be created.
- static int do_epoll_create()
- {
- int fd = epoll_create(epoll_size);
- if (fd == -1)
- {
- boost::throw_exception(
- asio::system_error(
- asio::error_code(errno,
- asio::error::get_system_category()),
- "epoll"));
- }
- return fd;
- }
+ ASIO_DECL static int do_epoll_create();
+
+ // Helper function to add a new timer queue.
+ ASIO_DECL void do_add_timer_queue(timer_queue_base& queue);
+
+ // Helper function to remove a timer queue.
+ ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue);
+
+ // Called to recalculate and update the timeout.
+ ASIO_DECL void update_timeout();
// Get the timeout value for the epoll_wait call. The timeout value is
// returned as a number of milliseconds. A return value of -1 indicates
// that epoll_wait should block indefinitely.
- int get_timeout()
- {
- // By default we will wait no longer than 5 minutes. This will ensure that
- // any changes to the system clock are detected after no longer than this.
- return timer_queues_.wait_duration_msec(5 * 60 * 1000);
- }
+ ASIO_DECL int get_timeout();
#if defined(ASIO_HAS_TIMERFD)
// Get the timeout value for the timer descriptor. The return value is the
// flag argument to be used when calling timerfd_settime.
- int get_timeout(itimerspec& ts)
- {
- ts.it_interval.tv_sec = 0;
- ts.it_interval.tv_nsec = 0;
-
- long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000);
- ts.it_value.tv_sec = usec / 1000000;
- ts.it_value.tv_nsec = usec ? (usec % 1000000) * 1000 : 1;
-
- return usec ? 0 : TFD_TIMER_ABSTIME;
- }
+ ASIO_DECL int get_timeout(itimerspec& ts);
#endif // defined(ASIO_HAS_TIMERFD)
// The io_service implementation used to post completions.
@@ -486,22 +178,20 @@ private:
// Mutex to protect access to the registered descriptors.
mutex registered_descriptors_mutex_;
- // Keep track of all registered descriptors. This code relies on the fact that
- // the hash_map implementation pools deleted nodes, meaning that we can assume
- // our descriptor_state pointer remains valid even after the entry is removed.
- // Technically this is not true for C++98, as that standard says that spliced
- // elements in a list are invalidated. However, C++0x fixes this shortcoming
- // so we'll just assume that C++98 std::list implementations will do the right
- // thing anyway.
- typedef detail::hash_map<socket_type, descriptor_state> descriptor_map;
- descriptor_map registered_descriptors_;
+ // Keep track of all registered descriptors.
+ object_pool<descriptor_state> registered_descriptors_;
};
} // namespace detail
} // namespace asio
-#endif // defined(ASIO_HAS_EPOLL)
-
#include "asio/detail/pop_options.hpp"
+#include "asio/detail/impl/epoll_reactor.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/epoll_reactor.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(ASIO_HAS_EPOLL)
+
#endif // ASIO_DETAIL_EPOLL_REACTOR_HPP
diff --git a/ext/asio/asio/detail/epoll_reactor_fwd.hpp b/ext/asio/asio/detail/epoll_reactor_fwd.hpp
index 266bccd..49df6ed 100644
--- a/ext/asio/asio/detail/epoll_reactor_fwd.hpp
+++ b/ext/asio/asio/detail/epoll_reactor_fwd.hpp
@@ -1,8 +1,8 @@
//
-// epoll_reactor_fwd.hpp
-// ~~~~~~~~~~~~~~~~~~~~~
+// detail/epoll_reactor_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,19 +15,9 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#if !defined(ASIO_DISABLE_EPOLL)
-#if defined(__linux__) // This service is only supported on Linux.
-
-#include "asio/detail/push_options.hpp"
-#include <linux/version.h>
-#include "asio/detail/pop_options.hpp"
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45) // Only kernels >= 2.5.45.
-
-// Define this to indicate that epoll is supported on the target platform.
-#define ASIO_HAS_EPOLL 1
+#if defined(ASIO_HAS_EPOLL)
namespace asio {
namespace detail {
@@ -37,10 +27,6 @@ class epoll_reactor;
} // namespace detail
} // namespace asio
-#endif // LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45)
-#endif // defined(__linux__)
-#endif // !defined(ASIO_DISABLE_EPOLL)
-
-#include "asio/detail/pop_options.hpp"
+#endif // defined(ASIO_HAS_EPOLL)
#endif // ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP
diff --git a/ext/asio/asio/detail/event.hpp b/ext/asio/asio/detail/event.hpp
index 65aa4cb..46ee8a6 100644
--- a/ext/asio/asio/detail/event.hpp
+++ b/ext/asio/asio/detail/event.hpp
@@ -1,8 +1,8 @@
//
-// event.hpp
-// ~~~~~~~~~
+// detail/event.hpp
+// ~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,11 +15,7 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
# include "asio/detail/null_event.hpp"
@@ -45,6 +41,4 @@ typedef posix_event event;
} // namespace detail
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_EVENT_HPP
diff --git a/ext/asio/asio/detail/eventfd_select_interrupter.hpp b/ext/asio/asio/detail/eventfd_select_interrupter.hpp
index 63b7ac4..0d3b958 100644
--- a/ext/asio/asio/detail/eventfd_select_interrupter.hpp
+++ b/ext/asio/asio/detail/eventfd_select_interrupter.hpp
@@ -1,8 +1,8 @@
//
-// eventfd_select_interrupter.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/eventfd_select_interrupter.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
// Copyright (c) 2008 Roelof Naude (roelof.naude at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
@@ -16,36 +16,11 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#if defined(__linux__)
-# if !defined(ASIO_DISABLE_EVENTFD)
-# include <linux/version.h>
-# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
-# define ASIO_HAS_EVENTFD
-# endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
-# endif // !defined(ASIO_DISABLE_EVENTFD)
-#endif // defined(__linux__)
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_EVENTFD)
#include "asio/detail/push_options.hpp"
-#include <fcntl.h>
-#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
-# include <asm/unistd.h>
-#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
-# include <sys/eventfd.h>
-#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error.hpp"
-#include "asio/system_error.hpp"
-#include "asio/detail/socket_types.hpp"
namespace asio {
namespace detail {
@@ -54,87 +29,16 @@ class eventfd_select_interrupter
{
public:
// Constructor.
- eventfd_select_interrupter()
- {
-#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
- write_descriptor_ = read_descriptor_ = syscall(__NR_eventfd, 0);
-#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
- write_descriptor_ = read_descriptor_ = ::eventfd(0, 0);
-#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
- if (read_descriptor_ != -1)
- {
- ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK);
- }
- else
- {
- int pipe_fds[2];
- if (pipe(pipe_fds) == 0)
- {
- read_descriptor_ = pipe_fds[0];
- ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK);
- write_descriptor_ = pipe_fds[1];
- ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK);
- }
- else
- {
- asio::error_code ec(errno,
- asio::error::get_system_category());
- asio::system_error e(ec, "eventfd_select_interrupter");
- boost::throw_exception(e);
- }
- }
- }
+ ASIO_DECL eventfd_select_interrupter();
// Destructor.
- ~eventfd_select_interrupter()
- {
- if (write_descriptor_ != -1 && write_descriptor_ != read_descriptor_)
- ::close(write_descriptor_);
- if (read_descriptor_ != -1)
- ::close(read_descriptor_);
- }
+ ASIO_DECL ~eventfd_select_interrupter();
// Interrupt the select call.
- void interrupt()
- {
- uint64_t counter(1UL);
- int result = ::write(write_descriptor_, &counter, sizeof(uint64_t));
- (void)result;
- }
+ ASIO_DECL void interrupt();
// Reset the select interrupt. Returns true if the call was interrupted.
- bool reset()
- {
- if (write_descriptor_ == read_descriptor_)
- {
- for (;;)
- {
- // Only perform one read. The kernel maintains an atomic counter.
- uint64_t counter(0);
- errno = 0;
- int bytes_read = ::read(read_descriptor_, &counter, sizeof(uint64_t));
- if (bytes_read < 0 && errno == EINTR)
- continue;
- bool was_interrupted = (bytes_read > 0);
- return was_interrupted;
- }
- }
- else
- {
- for (;;)
- {
- // Clear all data from the pipe.
- char data[1024];
- int bytes_read = ::read(read_descriptor_, data, sizeof(data));
- if (bytes_read < 0 && errno == EINTR)
- continue;
- bool was_interrupted = (bytes_read > 0);
- while (bytes_read == sizeof(data))
- bytes_read = ::read(read_descriptor_, data, sizeof(data));
- return was_interrupted;
- }
- }
- }
+ ASIO_DECL bool reset();
// Get the read descriptor to be passed to select.
int read_descriptor() const
@@ -159,8 +63,12 @@ private:
} // namespace detail
} // namespace asio
-#endif // defined(ASIO_HAS_EVENTFD)
-
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/eventfd_select_interrupter.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(ASIO_HAS_EVENTFD)
+
#endif // ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP
diff --git a/ext/asio/asio/detail/fd_set_adapter.hpp b/ext/asio/asio/detail/fd_set_adapter.hpp
index a575491..953cad1 100644
--- a/ext/asio/asio/detail/fd_set_adapter.hpp
+++ b/ext/asio/asio/detail/fd_set_adapter.hpp
@@ -1,8 +1,8 @@
//
-// fd_set_adapter.hpp
-// ~~~~~~~~~~~~~~~~~~
+// detail/fd_set_adapter.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,12 +15,7 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/detail/posix_fd_set_adapter.hpp"
#include "asio/detail/win_fd_set_adapter.hpp"
@@ -36,6 +31,4 @@ typedef posix_fd_set_adapter fd_set_adapter;
} // namespace detail
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_FD_SET_ADAPTER_HPP
diff --git a/ext/asio/asio/detail/fenced_block.hpp b/ext/asio/asio/detail/fenced_block.hpp
index 60198bc..70c47e1 100644
--- a/ext/asio/asio/detail/fenced_block.hpp
+++ b/ext/asio/asio/detail/fenced_block.hpp
@@ -1,8 +1,8 @@
//
-// fenced_block.hpp
-// ~~~~~~~~~~~~~~~~
+// detail/fenced_block.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,23 +15,25 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
+#if !defined(BOOST_HAS_THREADS) \
+ || defined(ASIO_DISABLE_THREADS) \
+ || defined(ASIO_DISABLE_FENCED_BLOCK)
# include "asio/detail/null_fenced_block.hpp"
#elif defined(__MACH__) && defined(__APPLE__)
# include "asio/detail/macos_fenced_block.hpp"
#elif defined(__sun)
# include "asio/detail/solaris_fenced_block.hpp"
+#elif defined(__GNUC__) && defined(__arm__)
+# include "asio/detail/gcc_arm_fenced_block.hpp"
+#elif defined(__GNUC__) && (defined(__hppa) || defined(__hppa__))
+# include "asio/detail/gcc_hppa_fenced_block.hpp"
#elif defined(__GNUC__) \
&& ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \
&& !defined(__INTEL_COMPILER) && !defined(__ICL) \
&& !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__)
-# include "asio/detail/gcc_fenced_block.hpp"
+# include "asio/detail/gcc_sync_fenced_block.hpp"
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
# include "asio/detail/gcc_x86_fenced_block.hpp"
#elif defined(BOOST_WINDOWS) && !defined(UNDER_CE)
@@ -43,17 +45,23 @@
namespace asio {
namespace detail {
-#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
+#if !defined(BOOST_HAS_THREADS) \
+ || defined(ASIO_DISABLE_THREADS) \
+ || defined(ASIO_DISABLE_FENCED_BLOCK)
typedef null_fenced_block fenced_block;
#elif defined(__MACH__) && defined(__APPLE__)
typedef macos_fenced_block fenced_block;
#elif defined(__sun)
typedef solaris_fenced_block fenced_block;
+#elif defined(__GNUC__) && defined(__arm__)
+typedef gcc_arm_fenced_block fenced_block;
+#elif defined(__GNUC__) && (defined(__hppa) || defined(__hppa__))
+typedef gcc_hppa_fenced_block fenced_block;
#elif defined(__GNUC__) \
&& ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \
&& !defined(__INTEL_COMPILER) && !defined(__ICL) \
&& !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__)
-typedef gcc_fenced_block fenced_block;
+typedef gcc_sync_fenced_block fenced_block;
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
typedef gcc_x86_fenced_block fenced_block;
#elif defined(BOOST_WINDOWS) && !defined(UNDER_CE)
@@ -65,6 +73,4 @@ typedef null_fenced_block fenced_block;
} // namespace detail
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_FENCED_BLOCK_HPP
diff --git a/ext/asio/asio/detail/gcc_arm_fenced_block.hpp b/ext/asio/asio/detail/gcc_arm_fenced_block.hpp
new file mode 100644
index 0000000..76c4d99
--- /dev/null
+++ b/ext/asio/asio/detail/gcc_arm_fenced_block.hpp
@@ -0,0 +1,76 @@
+//
+// detail/gcc_arm_fenced_block.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_GCC_ARM_FENCED_BLOCK_HPP
+#define ASIO_DETAIL_GCC_ARM_FENCED_BLOCK_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(__GNUC__) && defined(__arm__)
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+class gcc_arm_fenced_block
+ : private noncopyable
+{
+public:
+ // Constructor.
+ gcc_arm_fenced_block()
+ {
+ barrier();
+ }
+
+ // Destructor.
+ ~gcc_arm_fenced_block()
+ {
+ barrier();
+ }
+
+private:
+ static void barrier()
+ {
+#if defined(__ARM_ARCH_4__) \
+ || defined(__ARM_ARCH_4T__) \
+ || defined(__ARM_ARCH_5__) \
+ || defined(__ARM_ARCH_5E__) \
+ || defined(__ARM_ARCH_5T__) \
+ || defined(__ARM_ARCH_5TE__) \
+ || defined(__ARM_ARCH_5TEJ__) \
+ || defined(__ARM_ARCH_6__) \
+ || defined(__ARM_ARCH_6J__) \
+ || defined(__ARM_ARCH_6K__) \
+ || defined(__ARM_ARCH_6Z__) \
+ || defined(__ARM_ARCH_6ZK__) \
+ || defined(__ARM_ARCH_6T2__)
+ int a = 0, b = 0;
+ __asm__ __volatile__ ("swp %0, %1, [%2]"
+ : "=&r"(a) : "r"(1), "r"(&b) : "memory", "cc");
+#else
+ // ARMv7 and later.
+ __asm__ __volatile__ ("dmb" : : : "memory");
+#endif
+ }
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(__GNUC__) && defined(__arm__)
+
+#endif // ASIO_DETAIL_GCC_ARM_FENCED_BLOCK_HPP
diff --git a/ext/asio/asio/detail/gcc_hppa_fenced_block.hpp b/ext/asio/asio/detail/gcc_hppa_fenced_block.hpp
new file mode 100644
index 0000000..215086a
--- /dev/null
+++ b/ext/asio/asio/detail/gcc_hppa_fenced_block.hpp
@@ -0,0 +1,58 @@
+//
+// detail/gcc_hppa_fenced_block.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_GCC_HPPA_FENCED_BLOCK_HPP
+#define ASIO_DETAIL_GCC_HPPA_FENCED_BLOCK_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(__GNUC__) && (defined(__hppa) || defined(__hppa__))
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+class gcc_hppa_fenced_block
+ : private noncopyable
+{
+public:
+ // Constructor.
+ gcc_hppa_fenced_block()
+ {
+ barrier();
+ }
+
+ // Destructor.
+ ~gcc_hppa_fenced_block()
+ {
+ barrier();
+ }
+
+private:
+ static void barrier()
+ {
+ // This is just a placeholder and almost certainly not sufficient.
+ __asm__ __volatile__ ("" : : : "memory");
+ }
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(__GNUC__) && (defined(__hppa) || defined(__hppa__))
+
+#endif // ASIO_DETAIL_GCC_HPPA_FENCED_BLOCK_HPP
diff --git a/ext/asio/asio/detail/gcc_sync_fenced_block.hpp b/ext/asio/asio/detail/gcc_sync_fenced_block.hpp
new file mode 100644
index 0000000..569b559
--- /dev/null
+++ b/ext/asio/asio/detail/gcc_sync_fenced_block.hpp
@@ -0,0 +1,61 @@
+//
+// detail/gcc_sync_fenced_block.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_GCC_SYNC_FENCED_BLOCK_HPP
+#define ASIO_DETAIL_GCC_SYNC_FENCED_BLOCK_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(__GNUC__) \
+ && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \
+ && !defined(__INTEL_COMPILER) && !defined(__ICL) \
+ && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__)
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+class gcc_sync_fenced_block
+ : private noncopyable
+{
+public:
+ // Constructor.
+ gcc_sync_fenced_block()
+ : value_(0)
+ {
+ __sync_lock_test_and_set(&value_, 1);
+ }
+
+ // Destructor.
+ ~gcc_sync_fenced_block()
+ {
+ __sync_lock_release(&value_);
+ }
+
+private:
+ int value_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(__GNUC__)
+ // && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4))
+ // && !defined(__INTEL_COMPILER) && !defined(__ICL)
+ // && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__)
+
+#endif // ASIO_DETAIL_GCC_SYNC_FENCED_BLOCK_HPP
diff --git a/ext/asio/asio/detail/gcc_x86_fenced_block.hpp b/ext/asio/asio/detail/gcc_x86_fenced_block.hpp
index 48119f4..3e2e5bc 100644
--- a/ext/asio/asio/detail/gcc_x86_fenced_block.hpp
+++ b/ext/asio/asio/detail/gcc_x86_fenced_block.hpp
@@ -1,8 +1,8 @@
//
-// gcc_x86_fenced_block.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/gcc_x86_fenced_block.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,14 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -54,8 +52,8 @@ private:
} // namespace detail
} // namespace asio
-#endif // defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
-
#include "asio/detail/pop_options.hpp"
+#endif // defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+
#endif // ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP
diff --git a/ext/asio/asio/detail/handler_alloc_helpers.hpp b/ext/asio/asio/detail/handler_alloc_helpers.hpp
index 0ac42a7..9ae80de 100644
--- a/ext/asio/asio/detail/handler_alloc_helpers.hpp
+++ b/ext/asio/asio/detail/handler_alloc_helpers.hpp
@@ -1,8 +1,8 @@
//
-// handler_alloc_helpers.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/handler_alloc_helpers.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,15 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <boost/detail/workaround.hpp>
#include <boost/utility/addressof.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/handler_alloc_hook.hpp"
#include "asio/detail/noncopyable.hpp"
+#include "asio/handler_alloc_hook.hpp"
+
+#include "asio/detail/push_options.hpp"
// Calls to asio_handler_allocate and asio_handler_deallocate must be made from
// a namespace that does not contain any overloads of these functions. The
@@ -56,203 +54,31 @@ inline void deallocate(void* p, std::size_t s, Handler& h)
} // namespace asio_handler_alloc_helpers
-namespace asio {
-namespace detail {
-
-// Traits for handler allocation.
-template <typename Handler, typename Object>
-struct handler_alloc_traits
-{
- typedef Handler handler_type;
- typedef Object value_type;
- typedef Object* pointer_type;
- BOOST_STATIC_CONSTANT(std::size_t, value_size = sizeof(Object));
-};
-
-template <typename Alloc_Traits>
-class handler_ptr;
-
-// Helper class to provide RAII on uninitialised handler memory.
-template <typename Alloc_Traits>
-class raw_handler_ptr
- : private noncopyable
-{
-public:
- typedef typename Alloc_Traits::handler_type handler_type;
- typedef typename Alloc_Traits::value_type value_type;
- typedef typename Alloc_Traits::pointer_type pointer_type;
- BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size);
-
- // Constructor allocates the memory.
- raw_handler_ptr(handler_type& handler)
- : handler_(handler),
- pointer_(static_cast<pointer_type>(
- asio_handler_alloc_helpers::allocate(value_size, handler_)))
- {
- }
-
- // Destructor automatically deallocates memory, unless it has been stolen by
- // a handler_ptr object.
- ~raw_handler_ptr()
- {
- if (pointer_)
- asio_handler_alloc_helpers::deallocate(
- pointer_, value_size, handler_);
- }
-
-private:
- friend class handler_ptr<Alloc_Traits>;
- handler_type& handler_;
- pointer_type pointer_;
-};
-
-// Helper class to provide RAII on uninitialised handler memory.
-template <typename Alloc_Traits>
-class handler_ptr
- : private noncopyable
-{
-public:
- typedef typename Alloc_Traits::handler_type handler_type;
- typedef typename Alloc_Traits::value_type value_type;
- typedef typename Alloc_Traits::pointer_type pointer_type;
- BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size);
- typedef raw_handler_ptr<Alloc_Traits> raw_ptr_type;
-
- // Take ownership of existing memory.
- handler_ptr(handler_type& handler, pointer_type pointer)
- : handler_(handler),
- pointer_(pointer)
- {
- }
-
- // Construct object in raw memory and take ownership if construction succeeds.
- handler_ptr(raw_ptr_type& raw_ptr)
- : handler_(raw_ptr.handler_),
- pointer_(new (raw_ptr.pointer_) value_type)
- {
- raw_ptr.pointer_ = 0;
- }
-
- // Construct object in raw memory and take ownership if construction succeeds.
- template <typename Arg1>
- handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1)
- : handler_(raw_ptr.handler_),
- pointer_(new (raw_ptr.pointer_) value_type(a1))
- {
- raw_ptr.pointer_ = 0;
- }
-
- // Construct object in raw memory and take ownership if construction succeeds.
- template <typename Arg1, typename Arg2>
- handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2)
- : handler_(raw_ptr.handler_),
- pointer_(new (raw_ptr.pointer_) value_type(a1, a2))
- {
- raw_ptr.pointer_ = 0;
- }
-
- // Construct object in raw memory and take ownership if construction succeeds.
- template <typename Arg1, typename Arg2, typename Arg3>
- handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3)
- : handler_(raw_ptr.handler_),
- pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3))
- {
- raw_ptr.pointer_ = 0;
- }
-
- // Construct object in raw memory and take ownership if construction succeeds.
- template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
- handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4)
- : handler_(raw_ptr.handler_),
- pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4))
- {
- raw_ptr.pointer_ = 0;
- }
-
- // Construct object in raw memory and take ownership if construction succeeds.
- template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
- typename Arg5>
- handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
- Arg5& a5)
- : handler_(raw_ptr.handler_),
- pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5))
- {
- raw_ptr.pointer_ = 0;
- }
-
- // Construct object in raw memory and take ownership if construction succeeds.
- template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
- typename Arg5, typename Arg6>
- handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
- Arg5& a5, Arg6& a6)
- : handler_(raw_ptr.handler_),
- pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6))
- {
- raw_ptr.pointer_ = 0;
- }
-
- // Construct object in raw memory and take ownership if construction succeeds.
- template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
- typename Arg5, typename Arg6, typename Arg7>
- handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
- Arg5& a5, Arg6& a6, Arg7& a7)
- : handler_(raw_ptr.handler_),
- pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6, a7))
- {
- raw_ptr.pointer_ = 0;
- }
-
- // Construct object in raw memory and take ownership if construction succeeds.
- template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
- typename Arg5, typename Arg6, typename Arg7, typename Arg8>
- handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4,
- Arg5& a5, Arg6& a6, Arg7& a7, Arg8& a8)
- : handler_(raw_ptr.handler_),
- pointer_(new (raw_ptr.pointer_) value_type(
- a1, a2, a3, a4, a5, a6, a7, a8))
- {
- raw_ptr.pointer_ = 0;
- }
-
- // Destructor automatically deallocates memory, unless it has been released.
- ~handler_ptr()
- {
- reset();
- }
-
- // Get the memory.
- pointer_type get() const
- {
- return pointer_;
- }
-
- // Release ownership of the memory.
- pointer_type release()
- {
- pointer_type tmp = pointer_;
- pointer_ = 0;
- return tmp;
- }
-
- // Explicitly destroy and deallocate the memory.
- void reset()
- {
- if (pointer_)
- {
- pointer_->value_type::~value_type();
- asio_handler_alloc_helpers::deallocate(
- pointer_, value_size, handler_);
- pointer_ = 0;
- }
- }
-
-private:
- handler_type& handler_;
- pointer_type pointer_;
-};
-
-} // namespace detail
-} // namespace asio
+#define ASIO_DEFINE_HANDLER_PTR(op) \
+ struct ptr \
+ { \
+ Handler* h; \
+ void* v; \
+ op* p; \
+ ~ptr() \
+ { \
+ reset(); \
+ } \
+ void reset() \
+ { \
+ if (p) \
+ { \
+ p->~op(); \
+ p = 0; \
+ } \
+ if (v) \
+ { \
+ asio_handler_alloc_helpers::deallocate(v, sizeof(op), *h); \
+ v = 0; \
+ } \
+ } \
+ } \
+ /**/
#include "asio/detail/pop_options.hpp"
diff --git a/ext/asio/asio/detail/handler_invoke_helpers.hpp b/ext/asio/asio/detail/handler_invoke_helpers.hpp
index 8332567..5b122d9 100644
--- a/ext/asio/asio/detail/handler_invoke_helpers.hpp
+++ b/ext/asio/asio/detail/handler_invoke_helpers.hpp
@@ -1,8 +1,8 @@
//
-// handler_invoke_helpers.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/handler_invoke_helpers.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,15 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <boost/detail/workaround.hpp>
#include <boost/utility/addressof.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/handler_invoke_hook.hpp"
+#include "asio/detail/push_options.hpp"
+
// Calls to asio_handler_invoke must be made from a namespace that does not
// contain overloads of this function. The asio_handler_invoke_helpers
// namespace is defined here for that purpose.
diff --git a/ext/asio/asio/detail/hash_map.hpp b/ext/asio/asio/detail/hash_map.hpp
index b2a1517..a3957df 100644
--- a/ext/asio/asio/detail/hash_map.hpp
+++ b/ext/asio/asio/detail/hash_map.hpp
@@ -1,8 +1,8 @@
//
-// hash_map.hpp
-// ~~~~~~~~~~~~
+// detail/hash_map.hpp
+// ~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,33 +15,38 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cassert>
#include <list>
#include <utility>
-#include <boost/functional/hash.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/detail/noncopyable.hpp"
-#include "asio/detail/socket_types.hpp"
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# include "asio/detail/socket_types.hpp"
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
-template <typename T>
-inline std::size_t calculate_hash_value(const T& t)
+inline std::size_t calculate_hash_value(int i)
+{
+ return static_cast<std::size_t>(i);
+}
+
+inline std::size_t calculate_hash_value(void* p)
{
- return boost::hash_value(t);
+ return reinterpret_cast<std::size_t>(p)
+ + (reinterpret_cast<std::size_t>(p) >> 3);
}
-#if defined(_WIN64)
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
inline std::size_t calculate_hash_value(SOCKET s)
{
return static_cast<std::size_t>(s);
}
-#endif // defined(_WIN64)
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
// Note: assumes K and V are POD types.
template <typename K, typename V>
diff --git a/ext/asio/asio/detail/impl/descriptor_ops.ipp b/ext/asio/asio/detail/impl/descriptor_ops.ipp
new file mode 100644
index 0000000..8e0c27d
--- /dev/null
+++ b/ext/asio/asio/detail/impl/descriptor_ops.ipp
@@ -0,0 +1,382 @@
+//
+// detail/impl/descriptor_ops.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP
+#define ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <cerrno>
+#include "asio/detail/descriptor_ops.hpp"
+#include "asio/error.hpp"
+
+#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+namespace descriptor_ops {
+
+ASIO_DECL
+int open(const char* path, int flags, asio::error_code& ec)
+{
+ errno = 0;
+ int result = error_wrapper(::open(path, flags), ec);
+ if (result >= 0)
+ ec = asio::error_code();
+ return result;
+}
+
+ASIO_DECL
+int close(int d, state_type& state, asio::error_code& ec)
+{
+ int result = 0;
+ if (d != -1)
+ {
+ if (state & internal_non_blocking)
+ {
+#if defined(__SYMBIAN32__)
+ int flags = ::fcntl(d, F_GETFL, 0);
+ if (flags >= 0)
+ ::fcntl(d, F_SETFL, flags & ~O_NONBLOCK);
+#else // defined(__SYMBIAN32__)
+ ioctl_arg_type arg = 0;
+ ::ioctl(d, FIONBIO, &arg);
+#endif // defined(__SYMBIAN32__)
+ state &= ~internal_non_blocking;
+ }
+
+ errno = 0;
+ result = error_wrapper(::close(d), ec);
+ }
+
+ if (result == 0)
+ ec = asio::error_code();
+ return result;
+}
+
+ASIO_DECL
+bool set_internal_non_blocking(int d,
+ state_type& state, asio::error_code& ec)
+{
+ if (d == -1)
+ {
+ ec = asio::error::bad_descriptor;
+ return false;
+ }
+
+ errno = 0;
+#if defined(__SYMBIAN32__)
+ int result = error_wrapper(::fcntl(d, F_GETFL, 0), ec);
+ if (result >= 0)
+ {
+ errno = 0;
+ result = error_wrapper(::fcntl(d, F_SETFL, result | O_NONBLOCK), ec);
+ }
+#else // defined(__SYMBIAN32__)
+ ioctl_arg_type arg = 1;
+ int result = error_wrapper(::ioctl(d, FIONBIO, &arg), ec);
+#endif // defined(__SYMBIAN32__)
+
+ if (result >= 0)
+ {
+ ec = asio::error_code();
+ state |= internal_non_blocking;
+ return true;
+ }
+
+ return false;
+}
+
+ASIO_DECL
+std::size_t sync_read(int d, state_type state, buf* bufs,
+ std::size_t count, bool all_empty, asio::error_code& ec)
+{
+ if (d == -1)
+ {
+ ec = asio::error::bad_descriptor;
+ return 0;
+ }
+
+ // A request to read 0 bytes on a stream is a no-op.
+ if (all_empty)
+ {
+ ec = asio::error_code();
+ return 0;
+ }
+
+ // Read some data.
+ for (;;)
+ {
+ // Try to complete the operation without blocking.
+ errno = 0;
+ int bytes = error_wrapper(::readv(d, bufs, static_cast<int>(count)), ec);
+
+ // Check if operation succeeded.
+ if (bytes > 0)
+ return bytes;
+
+ // Check for EOF.
+ if (bytes == 0)
+ {
+ ec = asio::error::eof;
+ return 0;
+ }
+
+ // Operation failed.
+ if ((state & user_set_non_blocking)
+ || (ec != asio::error::would_block
+ && ec != asio::error::try_again))
+ return 0;
+
+ // Wait for descriptor to become ready.
+ if (descriptor_ops::poll_read(d, ec) < 0)
+ return 0;
+ }
+}
+
+ASIO_DECL
+bool non_blocking_read(int d, buf* bufs, std::size_t count,
+ asio::error_code& ec, std::size_t& bytes_transferred)
+{
+ for (;;)
+ {
+ // Read some data.
+ errno = 0;
+ int bytes = error_wrapper(::readv(d, bufs, static_cast<int>(count)), ec);
+
+ // Check for end of stream.
+ if (bytes == 0)
+ {
+ ec = asio::error::eof;
+ return true;
+ }
+
+ // Retry operation if interrupted by signal.
+ if (ec == asio::error::interrupted)
+ continue;
+
+ // Check if we need to run the operation again.
+ if (ec == asio::error::would_block
+ || ec == asio::error::try_again)
+ return false;
+
+ // Operation is complete.
+ if (bytes > 0)
+ {
+ ec = asio::error_code();
+ bytes_transferred = bytes;
+ }
+ else
+ bytes_transferred = 0;
+
+ return true;
+ }
+}
+
+ASIO_DECL
+std::size_t sync_write(int d, state_type state, const buf* bufs,
+ std::size_t count, bool all_empty, asio::error_code& ec)
+{
+ if (d == -1)
+ {
+ ec = asio::error::bad_descriptor;
+ return 0;
+ }
+
+ // A request to write 0 bytes on a stream is a no-op.
+ if (all_empty)
+ {
+ ec = asio::error_code();
+ return 0;
+ }
+
+ // Write some data.
+ for (;;)
+ {
+ // Try to complete the operation without blocking.
+ errno = 0;
+ int bytes = error_wrapper(::writev(d, bufs, static_cast<int>(count)), ec);
+
+ // Check if operation succeeded.
+ if (bytes > 0)
+ return bytes;
+
+ // Operation failed.
+ if ((state & user_set_non_blocking)
+ || (ec != asio::error::would_block
+ && ec != asio::error::try_again))
+ return 0;
+
+ // Wait for descriptor to become ready.
+ if (descriptor_ops::poll_write(d, ec) < 0)
+ return 0;
+ }
+}
+
+ASIO_DECL
+bool non_blocking_write(int d, const buf* bufs, std::size_t count,
+ asio::error_code& ec, std::size_t& bytes_transferred)
+{
+ for (;;)
+ {
+ // Write some data.
+ errno = 0;
+ int bytes = error_wrapper(::writev(d, bufs, static_cast<int>(count)), ec);
+
+ // Retry operation if interrupted by signal.
+ if (ec == asio::error::interrupted)
+ continue;
+
+ // Check if we need to run the operation again.
+ if (ec == asio::error::would_block
+ || ec == asio::error::try_again)
+ return false;
+
+ // Operation is complete.
+ if (bytes >= 0)
+ {
+ ec = asio::error_code();
+ bytes_transferred = bytes;
+ }
+ else
+ bytes_transferred = 0;
+
+ return true;
+ }
+}
+
+ASIO_DECL
+int ioctl(int d, state_type& state, long cmd,
+ ioctl_arg_type* arg, asio::error_code& ec)
+{
+ if (d == -1)
+ {
+ ec = asio::error::bad_descriptor;
+ return -1;
+ }
+
+ errno = 0;
+ int result = error_wrapper(::ioctl(d, cmd, arg), ec);
+
+ if (result >= 0)
+ {
+ ec = asio::error_code();
+
+ // When updating the non-blocking mode we always perform the ioctl syscall,
+ // even if the flags would otherwise indicate that the descriptor is
+ // already in the correct state. This ensures that the underlying
+ // descriptor is put into the state that has been requested by the user. If
+ // the ioctl syscall was successful then we need to update the flags to
+ // match.
+ if (cmd == static_cast<long>(FIONBIO))
+ {
+ if (*arg)
+ {
+ state |= user_set_non_blocking;
+ }
+ else
+ {
+ // Clearing the non-blocking mode always overrides any internally-set
+ // non-blocking flag. Any subsequent asynchronous operations will need
+ // to re-enable non-blocking I/O.
+ state &= ~(user_set_non_blocking | internal_non_blocking);
+ }
+ }
+ }
+
+ return result;
+}
+
+ASIO_DECL
+int fcntl(int d, long cmd, asio::error_code& ec)
+{
+ if (d == -1)
+ {
+ ec = asio::error::bad_descriptor;
+ return -1;
+ }
+
+ errno = 0;
+ int result = error_wrapper(::fcntl(d, cmd), ec);
+ if (result != -1)
+ ec = asio::error_code();
+ return result;
+}
+
+ASIO_DECL
+int fcntl(int d, long cmd, long arg, asio::error_code& ec)
+{
+ if (d == -1)
+ {
+ ec = asio::error::bad_descriptor;
+ return -1;
+ }
+
+ errno = 0;
+ int result = error_wrapper(::fcntl(d, cmd, arg), ec);
+ if (result != -1)
+ ec = asio::error_code();
+ return result;
+}
+
+ASIO_DECL
+int poll_read(int d, asio::error_code& ec)
+{
+ if (d == -1)
+ {
+ ec = asio::error::bad_descriptor;
+ return -1;
+ }
+
+ pollfd fds;
+ fds.fd = d;
+ fds.events = POLLIN;
+ fds.revents = 0;
+ errno = 0;
+ int result = error_wrapper(::poll(&fds, 1, -1), ec);
+ if (result >= 0)
+ ec = asio::error_code();
+ return result;
+}
+
+ASIO_DECL
+int poll_write(int d, asio::error_code& ec)
+{
+ if (d == -1)
+ {
+ ec = asio::error::bad_descriptor;
+ return -1;
+ }
+
+ pollfd fds;
+ fds.fd = d;
+ fds.events = POLLOUT;
+ fds.revents = 0;
+ errno = 0;
+ int result = error_wrapper(::poll(&fds, 1, -1), ec);
+ if (result >= 0)
+ ec = asio::error_code();
+ return result;
+}
+
+} // namespace descriptor_ops
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
+#endif // ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP
diff --git a/ext/asio/asio/detail/impl/dev_poll_reactor.hpp b/ext/asio/asio/detail/impl/dev_poll_reactor.hpp
new file mode 100644
index 0000000..7fbef82
--- /dev/null
+++ b/ext/asio/asio/detail/impl/dev_poll_reactor.hpp
@@ -0,0 +1,77 @@
+//
+// detail/impl/dev_poll_reactor.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_HPP
+#define ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_DEV_POLL)
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Time_Traits>
+void dev_poll_reactor::add_timer_queue(timer_queue<Time_Traits>& queue)
+{
+ do_add_timer_queue(queue);
+}
+
+template <typename Time_Traits>
+void dev_poll_reactor::remove_timer_queue(timer_queue<Time_Traits>& queue)
+{
+ do_remove_timer_queue(queue);
+}
+
+template <typename Time_Traits>
+void dev_poll_reactor::schedule_timer(timer_queue<Time_Traits>& queue,
+ const typename Time_Traits::time_type& time,
+ typename timer_queue<Time_Traits>::per_timer_data& timer, timer_op* op)
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ if (shutdown_)
+ {
+ io_service_.post_immediate_completion(op);
+ return;
+ }
+
+ bool earliest = queue.enqueue_timer(time, timer, op);
+ io_service_.work_started();
+ if (earliest)
+ interrupter_.interrupt();
+}
+
+template <typename Time_Traits>
+std::size_t dev_poll_reactor::cancel_timer(timer_queue<Time_Traits>& queue,
+ typename timer_queue<Time_Traits>::per_timer_data& timer)
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ op_queue<operation> ops;
+ std::size_t n = queue.cancel_timer(timer, ops);
+ lock.unlock();
+ io_service_.post_deferred_completions(ops);
+ return n;
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_DEV_POLL)
+
+#endif // ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_HPP
diff --git a/ext/asio/asio/detail/impl/dev_poll_reactor.ipp b/ext/asio/asio/detail/impl/dev_poll_reactor.ipp
new file mode 100644
index 0000000..0d1456f
--- /dev/null
+++ b/ext/asio/asio/detail/impl/dev_poll_reactor.ipp
@@ -0,0 +1,338 @@
+//
+// detail/impl/dev_poll_reactor.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_IPP
+#define ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_DEV_POLL)
+
+#include "asio/detail/dev_poll_reactor.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+dev_poll_reactor::dev_poll_reactor(asio::io_service& io_service)
+ : asio::detail::service_base<dev_poll_reactor>(io_service),
+ io_service_(use_service<io_service_impl>(io_service)),
+ mutex_(),
+ dev_poll_fd_(do_dev_poll_create()),
+ interrupter_(),
+ shutdown_(false)
+{
+ // Add the interrupter's descriptor to /dev/poll.
+ ::pollfd ev = { 0 };
+ ev.fd = interrupter_.read_descriptor();
+ ev.events = POLLIN | POLLERR;
+ ev.revents = 0;
+ ::write(dev_poll_fd_, &ev, sizeof(ev));
+}
+
+dev_poll_reactor::~dev_poll_reactor()
+{
+ shutdown_service();
+ ::close(dev_poll_fd_);
+}
+
+void dev_poll_reactor::shutdown_service()
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ shutdown_ = true;
+ lock.unlock();
+
+ op_queue<operation> ops;
+
+ for (int i = 0; i < max_ops; ++i)
+ op_queue_[i].get_all_operations(ops);
+
+ timer_queues_.get_all_timers(ops);
+}
+
+void dev_poll_reactor::init_task()
+{
+ io_service_.init_task();
+}
+
+int dev_poll_reactor::register_descriptor(socket_type, per_descriptor_data&)
+{
+ return 0;
+}
+
+void dev_poll_reactor::start_op(int op_type, socket_type descriptor,
+ dev_poll_reactor::per_descriptor_data&,
+ reactor_op* op, bool allow_speculative)
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ if (shutdown_)
+ {
+ post_immediate_completion(op);
+ return;
+ }
+
+ if (allow_speculative)
+ {
+ if (op_type != read_op || !op_queue_[except_op].has_operation(descriptor))
+ {
+ if (!op_queue_[op_type].has_operation(descriptor))
+ {
+ if (op->perform())
+ {
+ lock.unlock();
+ io_service_.post_immediate_completion(op);
+ return;
+ }
+ }
+ }
+ }
+
+ bool first = op_queue_[op_type].enqueue_operation(descriptor, op);
+ io_service_.work_started();
+ if (first)
+ {
+ ::pollfd& ev = add_pending_event_change(descriptor);
+ ev.events = POLLERR | POLLHUP;
+ if (op_type == read_op
+ || op_queue_[read_op].has_operation(descriptor))
+ ev.events |= POLLIN;
+ if (op_type == write_op
+ || op_queue_[write_op].has_operation(descriptor))
+ ev.events |= POLLOUT;
+ if (op_type == except_op
+ || op_queue_[except_op].has_operation(descriptor))
+ ev.events |= POLLPRI;
+ interrupter_.interrupt();
+ }
+}
+
+void dev_poll_reactor::cancel_ops(socket_type descriptor,
+ dev_poll_reactor::per_descriptor_data&)
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ cancel_ops_unlocked(descriptor, asio::error::operation_aborted);
+}
+
+void dev_poll_reactor::close_descriptor(socket_type descriptor,
+ dev_poll_reactor::per_descriptor_data&)
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ // Remove the descriptor from /dev/poll.
+ ::pollfd& ev = add_pending_event_change(descriptor);
+ ev.events = POLLREMOVE;
+ interrupter_.interrupt();
+
+ // Cancel any outstanding operations associated with the descriptor.
+ cancel_ops_unlocked(descriptor, asio::error::operation_aborted);
+}
+
+void dev_poll_reactor::run(bool block, op_queue<operation>& ops)
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ // We can return immediately if there's no work to do and the reactor is
+ // not supposed to block.
+ if (!block && op_queue_[read_op].empty() && op_queue_[write_op].empty()
+ && op_queue_[except_op].empty() && timer_queues_.all_empty())
+ return;
+
+ // Write the pending event registration changes to the /dev/poll descriptor.
+ std::size_t events_size = sizeof(::pollfd) * pending_event_changes_.size();
+ if (events_size > 0)
+ {
+ errno = 0;
+ int result = ::write(dev_poll_fd_,
+ &pending_event_changes_[0], events_size);
+ if (result != static_cast<int>(events_size))
+ {
+ asio::error_code ec = asio::error_code(
+ errno, asio::error::get_system_category());
+ for (std::size_t i = 0; i < pending_event_changes_.size(); ++i)
+ {
+ int descriptor = pending_event_changes_[i].fd;
+ for (int j = 0; j < max_ops; ++j)
+ op_queue_[j].cancel_operations(descriptor, ops, ec);
+ }
+ }
+ pending_event_changes_.clear();
+ pending_event_change_index_.clear();
+ }
+
+ int timeout = block ? get_timeout() : 0;
+ lock.unlock();
+
+ // Block on the /dev/poll descriptor.
+ ::pollfd events[128] = { { 0 } };
+ ::dvpoll dp = { 0 };
+ dp.dp_fds = events;
+ dp.dp_nfds = 128;
+ dp.dp_timeout = timeout;
+ int num_events = ::ioctl(dev_poll_fd_, DP_POLL, &dp);
+
+ lock.lock();
+
+ // Dispatch the waiting events.
+ for (int i = 0; i < num_events; ++i)
+ {
+ int descriptor = events[i].fd;
+ if (descriptor == interrupter_.read_descriptor())
+ {
+ interrupter_.reset();
+ }
+ else
+ {
+ bool more_reads = false;
+ bool more_writes = false;
+ bool more_except = false;
+
+ // Exception operations must be processed first to ensure that any
+ // out-of-band data is read before normal data.
+ if (events[i].events & (POLLPRI | POLLERR | POLLHUP))
+ more_except =
+ op_queue_[except_op].perform_operations(descriptor, ops);
+ else
+ more_except = op_queue_[except_op].has_operation(descriptor);
+
+ if (events[i].events & (POLLIN | POLLERR | POLLHUP))
+ more_reads = op_queue_[read_op].perform_operations(descriptor, ops);
+ else
+ more_reads = op_queue_[read_op].has_operation(descriptor);
+
+ if (events[i].events & (POLLOUT | POLLERR | POLLHUP))
+ more_writes = op_queue_[write_op].perform_operations(descriptor, ops);
+ else
+ more_writes = op_queue_[write_op].has_operation(descriptor);
+
+ if ((events[i].events & (POLLERR | POLLHUP)) != 0
+ && !more_except && !more_reads && !more_writes)
+ {
+ // If we have an event and no operations associated with the
+ // descriptor then we need to delete the descriptor from /dev/poll.
+ // The poll operation can produce POLLHUP or POLLERR events when there
+ // is no operation pending, so if we do not remove the descriptor we
+ // can end up in a tight polling loop.
+ ::pollfd ev = { 0 };
+ ev.fd = descriptor;
+ ev.events = POLLREMOVE;
+ ev.revents = 0;
+ ::write(dev_poll_fd_, &ev, sizeof(ev));
+ }
+ else
+ {
+ ::pollfd ev = { 0 };
+ ev.fd = descriptor;
+ ev.events = POLLERR | POLLHUP;
+ if (more_reads)
+ ev.events |= POLLIN;
+ if (more_writes)
+ ev.events |= POLLOUT;
+ if (more_except)
+ ev.events |= POLLPRI;
+ ev.revents = 0;
+ int result = ::write(dev_poll_fd_, &ev, sizeof(ev));
+ if (result != sizeof(ev))
+ {
+ asio::error_code ec(errno,
+ asio::error::get_system_category());
+ for (int j = 0; j < max_ops; ++j)
+ op_queue_[j].cancel_operations(descriptor, ops, ec);
+ }
+ }
+ }
+ }
+ timer_queues_.get_ready_timers(ops);
+}
+
+void dev_poll_reactor::interrupt()
+{
+ interrupter_.interrupt();
+}
+
+int dev_poll_reactor::do_dev_poll_create()
+{
+ int fd = ::open("/dev/poll", O_RDWR);
+ if (fd == -1)
+ {
+ asio::error_code ec(errno,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "/dev/poll");
+ }
+ return fd;
+}
+
+void dev_poll_reactor::do_add_timer_queue(timer_queue_base& queue)
+{
+ mutex::scoped_lock lock(mutex_);
+ timer_queues_.insert(&queue);
+}
+
+void dev_poll_reactor::do_remove_timer_queue(timer_queue_base& queue)
+{
+ mutex::scoped_lock lock(mutex_);
+ timer_queues_.erase(&queue);
+}
+
+int dev_poll_reactor::get_timeout()
+{
+ // By default we will wait no longer than 5 minutes. This will ensure that
+ // any changes to the system clock are detected after no longer than this.
+ return timer_queues_.wait_duration_msec(5 * 60 * 1000);
+}
+
+void dev_poll_reactor::cancel_ops_unlocked(socket_type descriptor,
+ const asio::error_code& ec)
+{
+ bool need_interrupt = false;
+ op_queue<operation> ops;
+ for (int i = 0; i < max_ops; ++i)
+ need_interrupt = op_queue_[i].cancel_operations(
+ descriptor, ops, ec) || need_interrupt;
+ io_service_.post_deferred_completions(ops);
+ if (need_interrupt)
+ interrupter_.interrupt();
+}
+
+::pollfd& dev_poll_reactor::add_pending_event_change(int descriptor)
+{
+ hash_map<int, std::size_t>::iterator iter
+ = pending_event_change_index_.find(descriptor);
+ if (iter == pending_event_change_index_.end())
+ {
+ std::size_t index = pending_event_changes_.size();
+ pending_event_changes_.reserve(pending_event_changes_.size() + 1);
+ pending_event_change_index_.insert(std::make_pair(descriptor, index));
+ pending_event_changes_.push_back(::pollfd());
+ pending_event_changes_[index].fd = descriptor;
+ pending_event_changes_[index].revents = 0;
+ return pending_event_changes_[index];
+ }
+ else
+ {
+ return pending_event_changes_[iter->second];
+ }
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_DEV_POLL)
+
+#endif // ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_IPP
diff --git a/ext/asio/asio/detail/impl/epoll_reactor.hpp b/ext/asio/asio/detail/impl/epoll_reactor.hpp
new file mode 100644
index 0000000..9f50a23
--- /dev/null
+++ b/ext/asio/asio/detail/impl/epoll_reactor.hpp
@@ -0,0 +1,75 @@
+//
+// detail/impl/epoll_reactor.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_EPOLL_REACTOR_HPP
+#define ASIO_DETAIL_IMPL_EPOLL_REACTOR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#if defined(ASIO_HAS_EPOLL)
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Time_Traits>
+void epoll_reactor::add_timer_queue(timer_queue<Time_Traits>& queue)
+{
+ do_add_timer_queue(queue);
+}
+
+template <typename Time_Traits>
+void epoll_reactor::remove_timer_queue(timer_queue<Time_Traits>& queue)
+{
+ do_remove_timer_queue(queue);
+}
+
+template <typename Time_Traits>
+void epoll_reactor::schedule_timer(timer_queue<Time_Traits>& queue,
+ const typename Time_Traits::time_type& time,
+ typename timer_queue<Time_Traits>::per_timer_data& timer, timer_op* op)
+{
+ mutex::scoped_lock lock(mutex_);
+
+ if (shutdown_)
+ {
+ io_service_.post_immediate_completion(op);
+ return;
+ }
+
+ bool earliest = queue.enqueue_timer(time, timer, op);
+ io_service_.work_started();
+ if (earliest)
+ update_timeout();
+}
+
+template <typename Time_Traits>
+std::size_t epoll_reactor::cancel_timer(timer_queue<Time_Traits>& queue,
+ typename timer_queue<Time_Traits>::per_timer_data& timer)
+{
+ mutex::scoped_lock lock(mutex_);
+ op_queue<operation> ops;
+ std::size_t n = queue.cancel_timer(timer, ops);
+ lock.unlock();
+ io_service_.post_deferred_completions(ops);
+ return n;
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_EPOLL)
+
+#endif // ASIO_DETAIL_IMPL_EPOLL_REACTOR_HPP
diff --git a/ext/asio/asio/detail/impl/epoll_reactor.ipp b/ext/asio/asio/detail/impl/epoll_reactor.ipp
new file mode 100644
index 0000000..a95b8f2
--- /dev/null
+++ b/ext/asio/asio/detail/impl/epoll_reactor.ipp
@@ -0,0 +1,390 @@
+//
+// detail/impl/epoll_reactor.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP
+#define ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_EPOLL)
+
+#include <cstddef>
+#include <sys/epoll.h>
+#include "asio/detail/epoll_reactor.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#if defined(ASIO_HAS_TIMERFD)
+# include <sys/timerfd.h>
+#endif // defined(ASIO_HAS_TIMERFD)
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+epoll_reactor::epoll_reactor(asio::io_service& io_service)
+ : asio::detail::service_base<epoll_reactor>(io_service),
+ io_service_(use_service<io_service_impl>(io_service)),
+ mutex_(),
+ epoll_fd_(do_epoll_create()),
+#if defined(ASIO_HAS_TIMERFD)
+ timer_fd_(timerfd_create(CLOCK_MONOTONIC, 0)),
+#else // defined(ASIO_HAS_TIMERFD)
+ timer_fd_(-1),
+#endif // defined(ASIO_HAS_TIMERFD)
+ interrupter_(),
+ shutdown_(false)
+{
+ // Add the interrupter's descriptor to epoll.
+ epoll_event ev = { 0, { 0 } };
+ ev.events = EPOLLIN | EPOLLERR | EPOLLET;
+ ev.data.ptr = &interrupter_;
+ epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev);
+ interrupter_.interrupt();
+
+ // Add the timer descriptor to epoll.
+ if (timer_fd_ != -1)
+ {
+ ev.events = EPOLLIN | EPOLLERR;
+ ev.data.ptr = &timer_fd_;
+ epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, timer_fd_, &ev);
+ }
+}
+
+epoll_reactor::~epoll_reactor()
+{
+ close(epoll_fd_);
+ if (timer_fd_ != -1)
+ close(timer_fd_);
+}
+
+void epoll_reactor::shutdown_service()
+{
+ mutex::scoped_lock lock(mutex_);
+ shutdown_ = true;
+ lock.unlock();
+
+ op_queue<operation> ops;
+
+ while (descriptor_state* state = registered_descriptors_.first())
+ {
+ for (int i = 0; i < max_ops; ++i)
+ ops.push(state->op_queue_[i]);
+ state->shutdown_ = true;
+ registered_descriptors_.free(state);
+ }
+
+ timer_queues_.get_all_timers(ops);
+}
+
+void epoll_reactor::init_task()
+{
+ io_service_.init_task();
+}
+
+int epoll_reactor::register_descriptor(socket_type descriptor,
+ epoll_reactor::per_descriptor_data& descriptor_data)
+{
+ mutex::scoped_lock lock(registered_descriptors_mutex_);
+
+ descriptor_data = registered_descriptors_.alloc();
+ descriptor_data->shutdown_ = false;
+
+ lock.unlock();
+
+ epoll_event ev = { 0, { 0 } };
+ ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLOUT | EPOLLPRI | EPOLLET;
+ ev.data.ptr = descriptor_data;
+ int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev);
+ if (result != 0)
+ return errno;
+
+ return 0;
+}
+
+void epoll_reactor::start_op(int op_type, socket_type descriptor,
+ epoll_reactor::per_descriptor_data& descriptor_data,
+ reactor_op* op, bool allow_speculative)
+{
+ if (!descriptor_data)
+ {
+ op->ec_ = asio::error::bad_descriptor;
+ post_immediate_completion(op);
+ return;
+ }
+
+ mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
+
+ if (descriptor_data->shutdown_)
+ {
+ post_immediate_completion(op);
+ return;
+ }
+
+ if (descriptor_data->op_queue_[op_type].empty())
+ {
+ if (allow_speculative
+ && (op_type != read_op
+ || descriptor_data->op_queue_[except_op].empty()))
+ {
+ if (op->perform())
+ {
+ descriptor_lock.unlock();
+ io_service_.post_immediate_completion(op);
+ return;
+ }
+ }
+ else
+ {
+ epoll_event ev = { 0, { 0 } };
+ ev.events = EPOLLIN | EPOLLERR | EPOLLHUP
+ | EPOLLOUT | EPOLLPRI | EPOLLET;
+ ev.data.ptr = descriptor_data;
+ epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev);
+ }
+ }
+
+ descriptor_data->op_queue_[op_type].push(op);
+ io_service_.work_started();
+}
+
+void epoll_reactor::cancel_ops(socket_type,
+ epoll_reactor::per_descriptor_data& descriptor_data)
+{
+ if (!descriptor_data)
+ return;
+
+ mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
+
+ op_queue<operation> ops;
+ for (int i = 0; i < max_ops; ++i)
+ {
+ while (reactor_op* op = descriptor_data->op_queue_[i].front())
+ {
+ op->ec_ = asio::error::operation_aborted;
+ descriptor_data->op_queue_[i].pop();
+ ops.push(op);
+ }
+ }
+
+ descriptor_lock.unlock();
+
+ io_service_.post_deferred_completions(ops);
+}
+
+void epoll_reactor::close_descriptor(socket_type,
+ epoll_reactor::per_descriptor_data& descriptor_data)
+{
+ if (!descriptor_data)
+ return;
+
+ mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
+ mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_);
+
+ if (!descriptor_data->shutdown_)
+ {
+ // Remove the descriptor from the set of known descriptors. The descriptor
+ // will be automatically removed from the epoll set when it is closed.
+
+ op_queue<operation> ops;
+ for (int i = 0; i < max_ops; ++i)
+ {
+ while (reactor_op* op = descriptor_data->op_queue_[i].front())
+ {
+ op->ec_ = asio::error::operation_aborted;
+ descriptor_data->op_queue_[i].pop();
+ ops.push(op);
+ }
+ }
+
+ descriptor_data->shutdown_ = true;
+
+ descriptor_lock.unlock();
+
+ registered_descriptors_.free(descriptor_data);
+ descriptor_data = 0;
+
+ descriptors_lock.unlock();
+
+ io_service_.post_deferred_completions(ops);
+ }
+}
+
+void epoll_reactor::run(bool block, op_queue<operation>& ops)
+{
+ // Calculate a timeout only if timerfd is not used.
+ int timeout;
+ if (timer_fd_ != -1)
+ timeout = block ? -1 : 0;
+ else
+ {
+ mutex::scoped_lock lock(mutex_);
+ timeout = block ? get_timeout() : 0;
+ }
+
+ // Block on the epoll descriptor.
+ epoll_event events[128];
+ int num_events = epoll_wait(epoll_fd_, events, 128, timeout);
+
+#if defined(ASIO_HAS_TIMERFD)
+ bool check_timers = (timer_fd_ == -1);
+#else // defined(ASIO_HAS_TIMERFD)
+ bool check_timers = true;
+#endif // defined(ASIO_HAS_TIMERFD)
+
+ // Dispatch the waiting events.
+ for (int i = 0; i < num_events; ++i)
+ {
+ void* ptr = events[i].data.ptr;
+ if (ptr == &interrupter_)
+ {
+ // No need to reset the interrupter since we're leaving the descriptor
+ // in a ready-to-read state and relying on edge-triggered notifications
+ // to make it so that we only get woken up when the descriptor's epoll
+ // registration is updated.
+
+#if defined(ASIO_HAS_TIMERFD)
+ if (timer_fd_ == -1)
+ check_timers = true;
+#else // defined(ASIO_HAS_TIMERFD)
+ check_timers = true;
+#endif // defined(ASIO_HAS_TIMERFD)
+ }
+#if defined(ASIO_HAS_TIMERFD)
+ else if (ptr == &timer_fd_)
+ {
+ check_timers = true;
+ }
+#endif // defined(ASIO_HAS_TIMERFD)
+ else
+ {
+ descriptor_state* descriptor_data = static_cast<descriptor_state*>(ptr);
+ mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
+
+ // Exception operations must be processed first to ensure that any
+ // out-of-band data is read before normal data.
+ static const int flag[max_ops] = { EPOLLIN, EPOLLOUT, EPOLLPRI };
+ for (int j = max_ops - 1; j >= 0; --j)
+ {
+ if (events[i].events & (flag[j] | EPOLLERR | EPOLLHUP))
+ {
+ while (reactor_op* op = descriptor_data->op_queue_[j].front())
+ {
+ if (op->perform())
+ {
+ descriptor_data->op_queue_[j].pop();
+ ops.push(op);
+ }
+ else
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (check_timers)
+ {
+ mutex::scoped_lock common_lock(mutex_);
+ timer_queues_.get_ready_timers(ops);
+
+#if defined(ASIO_HAS_TIMERFD)
+ if (timer_fd_ != -1)
+ {
+ itimerspec new_timeout;
+ itimerspec old_timeout;
+ int flags = get_timeout(new_timeout);
+ timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout);
+ }
+#endif // defined(ASIO_HAS_TIMERFD)
+ }
+}
+
+void epoll_reactor::interrupt()
+{
+ epoll_event ev = { 0, { 0 } };
+ ev.events = EPOLLIN | EPOLLERR | EPOLLET;
+ ev.data.ptr = &interrupter_;
+ epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, interrupter_.read_descriptor(), &ev);
+}
+
+int epoll_reactor::do_epoll_create()
+{
+ int fd = epoll_create(epoll_size);
+ if (fd == -1)
+ {
+ asio::error_code ec(errno,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "epoll");
+ }
+ return fd;
+}
+
+void epoll_reactor::do_add_timer_queue(timer_queue_base& queue)
+{
+ mutex::scoped_lock lock(mutex_);
+ timer_queues_.insert(&queue);
+}
+
+void epoll_reactor::do_remove_timer_queue(timer_queue_base& queue)
+{
+ mutex::scoped_lock lock(mutex_);
+ timer_queues_.erase(&queue);
+}
+
+void epoll_reactor::update_timeout()
+{
+#if defined(ASIO_HAS_TIMERFD)
+ if (timer_fd_ != -1)
+ {
+ itimerspec new_timeout;
+ itimerspec old_timeout;
+ int flags = get_timeout(new_timeout);
+ timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout);
+ return;
+ }
+#endif // defined(ASIO_HAS_TIMERFD)
+ interrupt();
+}
+
+int epoll_reactor::get_timeout()
+{
+ // By default we will wait no longer than 5 minutes. This will ensure that
+ // any changes to the system clock are detected after no longer than this.
+ return timer_queues_.wait_duration_msec(5 * 60 * 1000);
+}
+
+#if defined(ASIO_HAS_TIMERFD)
+int epoll_reactor::get_timeout(itimerspec& ts)
+{
+ ts.it_interval.tv_sec = 0;
+ ts.it_interval.tv_nsec = 0;
+
+ long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000);
+ ts.it_value.tv_sec = usec / 1000000;
+ ts.it_value.tv_nsec = usec ? (usec % 1000000) * 1000 : 1;
+
+ return usec ? 0 : TFD_TIMER_ABSTIME;
+}
+#endif // defined(ASIO_HAS_TIMERFD)
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_EPOLL)
+
+#endif // ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP
diff --git a/ext/asio/asio/detail/impl/eventfd_select_interrupter.ipp b/ext/asio/asio/detail/impl/eventfd_select_interrupter.ipp
new file mode 100644
index 0000000..959d356
--- /dev/null
+++ b/ext/asio/asio/detail/impl/eventfd_select_interrupter.ipp
@@ -0,0 +1,125 @@
+//
+// detail/impl/eventfd_select_interrupter.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2008 Roelof Naude (roelof.naude at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP
+#define ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_EVENTFD)
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
+# include <asm/unistd.h>
+#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
+# include <sys/eventfd.h>
+#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
+#include "asio/detail/eventfd_select_interrupter.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+eventfd_select_interrupter::eventfd_select_interrupter()
+{
+#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
+ write_descriptor_ = read_descriptor_ = syscall(__NR_eventfd, 0);
+#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
+ write_descriptor_ = read_descriptor_ = ::eventfd(0, 0);
+#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
+ if (read_descriptor_ != -1)
+ {
+ ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK);
+ }
+ else
+ {
+ int pipe_fds[2];
+ if (pipe(pipe_fds) == 0)
+ {
+ read_descriptor_ = pipe_fds[0];
+ ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK);
+ write_descriptor_ = pipe_fds[1];
+ ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK);
+ }
+ else
+ {
+ asio::error_code ec(errno,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "eventfd_select_interrupter");
+ }
+ }
+}
+
+eventfd_select_interrupter::~eventfd_select_interrupter()
+{
+ if (write_descriptor_ != -1 && write_descriptor_ != read_descriptor_)
+ ::close(write_descriptor_);
+ if (read_descriptor_ != -1)
+ ::close(read_descriptor_);
+}
+
+void eventfd_select_interrupter::interrupt()
+{
+ uint64_t counter(1UL);
+ int result = ::write(write_descriptor_, &counter, sizeof(uint64_t));
+ (void)result;
+}
+
+bool eventfd_select_interrupter::reset()
+{
+ if (write_descriptor_ == read_descriptor_)
+ {
+ for (;;)
+ {
+ // Only perform one read. The kernel maintains an atomic counter.
+ uint64_t counter(0);
+ errno = 0;
+ int bytes_read = ::read(read_descriptor_, &counter, sizeof(uint64_t));
+ if (bytes_read < 0 && errno == EINTR)
+ continue;
+ bool was_interrupted = (bytes_read > 0);
+ return was_interrupted;
+ }
+ }
+ else
+ {
+ for (;;)
+ {
+ // Clear all data from the pipe.
+ char data[1024];
+ int bytes_read = ::read(read_descriptor_, data, sizeof(data));
+ if (bytes_read < 0 && errno == EINTR)
+ continue;
+ bool was_interrupted = (bytes_read > 0);
+ while (bytes_read == sizeof(data))
+ bytes_read = ::read(read_descriptor_, data, sizeof(data));
+ return was_interrupted;
+ }
+ }
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_EVENTFD)
+
+#endif // ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP
diff --git a/ext/asio/asio/detail/impl/kqueue_reactor.hpp b/ext/asio/asio/detail/impl/kqueue_reactor.hpp
new file mode 100644
index 0000000..7950c3b
--- /dev/null
+++ b/ext/asio/asio/detail/impl/kqueue_reactor.hpp
@@ -0,0 +1,79 @@
+//
+// detail/impl/kqueue_reactor.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_KQUEUE_REACTOR_HPP
+#define ASIO_DETAIL_IMPL_KQUEUE_REACTOR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_KQUEUE)
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Time_Traits>
+void kqueue_reactor::add_timer_queue(timer_queue<Time_Traits>& queue)
+{
+ do_add_timer_queue(queue);
+}
+
+// Remove a timer queue from the reactor.
+template <typename Time_Traits>
+void kqueue_reactor::remove_timer_queue(timer_queue<Time_Traits>& queue)
+{
+ do_remove_timer_queue(queue);
+}
+
+template <typename Time_Traits>
+void kqueue_reactor::schedule_timer(timer_queue<Time_Traits>& queue,
+ const typename Time_Traits::time_type& time,
+ typename timer_queue<Time_Traits>::per_timer_data& timer, timer_op* op)
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ if (shutdown_)
+ {
+ io_service_.post_immediate_completion(op);
+ return;
+ }
+
+ bool earliest = queue.enqueue_timer(time, timer, op);
+ io_service_.work_started();
+ if (earliest)
+ interrupt();
+}
+
+template <typename Time_Traits>
+std::size_t kqueue_reactor::cancel_timer(timer_queue<Time_Traits>& queue,
+ typename timer_queue<Time_Traits>::per_timer_data& timer)
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ op_queue<operation> ops;
+ std::size_t n = queue.cancel_timer(timer, ops);
+ lock.unlock();
+ io_service_.post_deferred_completions(ops);
+ return n;
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_KQUEUE)
+
+#endif // ASIO_DETAIL_IMPL_KQUEUE_REACTOR_HPP
diff --git a/ext/asio/asio/detail/impl/kqueue_reactor.ipp b/ext/asio/asio/detail/impl/kqueue_reactor.ipp
new file mode 100644
index 0000000..8db77cb
--- /dev/null
+++ b/ext/asio/asio/detail/impl/kqueue_reactor.ipp
@@ -0,0 +1,385 @@
+//
+// detail/impl/kqueue_reactor.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP
+#define ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_KQUEUE)
+
+#include "asio/detail/kqueue_reactor.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+#if defined(__NetBSD__)
+# define ASIO_KQUEUE_EV_SET(ev, ident, filt, flags, fflags, data, udata) \
+ EV_SET(ev, ident, filt, flags, fflags, \
+ data, reinterpret_cast<intptr_t>(udata))
+#else
+# define ASIO_KQUEUE_EV_SET(ev, ident, filt, flags, fflags, data, udata) \
+ EV_SET(ev, ident, filt, flags, fflags, data, udata)
+#endif
+
+namespace asio {
+namespace detail {
+
+kqueue_reactor::kqueue_reactor(asio::io_service& io_service)
+ : asio::detail::service_base<kqueue_reactor>(io_service),
+ io_service_(use_service<io_service_impl>(io_service)),
+ mutex_(),
+ kqueue_fd_(do_kqueue_create()),
+ interrupter_(),
+ shutdown_(false)
+{
+ // The interrupter is put into a permanently readable state. Whenever we
+ // want to interrupt the blocked kevent call we register a one-shot read
+ // operation against the descriptor.
+ interrupter_.interrupt();
+}
+
+kqueue_reactor::~kqueue_reactor()
+{
+ close(kqueue_fd_);
+}
+
+void kqueue_reactor::shutdown_service()
+{
+ mutex::scoped_lock lock(mutex_);
+ shutdown_ = true;
+ lock.unlock();
+
+ op_queue<operation> ops;
+
+ while (descriptor_state* state = registered_descriptors_.first())
+ {
+ for (int i = 0; i < max_ops; ++i)
+ ops.push(state->op_queue_[i]);
+ state->shutdown_ = true;
+ registered_descriptors_.free(state);
+ }
+
+ timer_queues_.get_all_timers(ops);
+}
+
+void kqueue_reactor::init_task()
+{
+ io_service_.init_task();
+}
+
+int kqueue_reactor::register_descriptor(socket_type,
+ kqueue_reactor::per_descriptor_data& descriptor_data)
+{
+ mutex::scoped_lock lock(registered_descriptors_mutex_);
+
+ descriptor_data = registered_descriptors_.alloc();
+ descriptor_data->shutdown_ = false;
+
+ return 0;
+}
+
+void kqueue_reactor::start_op(int op_type, socket_type descriptor,
+ kqueue_reactor::per_descriptor_data& descriptor_data,
+ reactor_op* op, bool allow_speculative)
+{
+ if (!descriptor_data)
+ {
+ op->ec_ = asio::error::bad_descriptor;
+ post_immediate_completion(op);
+ return;
+ }
+
+ mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
+
+ if (descriptor_data->shutdown_)
+ {
+ post_immediate_completion(op);
+ return;
+ }
+
+ bool first = descriptor_data->op_queue_[op_type].empty();
+ if (first)
+ {
+ if (allow_speculative)
+ {
+ if (op_type != read_op || descriptor_data->op_queue_[except_op].empty())
+ {
+ if (op->perform())
+ {
+ descriptor_lock.unlock();
+ io_service_.post_immediate_completion(op);
+ return;
+ }
+ }
+ }
+ }
+
+ descriptor_data->op_queue_[op_type].push(op);
+ io_service_.work_started();
+
+ if (first)
+ {
+ struct kevent event;
+ switch (op_type)
+ {
+ case read_op:
+ ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ,
+ EV_ADD | EV_ONESHOT, 0, 0, descriptor_data);
+ break;
+ case write_op:
+ ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_WRITE,
+ EV_ADD | EV_ONESHOT, 0, 0, descriptor_data);
+ break;
+ case except_op:
+ if (!descriptor_data->op_queue_[read_op].empty())
+ return; // Already registered for read events.
+ ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ,
+ EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data);
+ break;
+ }
+
+ if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
+ {
+ op->ec_ = asio::error_code(errno,
+ asio::error::get_system_category());
+ descriptor_data->op_queue_[op_type].pop();
+ io_service_.post_deferred_completion(op);
+ }
+ }
+}
+
+void kqueue_reactor::cancel_ops(socket_type,
+ kqueue_reactor::per_descriptor_data& descriptor_data)
+{
+ if (!descriptor_data)
+ return;
+
+ mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
+
+ op_queue<operation> ops;
+ for (int i = 0; i < max_ops; ++i)
+ {
+ while (reactor_op* op = descriptor_data->op_queue_[i].front())
+ {
+ op->ec_ = asio::error::operation_aborted;
+ descriptor_data->op_queue_[i].pop();
+ ops.push(op);
+ }
+ }
+
+ descriptor_lock.unlock();
+
+ io_service_.post_deferred_completions(ops);
+}
+
+void kqueue_reactor::close_descriptor(socket_type,
+ kqueue_reactor::per_descriptor_data& descriptor_data)
+{
+ if (!descriptor_data)
+ return;
+
+ mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
+ mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_);
+
+ if (!descriptor_data->shutdown_)
+ {
+ // Remove the descriptor from the set of known descriptors. The descriptor
+ // will be automatically removed from the kqueue set when it is closed.
+
+ op_queue<operation> ops;
+ for (int i = 0; i < max_ops; ++i)
+ {
+ while (reactor_op* op = descriptor_data->op_queue_[i].front())
+ {
+ op->ec_ = asio::error::operation_aborted;
+ descriptor_data->op_queue_[i].pop();
+ ops.push(op);
+ }
+ }
+
+ descriptor_data->shutdown_ = true;
+
+ descriptor_lock.unlock();
+
+ registered_descriptors_.free(descriptor_data);
+ descriptor_data = 0;
+
+ descriptors_lock.unlock();
+
+ io_service_.post_deferred_completions(ops);
+ }
+}
+
+void kqueue_reactor::run(bool block, op_queue<operation>& ops)
+{
+ mutex::scoped_lock lock(mutex_);
+
+ // Determine how long to block while waiting for events.
+ timespec timeout_buf = { 0, 0 };
+ timespec* timeout = block ? get_timeout(timeout_buf) : &timeout_buf;
+
+ lock.unlock();
+
+ // Block on the kqueue descriptor.
+ struct kevent events[128];
+ int num_events = kevent(kqueue_fd_, 0, 0, events, 128, timeout);
+
+ // Dispatch the waiting events.
+ for (int i = 0; i < num_events; ++i)
+ {
+ int descriptor = events[i].ident;
+ void* ptr = reinterpret_cast<void*>(events[i].udata);
+ if (ptr == &interrupter_)
+ {
+ // No need to reset the interrupter since we're leaving the descriptor
+ // in a ready-to-read state and relying on one-shot notifications.
+ }
+ else
+ {
+ descriptor_state* descriptor_data = static_cast<descriptor_state*>(ptr);
+ mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
+
+ // Exception operations must be processed first to ensure that any
+ // out-of-band data is read before normal data.
+#if defined(__NetBSD__)
+ static const unsigned int filter[max_ops] =
+#else
+ static const int filter[max_ops] =
+#endif
+ { EVFILT_READ, EVFILT_WRITE, EVFILT_READ };
+ for (int j = max_ops - 1; j >= 0; --j)
+ {
+ if (events[i].filter == filter[j])
+ {
+ if (j != except_op || events[i].flags & EV_OOBAND)
+ {
+ while (reactor_op* op = descriptor_data->op_queue_[j].front())
+ {
+ if (events[i].flags & EV_ERROR)
+ {
+ op->ec_ = asio::error_code(events[i].data,
+ asio::error::get_system_category());
+ descriptor_data->op_queue_[j].pop();
+ ops.push(op);
+ }
+ if (op->perform())
+ {
+ descriptor_data->op_queue_[j].pop();
+ ops.push(op);
+ }
+ else
+ break;
+ }
+ }
+ }
+ }
+
+ // Renew registration for event notifications.
+ struct kevent event;
+ switch (events[i].filter)
+ {
+ case EVFILT_READ:
+ if (!descriptor_data->op_queue_[read_op].empty())
+ ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ,
+ EV_ADD | EV_ONESHOT, 0, 0, descriptor_data);
+ else if (!descriptor_data->op_queue_[except_op].empty())
+ ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ,
+ EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data);
+ else
+ continue;
+ case EVFILT_WRITE:
+ if (!descriptor_data->op_queue_[write_op].empty())
+ ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_WRITE,
+ EV_ADD | EV_ONESHOT, 0, 0, descriptor_data);
+ else
+ continue;
+ default:
+ break;
+ }
+ if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
+ {
+ asio::error_code error(errno,
+ asio::error::get_system_category());
+ for (int j = 0; j < max_ops; ++j)
+ {
+ while (reactor_op* op = descriptor_data->op_queue_[j].front())
+ {
+ op->ec_ = error;
+ descriptor_data->op_queue_[j].pop();
+ ops.push(op);
+ }
+ }
+ }
+ }
+ }
+
+ lock.lock();
+ timer_queues_.get_ready_timers(ops);
+}
+
+void kqueue_reactor::interrupt()
+{
+ struct kevent event;
+ ASIO_KQUEUE_EV_SET(&event, interrupter_.read_descriptor(),
+ EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, &interrupter_);
+ ::kevent(kqueue_fd_, &event, 1, 0, 0, 0);
+}
+
+int kqueue_reactor::do_kqueue_create()
+{
+ int fd = ::kqueue();
+ if (fd == -1)
+ {
+ asio::error_code ec(errno,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "kqueue");
+ }
+ return fd;
+}
+
+void kqueue_reactor::do_add_timer_queue(timer_queue_base& queue)
+{
+ mutex::scoped_lock lock(mutex_);
+ timer_queues_.insert(&queue);
+}
+
+void kqueue_reactor::do_remove_timer_queue(timer_queue_base& queue)
+{
+ mutex::scoped_lock lock(mutex_);
+ timer_queues_.erase(&queue);
+}
+
+timespec* kqueue_reactor::get_timeout(timespec& ts)
+{
+ // By default we will wait no longer than 5 minutes. This will ensure that
+ // any changes to the system clock are detected after no longer than this.
+ long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000);
+ ts.tv_sec = usec / 1000000;
+ ts.tv_nsec = (usec % 1000000) * 1000;
+ return &ts;
+}
+
+} // namespace detail
+} // namespace asio
+
+#undef ASIO_KQUEUE_EV_SET
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_KQUEUE)
+
+#endif // ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP
diff --git a/ext/asio/asio/detail/impl/pipe_select_interrupter.ipp b/ext/asio/asio/detail/impl/pipe_select_interrupter.ipp
new file mode 100644
index 0000000..aac20d4
--- /dev/null
+++ b/ext/asio/asio/detail/impl/pipe_select_interrupter.ipp
@@ -0,0 +1,96 @@
+//
+// detail/impl/pipe_select_interrupter.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_PIPE_SELECT_INTERRUPTER_IPP
+#define ASIO_DETAIL_IMPL_PIPE_SELECT_INTERRUPTER_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if !defined(BOOST_WINDOWS)
+#if !defined(__CYGWIN__)
+#if !defined(__SYMBIAN32__)
+#if !defined(ASIO_HAS_EVENTFD)
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "asio/detail/pipe_select_interrupter.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+pipe_select_interrupter::pipe_select_interrupter()
+{
+ int pipe_fds[2];
+ if (pipe(pipe_fds) == 0)
+ {
+ read_descriptor_ = pipe_fds[0];
+ ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK);
+ write_descriptor_ = pipe_fds[1];
+ ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK);
+ }
+ else
+ {
+ asio::error_code ec(errno,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "pipe_select_interrupter");
+ }
+}
+
+pipe_select_interrupter::~pipe_select_interrupter()
+{
+ if (read_descriptor_ != -1)
+ ::close(read_descriptor_);
+ if (write_descriptor_ != -1)
+ ::close(write_descriptor_);
+}
+
+void pipe_select_interrupter::interrupt()
+{
+ char byte = 0;
+ int result = ::write(write_descriptor_, &byte, 1);
+ (void)result;
+}
+
+bool pipe_select_interrupter::reset()
+{
+ for (;;)
+ {
+ char data[1024];
+ int bytes_read = ::read(read_descriptor_, data, sizeof(data));
+ if (bytes_read < 0 && errno == EINTR)
+ continue;
+ bool was_interrupted = (bytes_read > 0);
+ while (bytes_read == sizeof(data))
+ bytes_read = ::read(read_descriptor_, data, sizeof(data));
+ return was_interrupted;
+ }
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // !defined(ASIO_HAS_EVENTFD)
+#endif // !defined(__SYMBIAN32__)
+#endif // !defined(__CYGWIN__)
+#endif // !defined(BOOST_WINDOWS)
+
+#endif // ASIO_DETAIL_IMPL_PIPE_SELECT_INTERRUPTER_IPP
diff --git a/ext/asio/asio/detail/impl/posix_event.ipp b/ext/asio/asio/detail/impl/posix_event.ipp
new file mode 100644
index 0000000..3206418
--- /dev/null
+++ b/ext/asio/asio/detail/impl/posix_event.ipp
@@ -0,0 +1,46 @@
+//
+// detail/impl/posix_event.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_POSIX_EVENT_IPP
+#define ASIO_DETAIL_IMPL_POSIX_EVENT_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
+
+#include "asio/detail/posix_event.hpp"
+#include "asio/detail/throw_error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+posix_event::posix_event()
+ : signalled_(false)
+{
+ int error = ::pthread_cond_init(&cond_, 0);
+ asio::error_code ec(error,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "event");
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
+
+#endif // ASIO_DETAIL_IMPL_POSIX_EVENT_IPP
diff --git a/ext/asio/asio/detail/impl/posix_mutex.ipp b/ext/asio/asio/detail/impl/posix_mutex.ipp
new file mode 100644
index 0000000..12b6659
--- /dev/null
+++ b/ext/asio/asio/detail/impl/posix_mutex.ipp
@@ -0,0 +1,46 @@
+//
+// detail/impl/posix_mutex.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_POSIX_MUTEX_IPP
+#define ASIO_DETAIL_IMPL_POSIX_MUTEX_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
+
+#include "asio/detail/posix_mutex.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+posix_mutex::posix_mutex()
+{
+ int error = ::pthread_mutex_init(&mutex_, 0);
+ asio::error_code ec(error,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "mutex");
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
+
+#endif // ASIO_DETAIL_IMPL_POSIX_MUTEX_IPP
diff --git a/ext/asio/asio/detail/impl/posix_thread.ipp b/ext/asio/asio/detail/impl/posix_thread.ipp
new file mode 100644
index 0000000..ce91c57
--- /dev/null
+++ b/ext/asio/asio/detail/impl/posix_thread.ipp
@@ -0,0 +1,74 @@
+//
+// detail/impl/posix_thread.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_POSIX_THREAD_IPP
+#define ASIO_DETAIL_IMPL_POSIX_THREAD_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
+
+#include "asio/detail/posix_thread.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+posix_thread::~posix_thread()
+{
+ if (!joined_)
+ ::pthread_detach(thread_);
+}
+
+void posix_thread::join()
+{
+ if (!joined_)
+ {
+ ::pthread_join(thread_, 0);
+ joined_ = true;
+ }
+}
+
+void posix_thread::start_thread(func_base* arg)
+{
+ int error = ::pthread_create(&thread_, 0,
+ asio_detail_posix_thread_function, arg);
+ if (error != 0)
+ {
+ delete arg;
+ asio::error_code ec(error,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "thread");
+ }
+}
+
+void* asio_detail_posix_thread_function(void* arg)
+{
+ posix_thread::auto_func_base_ptr func = {
+ static_cast<posix_thread::func_base*>(arg) };
+ func.ptr->run();
+ return 0;
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
+
+#endif // ASIO_DETAIL_IMPL_POSIX_THREAD_IPP
diff --git a/ext/asio/asio/detail/impl/posix_tss_ptr.ipp b/ext/asio/asio/detail/impl/posix_tss_ptr.ipp
new file mode 100644
index 0000000..89b40f7
--- /dev/null
+++ b/ext/asio/asio/detail/impl/posix_tss_ptr.ipp
@@ -0,0 +1,46 @@
+//
+// detail/impl/posix_tss_ptr.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_POSIX_TSS_PTR_IPP
+#define ASIO_DETAIL_IMPL_POSIX_TSS_PTR_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
+
+#include "asio/detail/posix_tss_ptr.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+void posix_tss_ptr_create(pthread_key_t& key)
+{
+ int error = ::pthread_key_create(&key, 0);
+ asio::error_code ec(error,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "tss");
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
+
+#endif // ASIO_DETAIL_IMPL_POSIX_TSS_PTR_IPP
diff --git a/ext/asio/asio/detail/impl/reactive_descriptor_service.ipp b/ext/asio/asio/detail/impl/reactive_descriptor_service.ipp
new file mode 100644
index 0000000..706d60f
--- /dev/null
+++ b/ext/asio/asio/detail/impl/reactive_descriptor_service.ipp
@@ -0,0 +1,136 @@
+//
+// detail/impl/reactive_descriptor_service.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_REACTIVE_DESCRIPTOR_SERVICE_IPP
+#define ASIO_DETAIL_IMPL_REACTIVE_DESCRIPTOR_SERVICE_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
+#include "asio/error.hpp"
+#include "asio/detail/reactive_descriptor_service.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+reactive_descriptor_service::reactive_descriptor_service(
+ asio::io_service& io_service)
+ : reactor_(asio::use_service<reactor>(io_service))
+{
+ reactor_.init_task();
+}
+
+void reactive_descriptor_service::shutdown_service()
+{
+}
+
+void reactive_descriptor_service::construct(
+ reactive_descriptor_service::implementation_type& impl)
+{
+ impl.descriptor_ = -1;
+ impl.state_ = 0;
+}
+
+void reactive_descriptor_service::destroy(
+ reactive_descriptor_service::implementation_type& impl)
+{
+ if (is_open(impl))
+ reactor_.close_descriptor(impl.descriptor_, impl.reactor_data_);
+
+ asio::error_code ignored_ec;
+ descriptor_ops::close(impl.descriptor_, impl.state_, ignored_ec);
+}
+
+asio::error_code reactive_descriptor_service::assign(
+ reactive_descriptor_service::implementation_type& impl,
+ const native_type& native_descriptor, asio::error_code& ec)
+{
+ if (is_open(impl))
+ {
+ ec = asio::error::already_open;
+ return ec;
+ }
+
+ if (int err = reactor_.register_descriptor(
+ native_descriptor, impl.reactor_data_))
+ {
+ ec = asio::error_code(err,
+ asio::error::get_system_category());
+ return ec;
+ }
+
+ impl.descriptor_ = native_descriptor;
+ impl.state_ = 0;
+ ec = asio::error_code();
+ return ec;
+}
+
+asio::error_code reactive_descriptor_service::close(
+ reactive_descriptor_service::implementation_type& impl,
+ asio::error_code& ec)
+{
+ if (is_open(impl))
+ reactor_.close_descriptor(impl.descriptor_, impl.reactor_data_);
+
+ if (descriptor_ops::close(impl.descriptor_, impl.state_, ec) == 0)
+ construct(impl);
+
+ return ec;
+}
+
+asio::error_code reactive_descriptor_service::cancel(
+ reactive_descriptor_service::implementation_type& impl,
+ asio::error_code& ec)
+{
+ if (!is_open(impl))
+ {
+ ec = asio::error::bad_descriptor;
+ return ec;
+ }
+
+ reactor_.cancel_ops(impl.descriptor_, impl.reactor_data_);
+ ec = asio::error_code();
+ return ec;
+}
+
+void reactive_descriptor_service::start_op(
+ reactive_descriptor_service::implementation_type& impl,
+ int op_type, reactor_op* op, bool non_blocking, bool noop)
+{
+ if (!noop)
+ {
+ if ((impl.state_ & descriptor_ops::non_blocking) ||
+ descriptor_ops::set_internal_non_blocking(
+ impl.descriptor_, impl.state_, op->ec_))
+ {
+ reactor_.start_op(op_type, impl.descriptor_,
+ impl.reactor_data_, op, non_blocking);
+ return;
+ }
+ }
+
+ reactor_.post_immediate_completion(op);
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
+#endif // ASIO_DETAIL_IMPL_REACTIVE_DESCRIPTOR_SERVICE_IPP
diff --git a/ext/asio/asio/detail/impl/reactive_serial_port_service.ipp b/ext/asio/asio/detail/impl/reactive_serial_port_service.ipp
new file mode 100644
index 0000000..c4df793
--- /dev/null
+++ b/ext/asio/asio/detail/impl/reactive_serial_port_service.ipp
@@ -0,0 +1,151 @@
+//
+// detail/impl/reactive_serial_port_service.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2008 Rep Invariant Systems, Inc. (info at repinvariant.com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_REACTIVE_SERIAL_PORT_SERVICE_IPP
+#define ASIO_DETAIL_IMPL_REACTIVE_SERIAL_PORT_SERVICE_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_SERIAL_PORT)
+#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
+#include <cstring>
+#include "asio/detail/reactive_serial_port_service.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+reactive_serial_port_service::reactive_serial_port_service(
+ asio::io_service& io_service)
+ : descriptor_service_(io_service)
+{
+}
+
+void reactive_serial_port_service::shutdown_service()
+{
+ descriptor_service_.shutdown_service();
+}
+
+asio::error_code reactive_serial_port_service::open(
+ reactive_serial_port_service::implementation_type& impl,
+ const std::string& device, asio::error_code& ec)
+{
+ if (is_open(impl))
+ {
+ ec = asio::error::already_open;
+ return ec;
+ }
+
+ descriptor_ops::state_type state = 0;
+ int fd = descriptor_ops::open(device.c_str(),
+ O_RDWR | O_NONBLOCK | O_NOCTTY, ec);
+ if (fd < 0)
+ return ec;
+
+ int s = descriptor_ops::fcntl(fd, F_GETFL, ec);
+ if (s >= 0)
+ s = descriptor_ops::fcntl(fd, F_SETFL, s | O_NONBLOCK, ec);
+ if (s < 0)
+ {
+ asio::error_code ignored_ec;
+ descriptor_ops::close(fd, state, ignored_ec);
+ return ec;
+ }
+
+ // Set up default serial port options.
+ termios ios;
+ errno = 0;
+ s = descriptor_ops::error_wrapper(::tcgetattr(fd, &ios), ec);
+ if (s >= 0)
+ {
+#if defined(_BSD_SOURCE)
+ ::cfmakeraw(&ios);
+#else
+ ios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK
+ | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
+ ios.c_oflag &= ~OPOST;
+ ios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+ ios.c_cflag &= ~(CSIZE | PARENB);
+ ios.c_cflag |= CS8;
+#endif
+ ios.c_iflag |= IGNPAR;
+ ios.c_cflag |= CREAD | CLOCAL;
+ errno = 0;
+ s = descriptor_ops::error_wrapper(::tcsetattr(fd, TCSANOW, &ios), ec);
+ }
+ if (s < 0)
+ {
+ asio::error_code ignored_ec;
+ descriptor_ops::close(fd, state, ignored_ec);
+ return ec;
+ }
+
+ // We're done. Take ownership of the serial port descriptor.
+ if (descriptor_service_.assign(impl, fd, ec))
+ {
+ asio::error_code ignored_ec;
+ descriptor_ops::close(fd, state, ignored_ec);
+ }
+
+ return ec;
+}
+
+asio::error_code reactive_serial_port_service::do_set_option(
+ reactive_serial_port_service::implementation_type& impl,
+ reactive_serial_port_service::store_function_type store,
+ const void* option, asio::error_code& ec)
+{
+ termios ios;
+ errno = 0;
+ descriptor_ops::error_wrapper(::tcgetattr(
+ descriptor_service_.native(impl), &ios), ec);
+ if (ec)
+ return ec;
+
+ if (store(option, ios, ec))
+ return ec;
+
+ errno = 0;
+ descriptor_ops::error_wrapper(::tcsetattr(
+ descriptor_service_.native(impl), TCSANOW, &ios), ec);
+ return ec;
+}
+
+asio::error_code reactive_serial_port_service::do_get_option(
+ const reactive_serial_port_service::implementation_type& impl,
+ reactive_serial_port_service::load_function_type load,
+ void* option, asio::error_code& ec) const
+{
+ termios ios;
+ errno = 0;
+ descriptor_ops::error_wrapper(::tcgetattr(
+ descriptor_service_.native(impl), &ios), ec);
+ if (ec)
+ return ec;
+
+ return load(option, ios, ec);
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+#endif // defined(ASIO_HAS_SERIAL_PORT)
+
+#endif // ASIO_DETAIL_IMPL_REACTIVE_SERIAL_PORT_SERVICE_IPP
diff --git a/ext/asio/asio/detail/impl/reactive_socket_service_base.ipp b/ext/asio/asio/detail/impl/reactive_socket_service_base.ipp
new file mode 100644
index 0000000..54e7944
--- /dev/null
+++ b/ext/asio/asio/detail/impl/reactive_socket_service_base.ipp
@@ -0,0 +1,212 @@
+//
+// detail/reactive_socket_service_base.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP
+#define ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if !defined(ASIO_HAS_IOCP)
+
+#include "asio/detail/reactive_socket_service_base.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+reactive_socket_service_base::reactive_socket_service_base(
+ asio::io_service& io_service)
+ : reactor_(use_service<reactor>(io_service))
+{
+ reactor_.init_task();
+}
+
+void reactive_socket_service_base::shutdown_service()
+{
+}
+
+void reactive_socket_service_base::construct(
+ reactive_socket_service_base::base_implementation_type& impl)
+{
+ impl.socket_ = invalid_socket;
+ impl.state_ = 0;
+}
+
+void reactive_socket_service_base::destroy(
+ reactive_socket_service_base::base_implementation_type& impl)
+{
+ if (impl.socket_ != invalid_socket)
+ {
+ reactor_.close_descriptor(impl.socket_, impl.reactor_data_);
+
+ asio::error_code ignored_ec;
+ socket_ops::close(impl.socket_, impl.state_, true, ignored_ec);
+ }
+}
+
+asio::error_code reactive_socket_service_base::close(
+ reactive_socket_service_base::base_implementation_type& impl,
+ asio::error_code& ec)
+{
+ if (is_open(impl))
+ reactor_.close_descriptor(impl.socket_, impl.reactor_data_);
+
+ if (socket_ops::close(impl.socket_, impl.state_, true, ec) == 0)
+ construct(impl);
+
+ return ec;
+}
+
+asio::error_code reactive_socket_service_base::cancel(
+ reactive_socket_service_base::base_implementation_type& impl,
+ asio::error_code& ec)
+{
+ if (!is_open(impl))
+ {
+ ec = asio::error::bad_descriptor;
+ return ec;
+ }
+
+ reactor_.cancel_ops(impl.socket_, impl.reactor_data_);
+ ec = asio::error_code();
+ return ec;
+}
+
+asio::error_code reactive_socket_service_base::do_open(
+ reactive_socket_service_base::base_implementation_type& impl,
+ int af, int type, int protocol, asio::error_code& ec)
+{
+ if (is_open(impl))
+ {
+ ec = asio::error::already_open;
+ return ec;
+ }
+
+ socket_holder sock(socket_ops::socket(af, type, protocol, ec));
+ if (sock.get() == invalid_socket)
+ return ec;
+
+ if (int err = reactor_.register_descriptor(sock.get(), impl.reactor_data_))
+ {
+ ec = asio::error_code(err,
+ asio::error::get_system_category());
+ return ec;
+ }
+
+ impl.socket_ = sock.release();
+ switch (type)
+ {
+ case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break;
+ case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break;
+ default: impl.state_ = 0; break;
+ }
+ ec = asio::error_code();
+ return ec;
+}
+
+asio::error_code reactive_socket_service_base::do_assign(
+ reactive_socket_service_base::base_implementation_type& impl, int type,
+ const reactive_socket_service_base::native_type& native_socket,
+ asio::error_code& ec)
+{
+ if (is_open(impl))
+ {
+ ec = asio::error::already_open;
+ return ec;
+ }
+
+ if (int err = reactor_.register_descriptor(
+ native_socket, impl.reactor_data_))
+ {
+ ec = asio::error_code(err,
+ asio::error::get_system_category());
+ return ec;
+ }
+
+ impl.socket_ = native_socket;
+ switch (type)
+ {
+ case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break;
+ case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break;
+ default: impl.state_ = 0; break;
+ }
+ ec = asio::error_code();
+ return ec;
+}
+
+void reactive_socket_service_base::start_op(
+ reactive_socket_service_base::base_implementation_type& impl,
+ int op_type, reactor_op* op, bool non_blocking, bool noop)
+{
+ if (!noop)
+ {
+ if ((impl.state_ & socket_ops::non_blocking)
+ || socket_ops::set_internal_non_blocking(
+ impl.socket_, impl.state_, op->ec_))
+ {
+ reactor_.start_op(op_type, impl.socket_,
+ impl.reactor_data_, op, non_blocking);
+ return;
+ }
+ }
+
+ reactor_.post_immediate_completion(op);
+}
+
+void reactive_socket_service_base::start_accept_op(
+ reactive_socket_service_base::base_implementation_type& impl,
+ reactor_op* op, bool peer_is_open)
+{
+ if (!peer_is_open)
+ start_op(impl, reactor::read_op, op, true, false);
+ else
+ {
+ op->ec_ = asio::error::already_open;
+ reactor_.post_immediate_completion(op);
+ }
+}
+
+void reactive_socket_service_base::start_connect_op(
+ reactive_socket_service_base::base_implementation_type& impl,
+ reactor_op* op, const socket_addr_type* addr, size_t addrlen)
+{
+ if ((impl.state_ & socket_ops::non_blocking)
+ || socket_ops::set_internal_non_blocking(
+ impl.socket_, impl.state_, op->ec_))
+ {
+ if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0)
+ {
+ if (op->ec_ == asio::error::in_progress
+ || op->ec_ == asio::error::would_block)
+ {
+ op->ec_ = asio::error_code();
+ reactor_.start_op(reactor::connect_op,
+ impl.socket_, impl.reactor_data_, op, false);
+ return;
+ }
+ }
+ }
+
+ reactor_.post_immediate_completion(op);
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // !defined(ASIO_HAS_IOCP)
+
+#endif // ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP
diff --git a/ext/asio/asio/detail/impl/resolver_service_base.ipp b/ext/asio/asio/detail/impl/resolver_service_base.ipp
new file mode 100644
index 0000000..a844a23
--- /dev/null
+++ b/ext/asio/asio/detail/impl/resolver_service_base.ipp
@@ -0,0 +1,106 @@
+//
+// detail/impl/resolver_service_base.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_RESOLVER_SERVICE_BASE_IPP
+#define ASIO_DETAIL_IMPL_RESOLVER_SERVICE_BASE_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include "asio/detail/resolver_service_base.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+class resolver_service_base::work_io_service_runner
+{
+public:
+ work_io_service_runner(asio::io_service& io_service)
+ : io_service_(io_service) {}
+ void operator()() { io_service_.run(); }
+private:
+ asio::io_service& io_service_;
+};
+
+resolver_service_base::resolver_service_base(
+ asio::io_service& io_service)
+ : io_service_impl_(asio::use_service<io_service_impl>(io_service)),
+ work_io_service_(new asio::io_service),
+ work_io_service_impl_(asio::use_service<
+ io_service_impl>(*work_io_service_)),
+ work_(new asio::io_service::work(*work_io_service_)),
+ work_thread_(0)
+{
+}
+
+resolver_service_base::~resolver_service_base()
+{
+ shutdown_service();
+}
+
+void resolver_service_base::shutdown_service()
+{
+ work_.reset();
+ if (work_io_service_)
+ {
+ work_io_service_->stop();
+ if (work_thread_)
+ {
+ work_thread_->join();
+ work_thread_.reset();
+ }
+ work_io_service_.reset();
+ }
+}
+
+void resolver_service_base::construct(
+ resolver_service_base::implementation_type& impl)
+{
+ impl.reset(static_cast<void*>(0), socket_ops::noop_deleter());
+}
+
+void resolver_service_base::destroy(
+ resolver_service_base::implementation_type&)
+{
+}
+
+void resolver_service_base::cancel(
+ resolver_service_base::implementation_type& impl)
+{
+ impl.reset(static_cast<void*>(0), socket_ops::noop_deleter());
+}
+
+void resolver_service_base::start_resolve_op(operation* op)
+{
+ start_work_thread();
+ io_service_impl_.work_started();
+ work_io_service_impl_.post_immediate_completion(op);
+}
+
+void resolver_service_base::start_work_thread()
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ if (!work_thread_)
+ {
+ work_thread_.reset(new asio::detail::thread(
+ work_io_service_runner(*work_io_service_)));
+ }
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_IMPL_RESOLVER_SERVICE_BASE_IPP
diff --git a/ext/asio/asio/detail/impl/select_reactor.hpp b/ext/asio/asio/detail/impl/select_reactor.hpp
new file mode 100644
index 0000000..50d3ad4
--- /dev/null
+++ b/ext/asio/asio/detail/impl/select_reactor.hpp
@@ -0,0 +1,84 @@
+//
+// detail/impl/select_reactor.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_SELECT_REACTOR_HPP
+#define ASIO_DETAIL_IMPL_SELECT_REACTOR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_IOCP) \
+ || (!defined(ASIO_HAS_DEV_POLL) \
+ && !defined(ASIO_HAS_EPOLL) \
+ && !defined(ASIO_HAS_KQUEUE))
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Time_Traits>
+void select_reactor::add_timer_queue(timer_queue<Time_Traits>& queue)
+{
+ do_add_timer_queue(queue);
+}
+
+// Remove a timer queue from the reactor.
+template <typename Time_Traits>
+void select_reactor::remove_timer_queue(timer_queue<Time_Traits>& queue)
+{
+ do_remove_timer_queue(queue);
+}
+
+template <typename Time_Traits>
+void select_reactor::schedule_timer(timer_queue<Time_Traits>& queue,
+ const typename Time_Traits::time_type& time,
+ typename timer_queue<Time_Traits>::per_timer_data& timer, timer_op* op)
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ if (shutdown_)
+ {
+ io_service_.post_immediate_completion(op);
+ return;
+ }
+
+ bool earliest = queue.enqueue_timer(time, timer, op);
+ io_service_.work_started();
+ if (earliest)
+ interrupter_.interrupt();
+}
+
+template <typename Time_Traits>
+std::size_t select_reactor::cancel_timer(timer_queue<Time_Traits>& queue,
+ typename timer_queue<Time_Traits>::per_timer_data& timer)
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ op_queue<operation> ops;
+ std::size_t n = queue.cancel_timer(timer, ops);
+ lock.unlock();
+ io_service_.post_deferred_completions(ops);
+ return n;
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_IOCP)
+ // || (!defined(ASIO_HAS_DEV_POLL)
+ // && !defined(ASIO_HAS_EPOLL)
+ // && !defined(ASIO_HAS_KQUEUE))
+
+#endif // ASIO_DETAIL_IMPL_SELECT_REACTOR_HPP
diff --git a/ext/asio/asio/detail/impl/select_reactor.ipp b/ext/asio/asio/detail/impl/select_reactor.ipp
new file mode 100644
index 0000000..1a59be3
--- /dev/null
+++ b/ext/asio/asio/detail/impl/select_reactor.ipp
@@ -0,0 +1,273 @@
+//
+// detail/impl/select_reactor.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP
+#define ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_IOCP) \
+ || (!defined(ASIO_HAS_DEV_POLL) \
+ && !defined(ASIO_HAS_EPOLL) \
+ && !defined(ASIO_HAS_KQUEUE))
+
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/fd_set_adapter.hpp"
+#include "asio/detail/select_reactor.hpp"
+#include "asio/detail/signal_blocker.hpp"
+#include "asio/detail/socket_ops.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+select_reactor::select_reactor(asio::io_service& io_service)
+ : asio::detail::service_base<select_reactor>(io_service),
+ io_service_(use_service<io_service_impl>(io_service)),
+ mutex_(),
+ interrupter_(),
+#if defined(ASIO_HAS_IOCP)
+ stop_thread_(false),
+ thread_(0),
+#endif // defined(ASIO_HAS_IOCP)
+ shutdown_(false)
+{
+#if defined(ASIO_HAS_IOCP)
+ asio::detail::signal_blocker sb;
+ thread_ = new asio::detail::thread(
+ bind_handler(&select_reactor::call_run_thread, this));
+#endif // defined(ASIO_HAS_IOCP)
+}
+
+select_reactor::~select_reactor()
+{
+ shutdown_service();
+}
+
+void select_reactor::shutdown_service()
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ shutdown_ = true;
+#if defined(ASIO_HAS_IOCP)
+ stop_thread_ = true;
+#endif // defined(ASIO_HAS_IOCP)
+ lock.unlock();
+
+#if defined(ASIO_HAS_IOCP)
+ if (thread_)
+ {
+ interrupter_.interrupt();
+ thread_->join();
+ delete thread_;
+ thread_ = 0;
+ }
+#endif // defined(ASIO_HAS_IOCP)
+
+ op_queue<operation> ops;
+
+ for (int i = 0; i < max_ops; ++i)
+ op_queue_[i].get_all_operations(ops);
+
+ timer_queues_.get_all_timers(ops);
+}
+
+void select_reactor::init_task()
+{
+ io_service_.init_task();
+}
+
+int select_reactor::register_descriptor(socket_type,
+ select_reactor::per_descriptor_data&)
+{
+ return 0;
+}
+
+void select_reactor::start_op(int op_type, socket_type descriptor,
+ select_reactor::per_descriptor_data&, reactor_op* op, bool)
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ if (shutdown_)
+ {
+ post_immediate_completion(op);
+ return;
+ }
+
+ bool first = op_queue_[op_type].enqueue_operation(descriptor, op);
+ io_service_.work_started();
+ if (first)
+ interrupter_.interrupt();
+}
+
+void select_reactor::cancel_ops(socket_type descriptor,
+ select_reactor::per_descriptor_data&)
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ cancel_ops_unlocked(descriptor, asio::error::operation_aborted);
+}
+
+void select_reactor::close_descriptor(socket_type descriptor,
+ select_reactor::per_descriptor_data&)
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ cancel_ops_unlocked(descriptor, asio::error::operation_aborted);
+}
+
+void select_reactor::run(bool block, op_queue<operation>& ops)
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+#if defined(ASIO_HAS_IOCP)
+ // Check if the thread is supposed to stop.
+ if (stop_thread_)
+ return;
+#endif // defined(ASIO_HAS_IOCP)
+
+ // Set up the descriptor sets.
+ fd_set_adapter fds[max_select_ops];
+ fds[read_op].set(interrupter_.read_descriptor());
+ socket_type max_fd = 0;
+ bool have_work_to_do = !timer_queues_.all_empty();
+ for (int i = 0; i < max_select_ops; ++i)
+ {
+ have_work_to_do = have_work_to_do || !op_queue_[i].empty();
+ op_queue_[i].get_descriptors(fds[i], ops);
+ if (fds[i].max_descriptor() > max_fd)
+ max_fd = fds[i].max_descriptor();
+ }
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ // Connection operations on Windows use both except and write fd_sets.
+ have_work_to_do = have_work_to_do || !op_queue_[connect_op].empty();
+ op_queue_[connect_op].get_descriptors(fds[write_op], ops);
+ if (fds[write_op].max_descriptor() > max_fd)
+ max_fd = fds[write_op].max_descriptor();
+ op_queue_[connect_op].get_descriptors(fds[except_op], ops);
+ if (fds[except_op].max_descriptor() > max_fd)
+ max_fd = fds[except_op].max_descriptor();
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+ // We can return immediately if there's no work to do and the reactor is
+ // not supposed to block.
+ if (!block && !have_work_to_do)
+ return;
+
+ // Determine how long to block while waiting for events.
+ timeval tv_buf = { 0, 0 };
+ timeval* tv = block ? get_timeout(tv_buf) : &tv_buf;
+
+ lock.unlock();
+
+ // Block on the select call until descriptors become ready.
+ asio::error_code ec;
+ int retval = socket_ops::select(static_cast<int>(max_fd + 1),
+ fds[read_op], fds[write_op], fds[except_op], tv, ec);
+
+ // Reset the interrupter.
+ if (retval > 0 && fds[read_op].is_set(interrupter_.read_descriptor()))
+ interrupter_.reset();
+
+ lock.lock();
+
+ // Dispatch all ready operations.
+ if (retval > 0)
+ {
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ // Connection operations on Windows use both except and write fd_sets.
+ op_queue_[connect_op].perform_operations_for_descriptors(
+ fds[except_op], ops);
+ op_queue_[connect_op].perform_operations_for_descriptors(
+ fds[write_op], ops);
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+ // Exception operations must be processed first to ensure that any
+ // out-of-band data is read before normal data.
+ for (int i = max_select_ops - 1; i >= 0; --i)
+ op_queue_[i].perform_operations_for_descriptors(fds[i], ops);
+ }
+ timer_queues_.get_ready_timers(ops);
+}
+
+void select_reactor::interrupt()
+{
+ interrupter_.interrupt();
+}
+
+#if defined(ASIO_HAS_IOCP)
+void select_reactor::run_thread()
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ while (!stop_thread_)
+ {
+ lock.unlock();
+ op_queue<operation> ops;
+ run(true, ops);
+ io_service_.post_deferred_completions(ops);
+ lock.lock();
+ }
+}
+
+void select_reactor::call_run_thread(select_reactor* reactor)
+{
+ reactor->run_thread();
+}
+#endif // defined(ASIO_HAS_IOCP)
+
+void select_reactor::do_add_timer_queue(timer_queue_base& queue)
+{
+ mutex::scoped_lock lock(mutex_);
+ timer_queues_.insert(&queue);
+}
+
+void select_reactor::do_remove_timer_queue(timer_queue_base& queue)
+{
+ mutex::scoped_lock lock(mutex_);
+ timer_queues_.erase(&queue);
+}
+
+timeval* select_reactor::get_timeout(timeval& tv)
+{
+ // By default we will wait no longer than 5 minutes. This will ensure that
+ // any changes to the system clock are detected after no longer than this.
+ long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000);
+ tv.tv_sec = usec / 1000000;
+ tv.tv_usec = usec % 1000000;
+ return &tv;
+}
+
+void select_reactor::cancel_ops_unlocked(socket_type descriptor,
+ const asio::error_code& ec)
+{
+ bool need_interrupt = false;
+ op_queue<operation> ops;
+ for (int i = 0; i < max_ops; ++i)
+ need_interrupt = op_queue_[i].cancel_operations(
+ descriptor, ops, ec) || need_interrupt;
+ io_service_.post_deferred_completions(ops);
+ if (need_interrupt)
+ interrupter_.interrupt();
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_IOCP)
+ // || (!defined(ASIO_HAS_DEV_POLL)
+ // && !defined(ASIO_HAS_EPOLL)
+ // && !defined(ASIO_HAS_KQUEUE))
+
+#endif // ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP
diff --git a/ext/asio/asio/detail/impl/service_registry.hpp b/ext/asio/asio/detail/impl/service_registry.hpp
new file mode 100644
index 0000000..c2aa9ec
--- /dev/null
+++ b/ext/asio/asio/detail/impl/service_registry.hpp
@@ -0,0 +1,70 @@
+//
+// detail/impl/service_registry.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP
+#define ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Service>
+Service& service_registry::use_service()
+{
+ asio::io_service::service::key key;
+ init_key(key, Service::id);
+ factory_type factory = &service_registry::create<Service>;
+ return *static_cast<Service*>(do_use_service(key, factory));
+}
+
+template <typename Service>
+void service_registry::add_service(Service* new_service)
+{
+ asio::io_service::service::key key;
+ init_key(key, Service::id);
+ return do_add_service(key, new_service);
+}
+
+template <typename Service>
+bool service_registry::has_service() const
+{
+ asio::io_service::service::key key;
+ init_key(key, Service::id);
+ return do_has_service(key);
+}
+
+#if !defined(ASIO_NO_TYPEID)
+template <typename Service>
+void service_registry::init_key(asio::io_service::service::key& key,
+ const asio::detail::service_id<Service>& /*id*/)
+{
+ key.type_info_ = &typeid(typeid_wrapper<Service>);
+ key.id_ = 0;
+}
+#endif // !defined(ASIO_NO_TYPEID)
+
+template <typename Service>
+asio::io_service::service* service_registry::create(
+ asio::io_service& owner)
+{
+ return new Service(owner);
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP
diff --git a/ext/asio/asio/detail/impl/service_registry.ipp b/ext/asio/asio/detail/impl/service_registry.ipp
new file mode 100644
index 0000000..57c57b8
--- /dev/null
+++ b/ext/asio/asio/detail/impl/service_registry.ipp
@@ -0,0 +1,164 @@
+//
+// detail/impl/service_registry.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP
+#define ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <boost/throw_exception.hpp>
+#include "asio/detail/service_registry.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+service_registry::service_registry(asio::io_service& o)
+ : owner_(o),
+ first_service_(0)
+{
+}
+
+service_registry::~service_registry()
+{
+ // Shutdown all services. This must be done in a separate loop before the
+ // services are destroyed since the destructors of user-defined handler
+ // objects may try to access other service objects.
+ asio::io_service::service* service = first_service_;
+ while (service)
+ {
+ service->shutdown_service();
+ service = service->next_;
+ }
+
+ // Destroy all services.
+ while (first_service_)
+ {
+ asio::io_service::service* next_service = first_service_->next_;
+ destroy(first_service_);
+ first_service_ = next_service;
+ }
+}
+
+void service_registry::init_key(asio::io_service::service::key& key,
+ const asio::io_service::id& id)
+{
+ key.type_info_ = 0;
+ key.id_ = &id;
+}
+
+bool service_registry::keys_match(
+ const asio::io_service::service::key& key1,
+ const asio::io_service::service::key& key2)
+{
+ if (key1.id_ && key2.id_)
+ if (key1.id_ == key2.id_)
+ return true;
+ if (key1.type_info_ && key2.type_info_)
+ if (*key1.type_info_ == *key2.type_info_)
+ return true;
+ return false;
+}
+
+void service_registry::destroy(asio::io_service::service* service)
+{
+ delete service;
+}
+
+asio::io_service::service* service_registry::do_use_service(
+ const asio::io_service::service::key& key,
+ factory_type factory)
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ // First see if there is an existing service object with the given key.
+ asio::io_service::service* service = first_service_;
+ while (service)
+ {
+ if (keys_match(service->key_, key))
+ return service;
+ service = service->next_;
+ }
+
+ // Create a new service object. The service registry's mutex is not locked
+ // at this time to allow for nested calls into this function from the new
+ // service's constructor.
+ lock.unlock();
+ auto_service_ptr new_service = { factory(owner_) };
+ new_service.ptr_->key_ = key;
+ lock.lock();
+
+ // Check that nobody else created another service object of the same type
+ // while the lock was released.
+ service = first_service_;
+ while (service)
+ {
+ if (keys_match(service->key_, key))
+ return service;
+ service = service->next_;
+ }
+
+ // Service was successfully initialised, pass ownership to registry.
+ new_service.ptr_->next_ = first_service_;
+ first_service_ = new_service.ptr_;
+ new_service.ptr_ = 0;
+ return first_service_;
+}
+
+void service_registry::do_add_service(
+ const asio::io_service::service::key& key,
+ asio::io_service::service* new_service)
+{
+ if (&owner_ != &new_service->io_service())
+ boost::throw_exception(invalid_service_owner());
+
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ // Check if there is an existing service object with the given key.
+ asio::io_service::service* service = first_service_;
+ while (service)
+ {
+ if (keys_match(service->key_, key))
+ boost::throw_exception(service_already_exists());
+ service = service->next_;
+ }
+
+ // Take ownership of the service object.
+ new_service->key_ = key;
+ new_service->next_ = first_service_;
+ first_service_ = new_service;
+}
+
+bool service_registry::do_has_service(
+ const asio::io_service::service::key& key) const
+{
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ asio::io_service::service* service = first_service_;
+ while (service)
+ {
+ if (keys_match(service->key_, key))
+ return true;
+ service = service->next_;
+ }
+
+ return false;
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP
diff --git a/ext/asio/asio/detail/impl/socket_ops.ipp b/ext/asio/asio/detail/impl/socket_ops.ipp
new file mode 100644
index 0000000..66006ea
--- /dev/null
+++ b/ext/asio/asio/detail/impl/socket_ops.ipp
@@ -0,0 +1,2917 @@
+//
+// detail/impl/socket_ops.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_SOCKET_OPS_IPP
+#define ASIO_DETAIL_SOCKET_OPS_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <boost/assert.hpp>
+#include <boost/detail/workaround.hpp>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cerrno>
+#include <new>
+#include "asio/detail/socket_ops.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+namespace socket_ops {
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+struct msghdr { int msg_namelen; };
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+#if defined(__hpux)
+// HP-UX doesn't declare these functions extern "C", so they are declared again
+// here to avoid linker errors about undefined symbols.
+extern "C" char* if_indextoname(unsigned int, char*);
+extern "C" unsigned int if_nametoindex(const char*);
+#endif // defined(__hpux)
+
+inline void clear_last_error()
+{
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ WSASetLastError(0);
+#else
+ errno = 0;
+#endif
+}
+
+template <typename ReturnType>
+inline ReturnType error_wrapper(ReturnType return_value,
+ asio::error_code& ec)
+{
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ ec = asio::error_code(WSAGetLastError(),
+ asio::error::get_system_category());
+#else
+ ec = asio::error_code(errno,
+ asio::error::get_system_category());
+#endif
+ return return_value;
+}
+
+template <typename SockLenType>
+inline socket_type call_accept(SockLenType msghdr::*,
+ socket_type s, socket_addr_type* addr, std::size_t* addrlen)
+{
+ SockLenType tmp_addrlen = addrlen ? (SockLenType)*addrlen : 0;
+ socket_type result = ::accept(s, addr, addrlen ? &tmp_addrlen : 0);
+ if (addrlen)
+ *addrlen = (std::size_t)tmp_addrlen;
+ return result;
+}
+
+ASIO_DECL
+socket_type accept(socket_type s, socket_addr_type* addr,
+ std::size_t* addrlen, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return invalid_socket;
+ }
+
+ clear_last_error();
+
+ socket_type new_s = error_wrapper(call_accept(
+ &msghdr::msg_namelen, s, addr, addrlen), ec);
+ if (new_s == invalid_socket)
+ return new_s;
+
+#if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__)
+ int optval = 1;
+ int result = error_wrapper(::setsockopt(new_s,
+ SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)), ec);
+ if (result != 0)
+ {
+ ::close(new_s);
+ return invalid_socket;
+ }
+#endif
+
+ ec = asio::error_code();
+ return new_s;
+}
+
+ASIO_DECL
+socket_type sync_accept(socket_type s, state_type state,
+ socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec)
+{
+ // Accept a socket.
+ for (;;)
+ {
+ // Try to complete the operation without blocking.
+ socket_type new_socket = socket_ops::accept(s, addr, addrlen, ec);
+
+ // Check if operation succeeded.
+ if (new_socket != invalid_socket)
+ return new_socket;
+
+ // Operation failed.
+ if (ec == asio::error::would_block
+ || ec == asio::error::try_again)
+ {
+ if (state & user_set_non_blocking)
+ return invalid_socket;
+ // Fall through to retry operation.
+ }
+ else if (ec == asio::error::connection_aborted)
+ {
+ if (state & enable_connection_aborted)
+ return invalid_socket;
+ // Fall through to retry operation.
+ }
+#if defined(EPROTO)
+ else if (ec.value() == EPROTO)
+ {
+ if (state & enable_connection_aborted)
+ return invalid_socket;
+ // Fall through to retry operation.
+ }
+#endif // defined(EPROTO)
+ else
+ return invalid_socket;
+
+ // Wait for socket to become ready.
+ if (socket_ops::poll_read(s, ec) < 0)
+ return invalid_socket;
+ }
+}
+
+#if defined(ASIO_HAS_IOCP)
+
+void complete_iocp_accept(socket_type s,
+ void* output_buffer, DWORD address_length,
+ socket_addr_type* addr, std::size_t* addrlen,
+ socket_type new_socket, asio::error_code& ec)
+{
+ // Map non-portable errors to their portable counterparts.
+ if (ec.value() == ERROR_NETNAME_DELETED)
+ ec = asio::error::connection_aborted;
+
+ if (!ec)
+ {
+ // Get the address of the peer.
+ if (addr && addrlen)
+ {
+ LPSOCKADDR local_addr = 0;
+ int local_addr_length = 0;
+ LPSOCKADDR remote_addr = 0;
+ int remote_addr_length = 0;
+ GetAcceptExSockaddrs(output_buffer, 0, address_length,
+ address_length, &local_addr, &local_addr_length,
+ &remote_addr, &remote_addr_length);
+ if (static_cast<std::size_t>(remote_addr_length) > *addrlen)
+ {
+ ec = asio::error::invalid_argument;
+ }
+ else
+ {
+ using namespace std; // For memcpy.
+ memcpy(addr, remote_addr, remote_addr_length);
+ *addrlen = static_cast<std::size_t>(remote_addr_length);
+ }
+ }
+
+ // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname
+ // and getpeername will work on the accepted socket.
+ SOCKET update_ctx_param = s;
+ socket_ops::state_type state = 0;
+ socket_ops::setsockopt(new_socket, state,
+ SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
+ &update_ctx_param, sizeof(SOCKET), ec);
+ }
+}
+
+#else // defined(ASIO_HAS_IOCP)
+
+ASIO_DECL
+bool non_blocking_accept(socket_type s,
+ state_type state, socket_addr_type* addr, std::size_t* addrlen,
+ asio::error_code& ec, socket_type& new_socket)
+{
+ for (;;)
+ {
+ // Accept the waiting connection.
+ new_socket = socket_ops::accept(s, addr, addrlen, ec);
+
+ // Check if operation succeeded.
+ if (new_socket != invalid_socket)
+ return true;
+
+ // Retry operation if interrupted by signal.
+ if (ec == asio::error::interrupted)
+ continue;
+
+ // Operation failed.
+ if (ec == asio::error::would_block
+ || ec == asio::error::try_again)
+ {
+ if (state & user_set_non_blocking)
+ return true;
+ // Fall through to retry operation.
+ }
+ else if (ec == asio::error::connection_aborted)
+ {
+ if (state & enable_connection_aborted)
+ return true;
+ // Fall through to retry operation.
+ }
+#if defined(EPROTO)
+ else if (ec.value() == EPROTO)
+ {
+ if (state & enable_connection_aborted)
+ return true;
+ // Fall through to retry operation.
+ }
+#endif // defined(EPROTO)
+ else
+ return true;
+
+ return false;
+ }
+}
+
+#endif // defined(ASIO_HAS_IOCP)
+
+template <typename SockLenType>
+inline int call_bind(SockLenType msghdr::*,
+ socket_type s, const socket_addr_type* addr, std::size_t addrlen)
+{
+ return ::bind(s, addr, (SockLenType)addrlen);
+}
+
+ASIO_DECL
+int bind(socket_type s, const socket_addr_type* addr,
+ std::size_t addrlen, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return socket_error_retval;
+ }
+
+ clear_last_error();
+ int result = error_wrapper(call_bind(
+ &msghdr::msg_namelen, s, addr, addrlen), ec);
+ if (result == 0)
+ ec = asio::error_code();
+ return result;
+}
+
+ASIO_DECL
+int close(socket_type s, state_type& state,
+ bool destruction, asio::error_code& ec)
+{
+ int result = 0;
+ if (s != invalid_socket)
+ {
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ if ((state & non_blocking) && (state & user_set_linger))
+ {
+ ioctl_arg_type arg = 0;
+ ::ioctlsocket(s, FIONBIO, &arg);
+ state &= ~non_blocking;
+ }
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ if (state & non_blocking)
+ {
+#if defined(__SYMBIAN32__)
+ int flags = ::fcntl(s, F_GETFL, 0);
+ if (flags >= 0)
+ ::fcntl(s, F_SETFL, flags & ~O_NONBLOCK);
+#else // defined(__SYMBIAN32__)
+ ioctl_arg_type arg = 0;
+ ::ioctl(s, FIONBIO, &arg);
+#endif // defined(__SYMBIAN32__)
+ state &= ~non_blocking;
+ }
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+ if (destruction && (state & user_set_linger))
+ {
+ ::linger opt;
+ opt.l_onoff = 0;
+ opt.l_linger = 0;
+ asio::error_code ignored_ec;
+ socket_ops::setsockopt(s, state, SOL_SOCKET,
+ SO_LINGER, &opt, sizeof(opt), ignored_ec);
+ }
+
+ clear_last_error();
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ result = error_wrapper(::closesocket(s), ec);
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ result = error_wrapper(::close(s), ec);
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ }
+
+ if (result == 0)
+ ec = asio::error_code();
+ return result;
+}
+
+ASIO_DECL
+bool set_internal_non_blocking(socket_type s,
+ state_type& state, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return false;
+ }
+
+ clear_last_error();
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ ioctl_arg_type arg = 1;
+ int result = error_wrapper(::ioctlsocket(s, FIONBIO, &arg), ec);
+#elif defined(__SYMBIAN32__)
+ int result = error_wrapper(::fcntl(s, F_GETFL, 0), ec);
+ if (result >= 0)
+ {
+ clear_last_error();
+ result = error_wrapper(::fcntl(s, F_SETFL, result | O_NONBLOCK), ec);
+ }
+#else
+ ioctl_arg_type arg = 1;
+ int result = error_wrapper(::ioctl(s, FIONBIO, &arg), ec);
+#endif
+
+ if (result >= 0)
+ {
+ ec = asio::error_code();
+ state |= internal_non_blocking;
+ return true;
+ }
+
+ return false;
+}
+
+ASIO_DECL
+int shutdown(socket_type s, int what, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return socket_error_retval;
+ }
+
+ clear_last_error();
+ int result = error_wrapper(::shutdown(s, what), ec);
+ if (result == 0)
+ ec = asio::error_code();
+ return result;
+}
+
+template <typename SockLenType>
+inline int call_connect(SockLenType msghdr::*,
+ socket_type s, const socket_addr_type* addr, std::size_t addrlen)
+{
+ return ::connect(s, addr, (SockLenType)addrlen);
+}
+
+ASIO_DECL
+int connect(socket_type s, const socket_addr_type* addr,
+ std::size_t addrlen, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return socket_error_retval;
+ }
+
+ clear_last_error();
+ int result = error_wrapper(call_connect(
+ &msghdr::msg_namelen, s, addr, addrlen), ec);
+ if (result == 0)
+ ec = asio::error_code();
+ return result;
+}
+
+ASIO_DECL
+void sync_connect(socket_type s, const socket_addr_type* addr,
+ std::size_t addrlen, asio::error_code& ec)
+{
+ // Perform the connect operation.
+ socket_ops::connect(s, addr, addrlen, ec);
+ if (ec != asio::error::in_progress
+ && ec != asio::error::would_block)
+ {
+ // The connect operation finished immediately.
+ return;
+ }
+
+ // Wait for socket to become ready.
+ if (socket_ops::poll_connect(s, ec) < 0)
+ return;
+
+ // Get the error code from the connect operation.
+ int connect_error = 0;
+ size_t connect_error_len = sizeof(connect_error);
+ if (socket_ops::getsockopt(s, 0, SOL_SOCKET, SO_ERROR,
+ &connect_error, &connect_error_len, ec) == socket_error_retval)
+ return;
+
+ // Return the result of the connect operation.
+ ec = asio::error_code(connect_error,
+ asio::error::get_system_category());
+}
+
+ASIO_DECL
+bool non_blocking_connect(socket_type s, asio::error_code& ec)
+{
+ // Get the error code from the connect operation.
+ int connect_error = 0;
+ size_t connect_error_len = sizeof(connect_error);
+ if (socket_ops::getsockopt(s, 0, SOL_SOCKET, SO_ERROR,
+ &connect_error, &connect_error_len, ec) == 0)
+ {
+ if (connect_error)
+ {
+ ec = asio::error_code(connect_error,
+ asio::error::get_system_category());
+ }
+ else
+ ec = asio::error_code();
+ }
+
+ return true;
+}
+
+ASIO_DECL
+int socketpair(int af, int type, int protocol,
+ socket_type sv[2], asio::error_code& ec)
+{
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ (void)(af);
+ (void)(type);
+ (void)(protocol);
+ (void)(sv);
+ ec = asio::error::operation_not_supported;
+ return socket_error_retval;
+#else
+ clear_last_error();
+ int result = error_wrapper(::socketpair(af, type, protocol, sv), ec);
+ if (result == 0)
+ ec = asio::error_code();
+ return result;
+#endif
+}
+
+ASIO_DECL
+bool sockatmark(socket_type s, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return false;
+ }
+
+#if defined(SIOCATMARK)
+ ioctl_arg_type value = 0;
+# if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ int result = error_wrapper(::ioctlsocket(s, SIOCATMARK, &value), ec);
+# else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ int result = error_wrapper(::ioctl(s, SIOCATMARK, &value), ec);
+# endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ if (result == 0)
+ ec = asio::error_code();
+# if defined(ENOTTY)
+ if (ec.value() == ENOTTY)
+ ec = asio::error::not_socket;
+# endif // defined(ENOTTY)
+#else // defined(SIOCATMARK)
+ int value = error_wrapper(::sockatmark(s), ec);
+ if (value != -1)
+ ec = asio::error_code();
+#endif // defined(SIOCATMARK)
+
+ return ec ? false : value != 0;
+}
+
+ASIO_DECL
+size_t available(socket_type s, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return 0;
+ }
+
+ ioctl_arg_type value = 0;
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ int result = error_wrapper(::ioctlsocket(s, FIONREAD, &value), ec);
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ int result = error_wrapper(::ioctl(s, FIONREAD, &value), ec);
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ if (result == 0)
+ ec = asio::error_code();
+#if defined(ENOTTY)
+ if (ec.value() == ENOTTY)
+ ec = asio::error::not_socket;
+#endif // defined(ENOTTY)
+
+ return ec ? static_cast<size_t>(0) : static_cast<size_t>(value);
+}
+
+ASIO_DECL
+int listen(socket_type s, int backlog, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return socket_error_retval;
+ }
+
+ clear_last_error();
+ int result = error_wrapper(::listen(s, backlog), ec);
+ if (result == 0)
+ ec = asio::error_code();
+ return result;
+}
+
+inline void init_buf_iov_base(void*& base, void* addr)
+{
+ base = addr;
+}
+
+template <typename T>
+inline void init_buf_iov_base(T& base, void* addr)
+{
+ base = static_cast<T>(addr);
+}
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+typedef WSABUF buf;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+typedef iovec buf;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+ASIO_DECL
+void init_buf(buf& b, void* data, size_t size)
+{
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ b.buf = static_cast<char*>(data);
+ b.len = static_cast<u_long>(size);
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ init_buf_iov_base(b.iov_base, data);
+ b.iov_len = size;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+ASIO_DECL
+void init_buf(buf& b, const void* data, size_t size)
+{
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ b.buf = static_cast<char*>(const_cast<void*>(data));
+ b.len = static_cast<u_long>(size);
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ init_buf_iov_base(b.iov_base, const_cast<void*>(data));
+ b.iov_len = size;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+inline void init_msghdr_msg_name(void*& name, socket_addr_type* addr)
+{
+ name = addr;
+}
+
+inline void init_msghdr_msg_name(void*& name, const socket_addr_type* addr)
+{
+ name = const_cast<socket_addr_type*>(addr);
+}
+
+template <typename T>
+inline void init_msghdr_msg_name(T& name, socket_addr_type* addr)
+{
+ name = reinterpret_cast<T>(addr);
+}
+
+template <typename T>
+inline void init_msghdr_msg_name(T& name, const socket_addr_type* addr)
+{
+ name = reinterpret_cast<T>(const_cast<socket_addr_type*>(addr));
+}
+
+ASIO_DECL
+int recv(socket_type s, buf* bufs, size_t count, int flags,
+ asio::error_code& ec)
+{
+ clear_last_error();
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ // Receive some data.
+ DWORD recv_buf_count = static_cast<DWORD>(count);
+ DWORD bytes_transferred = 0;
+ DWORD recv_flags = flags;
+ int result = error_wrapper(::WSARecv(s, bufs,
+ recv_buf_count, &bytes_transferred, &recv_flags, 0, 0), ec);
+ if (ec.value() == ERROR_NETNAME_DELETED)
+ ec = asio::error::connection_reset;
+ else if (ec.value() == ERROR_PORT_UNREACHABLE)
+ ec = asio::error::connection_refused;
+ if (result != 0)
+ return socket_error_retval;
+ ec = asio::error_code();
+ return bytes_transferred;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ msghdr msg = msghdr();
+ msg.msg_iov = bufs;
+ msg.msg_iovlen = count;
+ int result = error_wrapper(::recvmsg(s, &msg, flags), ec);
+ if (result >= 0)
+ ec = asio::error_code();
+ return result;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+ASIO_DECL
+size_t sync_recv(socket_type s, state_type state, buf* bufs,
+ size_t count, int flags, bool all_empty, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return 0;
+ }
+
+ // A request to read 0 bytes on a stream is a no-op.
+ if (all_empty && (state & stream_oriented))
+ {
+ ec = asio::error_code();
+ return 0;
+ }
+
+ // Read some data.
+ for (;;)
+ {
+ // Try to complete the operation without blocking.
+ int bytes = socket_ops::recv(s, bufs, count, flags, ec);
+
+ // Check if operation succeeded.
+ if (bytes > 0)
+ return bytes;
+
+ // Check for EOF.
+ if ((state & stream_oriented) && bytes == 0)
+ {
+ ec = asio::error::eof;
+ return 0;
+ }
+
+ // Operation failed.
+ if ((state & user_set_non_blocking)
+ || (ec != asio::error::would_block
+ && ec != asio::error::try_again))
+ return 0;
+
+ // Wait for socket to become ready.
+ if (socket_ops::poll_read(s, ec) < 0)
+ return 0;
+ }
+}
+
+#if defined(ASIO_HAS_IOCP)
+
+ASIO_DECL
+void complete_iocp_recv(state_type state,
+ const weak_cancel_token_type& cancel_token, bool all_empty,
+ asio::error_code& ec, size_t bytes_transferred)
+{
+ // Map non-portable errors to their portable counterparts.
+ if (ec.value() == ERROR_NETNAME_DELETED)
+ {
+ if (cancel_token.expired())
+ ec = asio::error::operation_aborted;
+ else
+ ec = asio::error::connection_reset;
+ }
+ else if (ec.value() == ERROR_PORT_UNREACHABLE)
+ {
+ ec = asio::error::connection_refused;
+ }
+
+ // Check for connection closed.
+ else if (!ec && bytes_transferred == 0
+ && (state & stream_oriented) != 0
+ && !all_empty)
+ {
+ ec = asio::error::eof;
+ }
+}
+
+#else // defined(ASIO_HAS_IOCP)
+
+ASIO_DECL
+bool non_blocking_recv(socket_type s,
+ buf* bufs, size_t count, int flags, bool is_stream,
+ asio::error_code& ec, size_t& bytes_transferred)
+{
+ for (;;)
+ {
+ // Read some data.
+ int bytes = socket_ops::recv(s, bufs, count, flags, ec);
+
+ // Check for end of stream.
+ if (is_stream && bytes == 0)
+ {
+ ec = asio::error::eof;
+ return true;
+ }
+
+ // Retry operation if interrupted by signal.
+ if (ec == asio::error::interrupted)
+ continue;
+
+ // Check if we need to run the operation again.
+ if (ec == asio::error::would_block
+ || ec == asio::error::try_again)
+ return false;
+
+ // Operation is complete.
+ if (bytes >= 0)
+ {
+ ec = asio::error_code();
+ bytes_transferred = bytes;
+ }
+ else
+ bytes_transferred = 0;
+
+ return true;
+ }
+}
+
+#endif // defined(ASIO_HAS_IOCP)
+
+ASIO_DECL
+int recvfrom(socket_type s, buf* bufs, size_t count, int flags,
+ socket_addr_type* addr, std::size_t* addrlen,
+ asio::error_code& ec)
+{
+ clear_last_error();
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ // Receive some data.
+ DWORD recv_buf_count = static_cast<DWORD>(count);
+ DWORD bytes_transferred = 0;
+ DWORD recv_flags = flags;
+ int tmp_addrlen = (int)*addrlen;
+ int result = error_wrapper(::WSARecvFrom(s, bufs, recv_buf_count,
+ &bytes_transferred, &recv_flags, addr, &tmp_addrlen, 0, 0), ec);
+ *addrlen = (std::size_t)tmp_addrlen;
+ if (ec.value() == ERROR_NETNAME_DELETED)
+ ec = asio::error::connection_reset;
+ else if (ec.value() == ERROR_PORT_UNREACHABLE)
+ ec = asio::error::connection_refused;
+ if (result != 0)
+ return socket_error_retval;
+ ec = asio::error_code();
+ return bytes_transferred;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ msghdr msg = msghdr();
+ init_msghdr_msg_name(msg.msg_name, addr);
+ msg.msg_namelen = *addrlen;
+ msg.msg_iov = bufs;
+ msg.msg_iovlen = count;
+ int result = error_wrapper(::recvmsg(s, &msg, flags), ec);
+ *addrlen = msg.msg_namelen;
+ if (result >= 0)
+ ec = asio::error_code();
+ return result;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+ASIO_DECL
+size_t sync_recvfrom(socket_type s, state_type state, buf* bufs,
+ size_t count, int flags, socket_addr_type* addr,
+ std::size_t* addrlen, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return 0;
+ }
+
+ // Read some data.
+ for (;;)
+ {
+ // Try to complete the operation without blocking.
+ int bytes = socket_ops::recvfrom(s, bufs, count, flags, addr, addrlen, ec);
+
+ // Check if operation succeeded.
+ if (bytes >= 0)
+ return bytes;
+
+ // Operation failed.
+ if ((state & user_set_non_blocking)
+ || (ec != asio::error::would_block
+ && ec != asio::error::try_again))
+ return 0;
+
+ // Wait for socket to become ready.
+ if (socket_ops::poll_read(s, ec) < 0)
+ return 0;
+ }
+}
+
+#if defined(ASIO_HAS_IOCP)
+
+ASIO_DECL
+void complete_iocp_recvfrom(
+ const weak_cancel_token_type& cancel_token,
+ asio::error_code& ec)
+{
+ // Map non-portable errors to their portable counterparts.
+ if (ec.value() == ERROR_NETNAME_DELETED)
+ {
+ if (cancel_token.expired())
+ ec = asio::error::operation_aborted;
+ else
+ ec = asio::error::connection_reset;
+ }
+ else if (ec.value() == ERROR_PORT_UNREACHABLE)
+ {
+ ec = asio::error::connection_refused;
+ }
+}
+
+#else // defined(ASIO_HAS_IOCP)
+
+ASIO_DECL
+bool non_blocking_recvfrom(socket_type s,
+ buf* bufs, size_t count, int flags,
+ socket_addr_type* addr, std::size_t* addrlen,
+ asio::error_code& ec, size_t& bytes_transferred)
+{
+ for (;;)
+ {
+ // Read some data.
+ int bytes = socket_ops::recvfrom(s, bufs, count, flags, addr, addrlen, ec);
+
+ // Retry operation if interrupted by signal.
+ if (ec == asio::error::interrupted)
+ continue;
+
+ // Check if we need to run the operation again.
+ if (ec == asio::error::would_block
+ || ec == asio::error::try_again)
+ return false;
+
+ // Operation is complete.
+ if (bytes >= 0)
+ {
+ ec = asio::error_code();
+ bytes_transferred = bytes;
+ }
+ else
+ bytes_transferred = 0;
+
+ return true;
+ }
+}
+
+#endif // defined(ASIO_HAS_IOCP)
+
+ASIO_DECL
+int send(socket_type s, const buf* bufs, size_t count, int flags,
+ asio::error_code& ec)
+{
+ clear_last_error();
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ // Send the data.
+ DWORD send_buf_count = static_cast<DWORD>(count);
+ DWORD bytes_transferred = 0;
+ DWORD send_flags = flags;
+ int result = error_wrapper(::WSASend(s, const_cast<buf*>(bufs),
+ send_buf_count, &bytes_transferred, send_flags, 0, 0), ec);
+ if (ec.value() == ERROR_NETNAME_DELETED)
+ ec = asio::error::connection_reset;
+ else if (ec.value() == ERROR_PORT_UNREACHABLE)
+ ec = asio::error::connection_refused;
+ if (result != 0)
+ return socket_error_retval;
+ ec = asio::error_code();
+ return bytes_transferred;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ msghdr msg = msghdr();
+ msg.msg_iov = const_cast<buf*>(bufs);
+ msg.msg_iovlen = count;
+#if defined(__linux__)
+ flags |= MSG_NOSIGNAL;
+#endif // defined(__linux__)
+ int result = error_wrapper(::sendmsg(s, &msg, flags), ec);
+ if (result >= 0)
+ ec = asio::error_code();
+ return result;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+ASIO_DECL
+size_t sync_send(socket_type s, state_type state, const buf* bufs,
+ size_t count, int flags, bool all_empty, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return 0;
+ }
+
+ // A request to write 0 bytes to a stream is a no-op.
+ if (all_empty && (state & stream_oriented))
+ {
+ ec = asio::error_code();
+ return 0;
+ }
+
+ // Read some data.
+ for (;;)
+ {
+ // Try to complete the operation without blocking.
+ int bytes = socket_ops::send(s, bufs, count, flags, ec);
+
+ // Check if operation succeeded.
+ if (bytes >= 0)
+ return bytes;
+
+ // Operation failed.
+ if ((state & user_set_non_blocking)
+ || (ec != asio::error::would_block
+ && ec != asio::error::try_again))
+ return 0;
+
+ // Wait for socket to become ready.
+ if (socket_ops::poll_write(s, ec) < 0)
+ return 0;
+ }
+}
+
+#if defined(ASIO_HAS_IOCP)
+
+ASIO_DECL
+void complete_iocp_send(
+ const weak_cancel_token_type& cancel_token,
+ asio::error_code& ec)
+{
+ // Map non-portable errors to their portable counterparts.
+ if (ec.value() == ERROR_NETNAME_DELETED)
+ {
+ if (cancel_token.expired())
+ ec = asio::error::operation_aborted;
+ else
+ ec = asio::error::connection_reset;
+ }
+ else if (ec.value() == ERROR_PORT_UNREACHABLE)
+ {
+ ec = asio::error::connection_refused;
+ }
+}
+
+#else // defined(ASIO_HAS_IOCP)
+
+ASIO_DECL
+bool non_blocking_send(socket_type s,
+ const buf* bufs, size_t count, int flags,
+ asio::error_code& ec, size_t& bytes_transferred)
+{
+ for (;;)
+ {
+ // Write some data.
+ int bytes = socket_ops::send(s, bufs, count, flags, ec);
+
+ // Retry operation if interrupted by signal.
+ if (ec == asio::error::interrupted)
+ continue;
+
+ // Check if we need to run the operation again.
+ if (ec == asio::error::would_block
+ || ec == asio::error::try_again)
+ return false;
+
+ // Operation is complete.
+ if (bytes >= 0)
+ {
+ ec = asio::error_code();
+ bytes_transferred = bytes;
+ }
+ else
+ bytes_transferred = 0;
+
+ return true;
+ }
+}
+
+#endif // defined(ASIO_HAS_IOCP)
+
+ASIO_DECL
+int sendto(socket_type s, const buf* bufs, size_t count, int flags,
+ const socket_addr_type* addr, std::size_t addrlen,
+ asio::error_code& ec)
+{
+ clear_last_error();
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ // Send the data.
+ DWORD send_buf_count = static_cast<DWORD>(count);
+ DWORD bytes_transferred = 0;
+ int result = error_wrapper(::WSASendTo(s, const_cast<buf*>(bufs),
+ send_buf_count, &bytes_transferred, flags, addr,
+ static_cast<int>(addrlen), 0, 0), ec);
+ if (ec.value() == ERROR_NETNAME_DELETED)
+ ec = asio::error::connection_reset;
+ else if (ec.value() == ERROR_PORT_UNREACHABLE)
+ ec = asio::error::connection_refused;
+ if (result != 0)
+ return socket_error_retval;
+ ec = asio::error_code();
+ return bytes_transferred;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ msghdr msg = msghdr();
+ init_msghdr_msg_name(msg.msg_name, addr);
+ msg.msg_namelen = addrlen;
+ msg.msg_iov = const_cast<buf*>(bufs);
+ msg.msg_iovlen = count;
+#if defined(__linux__)
+ flags |= MSG_NOSIGNAL;
+#endif // defined(__linux__)
+ int result = error_wrapper(::sendmsg(s, &msg, flags), ec);
+ if (result >= 0)
+ ec = asio::error_code();
+ return result;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+ASIO_DECL
+size_t sync_sendto(socket_type s, state_type state, const buf* bufs,
+ size_t count, int flags, const socket_addr_type* addr,
+ std::size_t addrlen, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return 0;
+ }
+
+ // Write some data.
+ for (;;)
+ {
+ // Try to complete the operation without blocking.
+ int bytes = socket_ops::sendto(s, bufs, count, flags, addr, addrlen, ec);
+
+ // Check if operation succeeded.
+ if (bytes >= 0)
+ return bytes;
+
+ // Operation failed.
+ if ((state & user_set_non_blocking)
+ || (ec != asio::error::would_block
+ && ec != asio::error::try_again))
+ return 0;
+
+ // Wait for socket to become ready.
+ if (socket_ops::poll_write(s, ec) < 0)
+ return 0;
+ }
+}
+
+#if !defined(ASIO_HAS_IOCP)
+
+ASIO_DECL
+bool non_blocking_sendto(socket_type s,
+ const buf* bufs, size_t count, int flags,
+ const socket_addr_type* addr, std::size_t addrlen,
+ asio::error_code& ec, size_t& bytes_transferred)
+{
+ for (;;)
+ {
+ // Write some data.
+ int bytes = socket_ops::sendto(s, bufs, count, flags, addr, addrlen, ec);
+
+ // Retry operation if interrupted by signal.
+ if (ec == asio::error::interrupted)
+ continue;
+
+ // Check if we need to run the operation again.
+ if (ec == asio::error::would_block
+ || ec == asio::error::try_again)
+ return false;
+
+ // Operation is complete.
+ if (bytes >= 0)
+ {
+ ec = asio::error_code();
+ bytes_transferred = bytes;
+ }
+ else
+ bytes_transferred = 0;
+
+ return true;
+ }
+}
+
+#endif // !defined(ASIO_HAS_IOCP)
+
+ASIO_DECL
+socket_type socket(int af, int type, int protocol,
+ asio::error_code& ec)
+{
+ clear_last_error();
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ socket_type s = error_wrapper(::WSASocket(af, type, protocol, 0, 0,
+ WSA_FLAG_OVERLAPPED), ec);
+ if (s == invalid_socket)
+ return s;
+
+ if (af == AF_INET6)
+ {
+ // Try to enable the POSIX default behaviour of having IPV6_V6ONLY set to
+ // false. This will only succeed on Windows Vista and later versions of
+ // Windows, where a dual-stack IPv4/v6 implementation is available.
+ DWORD optval = 0;
+ ::setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
+ reinterpret_cast<const char*>(&optval), sizeof(optval));
+ }
+
+ ec = asio::error_code();
+
+ return s;
+#elif defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__)
+ socket_type s = error_wrapper(::socket(af, type, protocol), ec);
+ if (s == invalid_socket)
+ return s;
+
+ int optval = 1;
+ int result = error_wrapper(::setsockopt(s,
+ SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)), ec);
+ if (result != 0)
+ {
+ ::close(s);
+ return invalid_socket;
+ }
+
+ return s;
+#else
+ int s = error_wrapper(::socket(af, type, protocol), ec);
+ if (s >= 0)
+ ec = asio::error_code();
+ return s;
+#endif
+}
+
+template <typename SockLenType>
+inline int call_setsockopt(SockLenType msghdr::*,
+ socket_type s, int level, int optname,
+ const void* optval, std::size_t optlen)
+{
+ return ::setsockopt(s, level, optname,
+ (const char*)optval, (SockLenType)optlen);
+}
+
+ASIO_DECL
+int setsockopt(socket_type s, state_type& state, int level, int optname,
+ const void* optval, std::size_t optlen, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return socket_error_retval;
+ }
+
+ if (level == custom_socket_option_level && optname == always_fail_option)
+ {
+ ec = asio::error::invalid_argument;
+ return socket_error_retval;
+ }
+
+ if (level == custom_socket_option_level
+ && optname == enable_connection_aborted_option)
+ {
+ if (optlen != sizeof(int))
+ {
+ ec = asio::error::invalid_argument;
+ return socket_error_retval;
+ }
+
+ if (*static_cast<const int*>(optval))
+ state |= enable_connection_aborted;
+ else
+ state &= ~enable_connection_aborted;
+ ec = asio::error_code();
+ return 0;
+ }
+
+ if (level == SOL_SOCKET && optname == SO_LINGER)
+ state |= user_set_linger;
+
+#if defined(__BORLANDC__)
+ // Mysteriously, using the getsockopt and setsockopt functions directly with
+ // Borland C++ results in incorrect values being set and read. The bug can be
+ // worked around by using function addresses resolved with GetProcAddress.
+ if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32"))
+ {
+ typedef int (WSAAPI *sso_t)(SOCKET, int, int, const char*, int);
+ if (sso_t sso = (sso_t)::GetProcAddress(winsock_module, "setsockopt"))
+ {
+ clear_last_error();
+ return error_wrapper(sso(s, level, optname,
+ reinterpret_cast<const char*>(optval),
+ static_cast<int>(optlen)), ec);
+ }
+ }
+ ec = asio::error::fault;
+ return socket_error_retval;
+#else // defined(__BORLANDC__)
+ clear_last_error();
+ int result = error_wrapper(call_setsockopt(&msghdr::msg_namelen,
+ s, level, optname, optval, optlen), ec);
+ if (result == 0)
+ {
+ ec = asio::error_code();
+
+#if defined(__MACH__) && defined(__APPLE__) \
+ || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ // To implement portable behaviour for SO_REUSEADDR with UDP sockets we
+ // need to also set SO_REUSEPORT on BSD-based platforms.
+ if ((state & datagram_oriented)
+ && level == SOL_SOCKET && optname == SO_REUSEADDR)
+ {
+ call_setsockopt(&msghdr::msg_namelen, s,
+ SOL_SOCKET, SO_REUSEPORT, optval, optlen);
+ }
+#endif
+ }
+
+ return result;
+#endif // defined(__BORLANDC__)
+}
+
+template <typename SockLenType>
+inline int call_getsockopt(SockLenType msghdr::*,
+ socket_type s, int level, int optname,
+ void* optval, std::size_t* optlen)
+{
+ SockLenType tmp_optlen = (SockLenType)*optlen;
+ int result = ::getsockopt(s, level, optname, (char*)optval, &tmp_optlen);
+ *optlen = (std::size_t)tmp_optlen;
+ return result;
+}
+
+ASIO_DECL
+int getsockopt(socket_type s, state_type state, int level, int optname,
+ void* optval, size_t* optlen, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return socket_error_retval;
+ }
+
+ if (level == custom_socket_option_level && optname == always_fail_option)
+ {
+ ec = asio::error::invalid_argument;
+ return socket_error_retval;
+ }
+
+ if (level == custom_socket_option_level
+ && optname == enable_connection_aborted_option)
+ {
+ if (*optlen != sizeof(int))
+ {
+ ec = asio::error::invalid_argument;
+ return socket_error_retval;
+ }
+
+ *static_cast<int*>(optval) = (state & enable_connection_aborted) ? 1 : 0;
+ ec = asio::error_code();
+ return 0;
+ }
+
+#if defined(__BORLANDC__)
+ // Mysteriously, using the getsockopt and setsockopt functions directly with
+ // Borland C++ results in incorrect values being set and read. The bug can be
+ // worked around by using function addresses resolved with GetProcAddress.
+ if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32"))
+ {
+ typedef int (WSAAPI *gso_t)(SOCKET, int, int, char*, int*);
+ if (gso_t gso = (gso_t)::GetProcAddress(winsock_module, "getsockopt"))
+ {
+ clear_last_error();
+ int tmp_optlen = static_cast<int>(*optlen);
+ int result = error_wrapper(gso(s, level, optname,
+ reinterpret_cast<char*>(optval), &tmp_optlen), ec);
+ *optlen = static_cast<size_t>(tmp_optlen);
+ if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY
+ && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD))
+ {
+ // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are
+ // only supported on Windows Vista and later. To simplify program logic
+ // we will fake success of getting this option and specify that the
+ // value is non-zero (i.e. true). This corresponds to the behavior of
+ // IPv6 sockets on Windows platforms pre-Vista.
+ *static_cast<DWORD*>(optval) = 1;
+ ec = asio::error_code();
+ }
+ return result;
+ }
+ }
+ ec = asio::error::fault;
+ return socket_error_retval;
+#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ clear_last_error();
+ int result = error_wrapper(call_getsockopt(&msghdr::msg_namelen,
+ s, level, optname, optval, optlen), ec);
+ if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY
+ && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD))
+ {
+ // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are only
+ // supported on Windows Vista and later. To simplify program logic we will
+ // fake success of getting this option and specify that the value is
+ // non-zero (i.e. true). This corresponds to the behavior of IPv6 sockets
+ // on Windows platforms pre-Vista.
+ *static_cast<DWORD*>(optval) = 1;
+ ec = asio::error_code();
+ }
+ if (result == 0)
+ ec = asio::error_code();
+ return result;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ clear_last_error();
+ int result = error_wrapper(call_getsockopt(&msghdr::msg_namelen,
+ s, level, optname, optval, optlen), ec);
+#if defined(__linux__)
+ if (result == 0 && level == SOL_SOCKET && *optlen == sizeof(int)
+ && (optname == SO_SNDBUF || optname == SO_RCVBUF))
+ {
+ // On Linux, setting SO_SNDBUF or SO_RCVBUF to N actually causes the kernel
+ // to set the buffer size to N*2. Linux puts additional stuff into the
+ // buffers so that only about half is actually available to the application.
+ // The retrieved value is divided by 2 here to make it appear as though the
+ // correct value has been set.
+ *static_cast<int*>(optval) /= 2;
+ }
+#endif // defined(__linux__)
+ if (result == 0)
+ ec = asio::error_code();
+ return result;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+template <typename SockLenType>
+inline int call_getpeername(SockLenType msghdr::*,
+ socket_type s, socket_addr_type* addr, std::size_t* addrlen)
+{
+ SockLenType tmp_addrlen = (SockLenType)*addrlen;
+ int result = ::getpeername(s, addr, &tmp_addrlen);
+ *addrlen = (std::size_t)tmp_addrlen;
+ return result;
+}
+
+ASIO_DECL
+int getpeername(socket_type s, socket_addr_type* addr,
+ std::size_t* addrlen, bool cached, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return socket_error_retval;
+ }
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ if (cached)
+ {
+ // Check if socket is still connected.
+ DWORD connect_time = 0;
+ size_t connect_time_len = sizeof(connect_time);
+ if (socket_ops::getsockopt(s, 0, SOL_SOCKET, SO_CONNECT_TIME,
+ &connect_time, &connect_time_len, ec) == socket_error_retval)
+ {
+ return socket_error_retval;
+ }
+ if (connect_time == 0xFFFFFFFF)
+ {
+ ec = asio::error::not_connected;
+ return socket_error_retval;
+ }
+
+ // The cached value is still valid.
+ ec = asio::error_code();
+ return 0;
+ }
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ (void)cached;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+ clear_last_error();
+ int result = error_wrapper(call_getpeername(
+ &msghdr::msg_namelen, s, addr, addrlen), ec);
+ if (result == 0)
+ ec = asio::error_code();
+ return result;
+}
+
+template <typename SockLenType>
+inline int call_getsockname(SockLenType msghdr::*,
+ socket_type s, socket_addr_type* addr, std::size_t* addrlen)
+{
+ SockLenType tmp_addrlen = (SockLenType)*addrlen;
+ int result = ::getsockname(s, addr, &tmp_addrlen);
+ *addrlen = (std::size_t)tmp_addrlen;
+ return result;
+}
+
+ASIO_DECL
+int getsockname(socket_type s, socket_addr_type* addr,
+ std::size_t* addrlen, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return socket_error_retval;
+ }
+
+ clear_last_error();
+ int result = error_wrapper(call_getsockname(
+ &msghdr::msg_namelen, s, addr, addrlen), ec);
+ if (result == 0)
+ ec = asio::error_code();
+ return result;
+}
+
+ASIO_DECL
+int ioctl(socket_type s, state_type& state, int cmd,
+ ioctl_arg_type* arg, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return socket_error_retval;
+ }
+
+ clear_last_error();
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ int result = error_wrapper(::ioctlsocket(s, cmd, arg), ec);
+#elif defined(__MACH__) && defined(__APPLE__) \
+ || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ int result = error_wrapper(::ioctl(s,
+ static_cast<unsigned int>(cmd), arg), ec);
+#else
+ int result = error_wrapper(::ioctl(s, cmd, arg), ec);
+#endif
+ if (result >= 0)
+ {
+ ec = asio::error_code();
+
+ // When updating the non-blocking mode we always perform the ioctl syscall,
+ // even if the flags would otherwise indicate that the socket is already in
+ // the correct state. This ensures that the underlying socket is put into
+ // the state that has been requested by the user. If the ioctl syscall was
+ // successful then we need to update the flags to match.
+ if (cmd == static_cast<int>(FIONBIO))
+ {
+ if (*arg)
+ {
+ state |= user_set_non_blocking;
+ }
+ else
+ {
+ // Clearing the non-blocking mode always overrides any internally-set
+ // non-blocking flag. Any subsequent asynchronous operations will need
+ // to re-enable non-blocking I/O.
+ state &= ~(user_set_non_blocking | internal_non_blocking);
+ }
+ }
+ }
+
+ return result;
+}
+
+ASIO_DECL
+int select(int nfds, fd_set* readfds, fd_set* writefds,
+ fd_set* exceptfds, timeval* timeout, asio::error_code& ec)
+{
+ clear_last_error();
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ if (!readfds && !writefds && !exceptfds && timeout)
+ {
+ DWORD milliseconds = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
+ if (milliseconds == 0)
+ milliseconds = 1; // Force context switch.
+ ::Sleep(milliseconds);
+ ec = asio::error_code();
+ return 0;
+ }
+
+ // The select() call allows timeout values measured in microseconds, but the
+ // system clock (as wrapped by boost::posix_time::microsec_clock) typically
+ // has a resolution of 10 milliseconds. This can lead to a spinning select
+ // reactor, meaning increased CPU usage, when waiting for the earliest
+ // scheduled timeout if it's less than 10 milliseconds away. To avoid a tight
+ // spin we'll use a minimum timeout of 1 millisecond.
+ if (timeout && timeout->tv_sec == 0
+ && timeout->tv_usec > 0 && timeout->tv_usec < 1000)
+ timeout->tv_usec = 1000;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+#if defined(__hpux) && defined(__SELECT)
+ timespec ts;
+ ts.tv_sec = timeout ? timeout->tv_sec : 0;
+ ts.tv_nsec = timeout ? timeout->tv_usec * 1000 : 0;
+ return error_wrapper(::pselect(nfds, readfds,
+ writefds, exceptfds, timeout ? &ts : 0, 0), ec);
+#else
+ int result = error_wrapper(::select(nfds, readfds,
+ writefds, exceptfds, timeout), ec);
+ if (result >= 0)
+ ec = asio::error_code();
+ return result;
+#endif
+}
+
+ASIO_DECL
+int poll_read(socket_type s, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return socket_error_retval;
+ }
+
+#if defined(BOOST_WINDOWS) \
+ || defined(__CYGWIN__) \
+ || defined(__SYMBIAN32__)
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(s, &fds);
+ clear_last_error();
+ int result = error_wrapper(::select(s, &fds, 0, 0, 0), ec);
+ if (result >= 0)
+ ec = asio::error_code();
+ return result;
+#else // defined(BOOST_WINDOWS)
+ // || defined(__CYGWIN__)
+ // || defined(__SYMBIAN32__)
+ pollfd fds;
+ fds.fd = s;
+ fds.events = POLLIN;
+ fds.revents = 0;
+ clear_last_error();
+ int result = error_wrapper(::poll(&fds, 1, -1), ec);
+ if (result >= 0)
+ ec = asio::error_code();
+ return result;
+#endif // defined(BOOST_WINDOWS)
+ // || defined(__CYGWIN__)
+ // || defined(__SYMBIAN32__)
+}
+
+ASIO_DECL
+int poll_write(socket_type s, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return socket_error_retval;
+ }
+
+#if defined(BOOST_WINDOWS) \
+ || defined(__CYGWIN__) \
+ || defined(__SYMBIAN32__)
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(s, &fds);
+ clear_last_error();
+ int result = error_wrapper(::select(s, 0, &fds, 0, 0), ec);
+ if (result >= 0)
+ ec = asio::error_code();
+ return result;
+#else // defined(BOOST_WINDOWS)
+ // || defined(__CYGWIN__)
+ // || defined(__SYMBIAN32__)
+ pollfd fds;
+ fds.fd = s;
+ fds.events = POLLOUT;
+ fds.revents = 0;
+ clear_last_error();
+ int result = error_wrapper(::poll(&fds, 1, -1), ec);
+ if (result >= 0)
+ ec = asio::error_code();
+ return result;
+#endif // defined(BOOST_WINDOWS)
+ // || defined(__CYGWIN__)
+ // || defined(__SYMBIAN32__)
+}
+
+ASIO_DECL
+int poll_connect(socket_type s, asio::error_code& ec)
+{
+ if (s == invalid_socket)
+ {
+ ec = asio::error::bad_descriptor;
+ return socket_error_retval;
+ }
+
+#if defined(BOOST_WINDOWS) \
+ || defined(__CYGWIN__) \
+ || defined(__SYMBIAN32__)
+ fd_set write_fds;
+ FD_ZERO(&write_fds);
+ FD_SET(s, &write_fds);
+ fd_set except_fds;
+ FD_ZERO(&except_fds);
+ FD_SET(s, &except_fds);
+ clear_last_error();
+ int result = error_wrapper(::select(s, 0, &write_fds, &except_fds, 0), ec);
+ if (result >= 0)
+ ec = asio::error_code();
+ return result;
+#else // defined(BOOST_WINDOWS)
+ // || defined(__CYGWIN__)
+ // || defined(__SYMBIAN32__)
+ pollfd fds;
+ fds.fd = s;
+ fds.events = POLLOUT;
+ fds.revents = 0;
+ clear_last_error();
+ int result = error_wrapper(::poll(&fds, 1, -1), ec);
+ if (result >= 0)
+ ec = asio::error_code();
+ return result;
+#endif // defined(BOOST_WINDOWS)
+ // || defined(__CYGWIN__)
+ // || defined(__SYMBIAN32__)
+}
+
+ASIO_DECL
+const char* inet_ntop(int af, const void* src, char* dest, size_t length,
+ unsigned long scope_id, asio::error_code& ec)
+{
+ clear_last_error();
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ using namespace std; // For memcpy.
+
+ if (af != AF_INET && af != AF_INET6)
+ {
+ ec = asio::error::address_family_not_supported;
+ return 0;
+ }
+
+ union
+ {
+ socket_addr_type base;
+ sockaddr_storage_type storage;
+ sockaddr_in4_type v4;
+ sockaddr_in6_type v6;
+ } address;
+ DWORD address_length;
+ if (af == AF_INET)
+ {
+ address_length = sizeof(sockaddr_in4_type);
+ address.v4.sin_family = AF_INET;
+ address.v4.sin_port = 0;
+ memcpy(&address.v4.sin_addr, src, sizeof(in4_addr_type));
+ }
+ else // AF_INET6
+ {
+ address_length = sizeof(sockaddr_in6_type);
+ address.v6.sin6_family = AF_INET6;
+ address.v6.sin6_port = 0;
+ address.v6.sin6_flowinfo = 0;
+ address.v6.sin6_scope_id = scope_id;
+ memcpy(&address.v6.sin6_addr, src, sizeof(in6_addr_type));
+ }
+
+ DWORD string_length = static_cast<DWORD>(length);
+#if defined(BOOST_NO_ANSI_APIS)
+ LPWSTR string_buffer = (LPWSTR)_alloca(length * sizeof(WCHAR));
+ int result = error_wrapper(::WSAAddressToStringW(&address.base,
+ address_length, 0, string_buffer, &string_length), ec);
+ ::WideCharToMultiByte(CP_ACP, 0, string_buffer, -1, dest, length, 0, 0);
+#else
+ int result = error_wrapper(::WSAAddressToStringA(
+ &address.base, address_length, 0, dest, &string_length), ec);
+#endif
+
+ // Windows may set error code on success.
+ if (result != socket_error_retval)
+ ec = asio::error_code();
+
+ // Windows may not set an error code on failure.
+ else if (result == socket_error_retval && !ec)
+ ec = asio::error::invalid_argument;
+
+ return result == socket_error_retval ? 0 : dest;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ const char* result = error_wrapper(::inet_ntop(af, src, dest, length), ec);
+ if (result == 0 && !ec)
+ ec = asio::error::invalid_argument;
+ if (result != 0 && af == AF_INET6 && scope_id != 0)
+ {
+ using namespace std; // For strcat and sprintf.
+ char if_name[IF_NAMESIZE + 1] = "%";
+ const in6_addr_type* ipv6_address = static_cast<const in6_addr_type*>(src);
+ bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address);
+ if (!is_link_local || if_indextoname(scope_id, if_name + 1) == 0)
+ sprintf(if_name + 1, "%lu", scope_id);
+ strcat(dest, if_name);
+ }
+ return result;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+ASIO_DECL
+int inet_pton(int af, const char* src, void* dest,
+ unsigned long* scope_id, asio::error_code& ec)
+{
+ clear_last_error();
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ using namespace std; // For memcpy and strcmp.
+
+ if (af != AF_INET && af != AF_INET6)
+ {
+ ec = asio::error::address_family_not_supported;
+ return -1;
+ }
+
+ union
+ {
+ socket_addr_type base;
+ sockaddr_storage_type storage;
+ sockaddr_in4_type v4;
+ sockaddr_in6_type v6;
+ } address;
+ int address_length = sizeof(sockaddr_storage_type);
+#if defined(BOOST_NO_ANSI_APIS)
+ int num_wide_chars = strlen(src) + 1;
+ LPWSTR wide_buffer = (LPWSTR)_alloca(num_wide_chars * sizeof(WCHAR));
+ ::MultiByteToWideChar(CP_ACP, 0, src, -1, wide_buffer, num_wide_chars);
+ int result = error_wrapper(::WSAStringToAddressW(
+ wide_buffer, af, 0, &address.base, &address_length), ec);
+#else
+ int result = error_wrapper(::WSAStringToAddressA(
+ const_cast<char*>(src), af, 0, &address.base, &address_length), ec);
+#endif
+
+ if (af == AF_INET)
+ {
+ if (result != socket_error_retval)
+ {
+ memcpy(dest, &address.v4.sin_addr, sizeof(in4_addr_type));
+ ec = asio::error_code();
+ }
+ else if (strcmp(src, "255.255.255.255") == 0)
+ {
+ static_cast<in4_addr_type*>(dest)->s_addr = INADDR_NONE;
+ ec = asio::error_code();
+ }
+ }
+ else // AF_INET6
+ {
+ if (result != socket_error_retval)
+ {
+ memcpy(dest, &address.v6.sin6_addr, sizeof(in6_addr_type));
+ if (scope_id)
+ *scope_id = address.v6.sin6_scope_id;
+ ec = asio::error_code();
+ }
+ }
+
+ // Windows may not set an error code on failure.
+ if (result == socket_error_retval && !ec)
+ ec = asio::error::invalid_argument;
+
+ if (result != socket_error_retval)
+ ec = asio::error_code();
+
+ return result == socket_error_retval ? -1 : 1;
+#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ int result = error_wrapper(::inet_pton(af, src, dest), ec);
+ if (result <= 0 && !ec)
+ ec = asio::error::invalid_argument;
+ if (result > 0 && af == AF_INET6 && scope_id)
+ {
+ using namespace std; // For strchr and atoi.
+ *scope_id = 0;
+ if (const char* if_name = strchr(src, '%'))
+ {
+ in6_addr_type* ipv6_address = static_cast<in6_addr_type*>(dest);
+ bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address);
+ if (is_link_local)
+ *scope_id = if_nametoindex(if_name + 1);
+ if (*scope_id == 0)
+ *scope_id = atoi(if_name + 1);
+ }
+ }
+ return result;
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+}
+
+ASIO_DECL
+int gethostname(char* name, int namelen, asio::error_code& ec)
+{
+ clear_last_error();
+ int result = error_wrapper(::gethostname(name, namelen), ec);
+#if defined(BOOST_WINDOWS)
+ if (result == 0)
+ ec = asio::error_code();
+#endif
+ return result;
+}
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) \
+ || defined(__MACH__) && defined(__APPLE__)
+
+// The following functions are only needed for emulation of getaddrinfo and
+// getnameinfo.
+
+inline asio::error_code translate_netdb_error(int error)
+{
+ switch (error)
+ {
+ case 0:
+ return asio::error_code();
+ case HOST_NOT_FOUND:
+ return asio::error::host_not_found;
+ case TRY_AGAIN:
+ return asio::error::host_not_found_try_again;
+ case NO_RECOVERY:
+ return asio::error::no_recovery;
+ case NO_DATA:
+ return asio::error::no_data;
+ default:
+ BOOST_ASSERT(false);
+ return asio::error::invalid_argument;
+ }
+}
+
+inline hostent* gethostbyaddr(const char* addr, int length, int af,
+ hostent* result, char* buffer, int buflength, asio::error_code& ec)
+{
+ clear_last_error();
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ (void)(buffer);
+ (void)(buflength);
+ hostent* retval = error_wrapper(::gethostbyaddr(addr, length, af), ec);
+ if (!retval)
+ return 0;
+ ec = asio::error_code();
+ *result = *retval;
+ return retval;
+#elif defined(__sun) || defined(__QNX__)
+ int error = 0;
+ hostent* retval = error_wrapper(::gethostbyaddr_r(addr, length, af, result,
+ buffer, buflength, &error), ec);
+ if (error)
+ ec = translate_netdb_error(error);
+ return retval;
+#elif defined(__MACH__) && defined(__APPLE__)
+ (void)(buffer);
+ (void)(buflength);
+ int error = 0;
+ hostent* retval = error_wrapper(::getipnodebyaddr(
+ addr, length, af, &error), ec);
+ if (error)
+ ec = translate_netdb_error(error);
+ if (!retval)
+ return 0;
+ *result = *retval;
+ return retval;
+#else
+ hostent* retval = 0;
+ int error = 0;
+ error_wrapper(::gethostbyaddr_r(addr, length, af, result, buffer,
+ buflength, &retval, &error), ec);
+ if (error)
+ ec = translate_netdb_error(error);
+ return retval;
+#endif
+}
+
+inline hostent* gethostbyname(const char* name, int af, struct hostent* result,
+ char* buffer, int buflength, int ai_flags, asio::error_code& ec)
+{
+ clear_last_error();
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ (void)(buffer);
+ (void)(buflength);
+ (void)(ai_flags);
+ if (af != AF_INET)
+ {
+ ec = asio::error::address_family_not_supported;
+ return 0;
+ }
+ hostent* retval = error_wrapper(::gethostbyname(name), ec);
+ if (!retval)
+ return 0;
+ ec = asio::error_code();
+ *result = *retval;
+ return result;
+#elif defined(__sun) || defined(__QNX__)
+ (void)(ai_flags);
+ if (af != AF_INET)
+ {
+ ec = asio::error::address_family_not_supported;
+ return 0;
+ }
+ int error = 0;
+ hostent* retval = error_wrapper(::gethostbyname_r(name, result, buffer,
+ buflength, &error), ec);
+ if (error)
+ ec = translate_netdb_error(error);
+ return retval;
+#elif defined(__MACH__) && defined(__APPLE__)
+ (void)(buffer);
+ (void)(buflength);
+ int error = 0;
+ hostent* retval = error_wrapper(::getipnodebyname(
+ name, af, ai_flags, &error), ec);
+ if (error)
+ ec = translate_netdb_error(error);
+ if (!retval)
+ return 0;
+ *result = *retval;
+ return retval;
+#else
+ (void)(ai_flags);
+ if (af != AF_INET)
+ {
+ ec = asio::error::address_family_not_supported;
+ return 0;
+ }
+ hostent* retval = 0;
+ int error = 0;
+ error_wrapper(::gethostbyname_r(name, result,
+ buffer, buflength, &retval, &error), ec);
+ if (error)
+ ec = translate_netdb_error(error);
+ return retval;
+#endif
+}
+
+inline void freehostent(hostent* h)
+{
+#if defined(__MACH__) && defined(__APPLE__)
+ if (h)
+ ::freehostent(h);
+#else
+ (void)(h);
+#endif
+}
+
+// Emulation of getaddrinfo based on implementation in:
+// Stevens, W. R., UNIX Network Programming Vol. 1, 2nd Ed., Prentice-Hall 1998.
+
+struct gai_search
+{
+ const char* host;
+ int family;
+};
+
+inline int gai_nsearch(const char* host,
+ const addrinfo_type* hints, gai_search (&search)[2])
+{
+ int search_count = 0;
+ if (host == 0 || host[0] == '\0')
+ {
+ if (hints->ai_flags & AI_PASSIVE)
+ {
+ // No host and AI_PASSIVE implies wildcard bind.
+ switch (hints->ai_family)
+ {
+ case AF_INET:
+ search[search_count].host = "0.0.0.0";
+ search[search_count].family = AF_INET;
+ ++search_count;
+ break;
+ case AF_INET6:
+ search[search_count].host = "0::0";
+ search[search_count].family = AF_INET6;
+ ++search_count;
+ break;
+ case AF_UNSPEC:
+ search[search_count].host = "0::0";
+ search[search_count].family = AF_INET6;
+ ++search_count;
+ search[search_count].host = "0.0.0.0";
+ search[search_count].family = AF_INET;
+ ++search_count;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ // No host and not AI_PASSIVE means connect to local host.
+ switch (hints->ai_family)
+ {
+ case AF_INET:
+ search[search_count].host = "localhost";
+ search[search_count].family = AF_INET;
+ ++search_count;
+ break;
+ case AF_INET6:
+ search[search_count].host = "localhost";
+ search[search_count].family = AF_INET6;
+ ++search_count;
+ break;
+ case AF_UNSPEC:
+ search[search_count].host = "localhost";
+ search[search_count].family = AF_INET6;
+ ++search_count;
+ search[search_count].host = "localhost";
+ search[search_count].family = AF_INET;
+ ++search_count;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Host is specified.
+ switch (hints->ai_family)
+ {
+ case AF_INET:
+ search[search_count].host = host;
+ search[search_count].family = AF_INET;
+ ++search_count;
+ break;
+ case AF_INET6:
+ search[search_count].host = host;
+ search[search_count].family = AF_INET6;
+ ++search_count;
+ break;
+ case AF_UNSPEC:
+ search[search_count].host = host;
+ search[search_count].family = AF_INET6;
+ ++search_count;
+ search[search_count].host = host;
+ search[search_count].family = AF_INET;
+ ++search_count;
+ break;
+ default:
+ break;
+ }
+ }
+ return search_count;
+}
+
+template <typename T>
+inline T* gai_alloc(std::size_t size = sizeof(T))
+{
+ using namespace std;
+ T* p = static_cast<T*>(::operator new(size, std::nothrow));
+ if (p)
+ memset(p, 0, size);
+ return p;
+}
+
+inline void gai_free(void* p)
+{
+ ::operator delete(p);
+}
+
+inline void gai_strcpy(char* target, const char* source, std::size_t max_size)
+{
+ using namespace std;
+#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE)
+ strcpy_s(target, max_size, source);
+#else
+ *target = 0;
+ strncat(target, source, max_size);
+#endif
+}
+
+enum { gai_clone_flag = 1 << 30 };
+
+inline int gai_aistruct(addrinfo_type*** next, const addrinfo_type* hints,
+ const void* addr, int family)
+{
+ using namespace std;
+
+ addrinfo_type* ai = gai_alloc<addrinfo_type>();
+ if (ai == 0)
+ return EAI_MEMORY;
+
+ ai->ai_next = 0;
+ **next = ai;
+ *next = &ai->ai_next;
+
+ ai->ai_canonname = 0;
+ ai->ai_socktype = hints->ai_socktype;
+ if (ai->ai_socktype == 0)
+ ai->ai_flags |= gai_clone_flag;
+ ai->ai_protocol = hints->ai_protocol;
+ ai->ai_family = family;
+
+ switch (ai->ai_family)
+ {
+ case AF_INET:
+ {
+ sockaddr_in4_type* sinptr = gai_alloc<sockaddr_in4_type>();
+ if (sinptr == 0)
+ return EAI_MEMORY;
+ sinptr->sin_family = AF_INET;
+ memcpy(&sinptr->sin_addr, addr, sizeof(in4_addr_type));
+ ai->ai_addr = reinterpret_cast<sockaddr*>(sinptr);
+ ai->ai_addrlen = sizeof(sockaddr_in4_type);
+ break;
+ }
+ case AF_INET6:
+ {
+ sockaddr_in6_type* sin6ptr = gai_alloc<sockaddr_in6_type>();
+ if (sin6ptr == 0)
+ return EAI_MEMORY;
+ sin6ptr->sin6_family = AF_INET6;
+ memcpy(&sin6ptr->sin6_addr, addr, sizeof(in6_addr_type));
+ ai->ai_addr = reinterpret_cast<sockaddr*>(sin6ptr);
+ ai->ai_addrlen = sizeof(sockaddr_in6_type);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+inline addrinfo_type* gai_clone(addrinfo_type* ai)
+{
+ using namespace std;
+
+ addrinfo_type* new_ai = gai_alloc<addrinfo_type>();
+ if (new_ai == 0)
+ return new_ai;
+
+ new_ai->ai_next = ai->ai_next;
+ ai->ai_next = new_ai;
+
+ new_ai->ai_flags = 0;
+ new_ai->ai_family = ai->ai_family;
+ new_ai->ai_socktype = ai->ai_socktype;
+ new_ai->ai_protocol = ai->ai_protocol;
+ new_ai->ai_canonname = 0;
+ new_ai->ai_addrlen = ai->ai_addrlen;
+ new_ai->ai_addr = gai_alloc<sockaddr>(ai->ai_addrlen);
+ memcpy(new_ai->ai_addr, ai->ai_addr, ai->ai_addrlen);
+
+ return new_ai;
+}
+
+inline int gai_port(addrinfo_type* aihead, int port, int socktype)
+{
+ int num_found = 0;
+
+ for (addrinfo_type* ai = aihead; ai; ai = ai->ai_next)
+ {
+ if (ai->ai_flags & gai_clone_flag)
+ {
+ if (ai->ai_socktype != 0)
+ {
+ ai = gai_clone(ai);
+ if (ai == 0)
+ return -1;
+ // ai now points to newly cloned entry.
+ }
+ }
+ else if (ai->ai_socktype != socktype)
+ {
+ // Ignore if mismatch on socket type.
+ continue;
+ }
+
+ ai->ai_socktype = socktype;
+
+ switch (ai->ai_family)
+ {
+ case AF_INET:
+ {
+ sockaddr_in4_type* sinptr =
+ reinterpret_cast<sockaddr_in4_type*>(ai->ai_addr);
+ sinptr->sin_port = port;
+ ++num_found;
+ break;
+ }
+ case AF_INET6:
+ {
+ sockaddr_in6_type* sin6ptr =
+ reinterpret_cast<sockaddr_in6_type*>(ai->ai_addr);
+ sin6ptr->sin6_port = port;
+ ++num_found;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return num_found;
+}
+
+inline int gai_serv(addrinfo_type* aihead,
+ const addrinfo_type* hints, const char* serv)
+{
+ using namespace std;
+
+ int num_found = 0;
+
+ if (
+#if defined(AI_NUMERICSERV)
+ (hints->ai_flags & AI_NUMERICSERV) ||
+#endif
+ isdigit(static_cast<unsigned char>(serv[0])))
+ {
+ int port = htons(atoi(serv));
+ if (hints->ai_socktype)
+ {
+ // Caller specifies socket type.
+ int rc = gai_port(aihead, port, hints->ai_socktype);
+ if (rc < 0)
+ return EAI_MEMORY;
+ num_found += rc;
+ }
+ else
+ {
+ // Caller does not specify socket type.
+ int rc = gai_port(aihead, port, SOCK_STREAM);
+ if (rc < 0)
+ return EAI_MEMORY;
+ num_found += rc;
+ rc = gai_port(aihead, port, SOCK_DGRAM);
+ if (rc < 0)
+ return EAI_MEMORY;
+ num_found += rc;
+ }
+ }
+ else
+ {
+ // Try service name with TCP first, then UDP.
+ if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_STREAM)
+ {
+ servent* sptr = getservbyname(serv, "tcp");
+ if (sptr != 0)
+ {
+ int rc = gai_port(aihead, sptr->s_port, SOCK_STREAM);
+ if (rc < 0)
+ return EAI_MEMORY;
+ num_found += rc;
+ }
+ }
+ if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_DGRAM)
+ {
+ servent* sptr = getservbyname(serv, "udp");
+ if (sptr != 0)
+ {
+ int rc = gai_port(aihead, sptr->s_port, SOCK_DGRAM);
+ if (rc < 0)
+ return EAI_MEMORY;
+ num_found += rc;
+ }
+ }
+ }
+
+ if (num_found == 0)
+ {
+ if (hints->ai_socktype == 0)
+ {
+ // All calls to getservbyname() failed.
+ return EAI_NONAME;
+ }
+ else
+ {
+ // Service not supported for socket type.
+ return EAI_SERVICE;
+ }
+ }
+
+ return 0;
+}
+
+inline int gai_echeck(const char* host, const char* service,
+ int flags, int family, int socktype, int protocol)
+{
+ (void)(flags);
+ (void)(protocol);
+
+ // Host or service must be specified.
+ if (host == 0 || host[0] == '\0')
+ if (service == 0 || service[0] == '\0')
+ return EAI_NONAME;
+
+ // Check combination of family and socket type.
+ switch (family)
+ {
+ case AF_UNSPEC:
+ break;
+ case AF_INET:
+ case AF_INET6:
+ if (service != 0 && service[0] != '\0')
+ if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
+ return EAI_SOCKTYPE;
+ break;
+ default:
+ return EAI_FAMILY;
+ }
+
+ return 0;
+}
+
+inline void freeaddrinfo_emulation(addrinfo_type* aihead)
+{
+ addrinfo_type* ai = aihead;
+ while (ai)
+ {
+ gai_free(ai->ai_addr);
+ gai_free(ai->ai_canonname);
+ addrinfo_type* ainext = ai->ai_next;
+ gai_free(ai);
+ ai = ainext;
+ }
+}
+
+inline int getaddrinfo_emulation(const char* host, const char* service,
+ const addrinfo_type* hintsp, addrinfo_type** result)
+{
+ // Set up linked list of addrinfo structures.
+ addrinfo_type* aihead = 0;
+ addrinfo_type** ainext = &aihead;
+ char* canon = 0;
+
+ // Supply default hints if not specified by caller.
+ addrinfo_type hints = addrinfo_type();
+ hints.ai_family = AF_UNSPEC;
+ if (hintsp)
+ hints = *hintsp;
+
+ // If the resolution is not specifically for AF_INET6, remove the AI_V4MAPPED
+ // and AI_ALL flags.
+#if defined(AI_V4MAPPED)
+ if (hints.ai_family != AF_INET6)
+ hints.ai_flags &= ~AI_V4MAPPED;
+#endif
+#if defined(AI_ALL)
+ if (hints.ai_family != AF_INET6)
+ hints.ai_flags &= ~AI_ALL;
+#endif
+
+ // Basic error checking.
+ int rc = gai_echeck(host, service, hints.ai_flags, hints.ai_family,
+ hints.ai_socktype, hints.ai_protocol);
+ if (rc != 0)
+ {
+ freeaddrinfo_emulation(aihead);
+ return rc;
+ }
+
+ gai_search search[2];
+ int search_count = gai_nsearch(host, &hints, search);
+ for (gai_search* sptr = search; sptr < search + search_count; ++sptr)
+ {
+ // Check for IPv4 dotted decimal string.
+ in4_addr_type inaddr;
+ asio::error_code ec;
+ if (socket_ops::inet_pton(AF_INET, sptr->host, &inaddr, 0, ec) == 1)
+ {
+ if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET)
+ {
+ freeaddrinfo_emulation(aihead);
+ gai_free(canon);
+ return EAI_FAMILY;
+ }
+ if (sptr->family == AF_INET)
+ {
+ rc = gai_aistruct(&ainext, &hints, &inaddr, AF_INET);
+ if (rc != 0)
+ {
+ freeaddrinfo_emulation(aihead);
+ gai_free(canon);
+ return rc;
+ }
+ }
+ continue;
+ }
+
+ // Check for IPv6 hex string.
+ in6_addr_type in6addr;
+ if (socket_ops::inet_pton(AF_INET6, sptr->host, &in6addr, 0, ec) == 1)
+ {
+ if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET6)
+ {
+ freeaddrinfo_emulation(aihead);
+ gai_free(canon);
+ return EAI_FAMILY;
+ }
+ if (sptr->family == AF_INET6)
+ {
+ rc = gai_aistruct(&ainext, &hints, &in6addr, AF_INET6);
+ if (rc != 0)
+ {
+ freeaddrinfo_emulation(aihead);
+ gai_free(canon);
+ return rc;
+ }
+ }
+ continue;
+ }
+
+ // Look up hostname.
+ hostent hent;
+ char hbuf[8192] = "";
+ hostent* hptr = socket_ops::gethostbyname(sptr->host,
+ sptr->family, &hent, hbuf, sizeof(hbuf), hints.ai_flags, ec);
+ if (hptr == 0)
+ {
+ if (search_count == 2)
+ {
+ // Failure is OK if there are multiple searches.
+ continue;
+ }
+ freeaddrinfo_emulation(aihead);
+ gai_free(canon);
+ if (ec == asio::error::host_not_found)
+ return EAI_NONAME;
+ if (ec == asio::error::host_not_found_try_again)
+ return EAI_AGAIN;
+ if (ec == asio::error::no_recovery)
+ return EAI_FAIL;
+ if (ec == asio::error::no_data)
+ return EAI_NONAME;
+ return EAI_NONAME;
+ }
+
+ // Check for address family mismatch if one was specified.
+ if (hints.ai_family != AF_UNSPEC && hints.ai_family != hptr->h_addrtype)
+ {
+ freeaddrinfo_emulation(aihead);
+ gai_free(canon);
+ socket_ops::freehostent(hptr);
+ return EAI_FAMILY;
+ }
+
+ // Save canonical name first time.
+ if (host != 0 && host[0] != '\0' && hptr->h_name && hptr->h_name[0]
+ && (hints.ai_flags & AI_CANONNAME) && canon == 0)
+ {
+ std::size_t canon_len = strlen(hptr->h_name) + 1;
+ canon = gai_alloc<char>(canon_len);
+ if (canon == 0)
+ {
+ freeaddrinfo_emulation(aihead);
+ socket_ops::freehostent(hptr);
+ return EAI_MEMORY;
+ }
+ gai_strcpy(canon, hptr->h_name, canon_len);
+ }
+
+ // Create an addrinfo structure for each returned address.
+ for (char** ap = hptr->h_addr_list; *ap; ++ap)
+ {
+ rc = gai_aistruct(&ainext, &hints, *ap, hptr->h_addrtype);
+ if (rc != 0)
+ {
+ freeaddrinfo_emulation(aihead);
+ gai_free(canon);
+ socket_ops::freehostent(hptr);
+ return EAI_FAMILY;
+ }
+ }
+
+ socket_ops::freehostent(hptr);
+ }
+
+ // Check if we found anything.
+ if (aihead == 0)
+ {
+ gai_free(canon);
+ return EAI_NONAME;
+ }
+
+ // Return canonical name in first entry.
+ if (host != 0 && host[0] != '\0' && (hints.ai_flags & AI_CANONNAME))
+ {
+ if (canon)
+ {
+ aihead->ai_canonname = canon;
+ canon = 0;
+ }
+ else
+ {
+ std::size_t canonname_len = strlen(search[0].host) + 1;
+ aihead->ai_canonname = gai_alloc<char>(canonname_len);
+ if (aihead->ai_canonname == 0)
+ {
+ freeaddrinfo_emulation(aihead);
+ return EAI_MEMORY;
+ }
+ gai_strcpy(aihead->ai_canonname, search[0].host, canonname_len);
+ }
+ }
+ gai_free(canon);
+
+ // Process the service name.
+ if (service != 0 && service[0] != '\0')
+ {
+ rc = gai_serv(aihead, &hints, service);
+ if (rc != 0)
+ {
+ freeaddrinfo_emulation(aihead);
+ return rc;
+ }
+ }
+
+ // Return result to caller.
+ *result = aihead;
+ return 0;
+}
+
+inline asio::error_code getnameinfo_emulation(
+ const socket_addr_type* sa, std::size_t salen, char* host,
+ std::size_t hostlen, char* serv, std::size_t servlen, int flags,
+ asio::error_code& ec)
+{
+ using namespace std;
+
+ const char* addr;
+ size_t addr_len;
+ unsigned short port;
+ switch (sa->sa_family)
+ {
+ case AF_INET:
+ if (salen != sizeof(sockaddr_in4_type))
+ {
+ return ec = asio::error::invalid_argument;
+ }
+ addr = reinterpret_cast<const char*>(
+ &reinterpret_cast<const sockaddr_in4_type*>(sa)->sin_addr);
+ addr_len = sizeof(in4_addr_type);
+ port = reinterpret_cast<const sockaddr_in4_type*>(sa)->sin_port;
+ break;
+ case AF_INET6:
+ if (salen != sizeof(sockaddr_in6_type))
+ {
+ return ec = asio::error::invalid_argument;
+ }
+ addr = reinterpret_cast<const char*>(
+ &reinterpret_cast<const sockaddr_in6_type*>(sa)->sin6_addr);
+ addr_len = sizeof(in6_addr_type);
+ port = reinterpret_cast<const sockaddr_in6_type*>(sa)->sin6_port;
+ break;
+ default:
+ return ec = asio::error::address_family_not_supported;
+ }
+
+ if (host && hostlen > 0)
+ {
+ if (flags & NI_NUMERICHOST)
+ {
+ if (socket_ops::inet_ntop(sa->sa_family, addr, host, hostlen, 0, ec) == 0)
+ {
+ return ec;
+ }
+ }
+ else
+ {
+ hostent hent;
+ char hbuf[8192] = "";
+ hostent* hptr = socket_ops::gethostbyaddr(addr,
+ static_cast<int>(addr_len), sa->sa_family,
+ &hent, hbuf, sizeof(hbuf), ec);
+ if (hptr && hptr->h_name && hptr->h_name[0] != '\0')
+ {
+ if (flags & NI_NOFQDN)
+ {
+ char* dot = strchr(hptr->h_name, '.');
+ if (dot)
+ {
+ *dot = 0;
+ }
+ }
+ gai_strcpy(host, hptr->h_name, hostlen);
+ socket_ops::freehostent(hptr);
+ }
+ else
+ {
+ socket_ops::freehostent(hptr);
+ if (flags & NI_NAMEREQD)
+ {
+ return ec = asio::error::host_not_found;
+ }
+ if (socket_ops::inet_ntop(sa->sa_family,
+ addr, host, hostlen, 0, ec) == 0)
+ {
+ return ec;
+ }
+ }
+ }
+ }
+
+ if (serv && servlen > 0)
+ {
+ if (flags & NI_NUMERICSERV)
+ {
+ if (servlen < 6)
+ {
+ return ec = asio::error::no_buffer_space;
+ }
+#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE)
+ sprintf_s(serv, servlen, "%u", ntohs(port));
+#else
+ sprintf(serv, "%u", ntohs(port));
+#endif
+ }
+ else
+ {
+#if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) \
+ && !defined(ASIO_DISABLE_THREADS)
+ static ::pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+ ::pthread_mutex_lock(&mutex);
+#endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS)
+ // && !defined(ASIO_DISABLE_THREADS)
+ servent* sptr = ::getservbyport(port, (flags & NI_DGRAM) ? "udp" : 0);
+ if (sptr && sptr->s_name && sptr->s_name[0] != '\0')
+ {
+ gai_strcpy(serv, sptr->s_name, servlen);
+ }
+ else
+ {
+ if (servlen < 6)
+ {
+ return ec = asio::error::no_buffer_space;
+ }
+#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE)
+ sprintf_s(serv, servlen, "%u", ntohs(port));
+#else
+ sprintf(serv, "%u", ntohs(port));
+#endif
+ }
+#if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) \
+ && !defined(ASIO_DISABLE_THREADS)
+ ::pthread_mutex_unlock(&mutex);
+#endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS)
+ // && !defined(ASIO_DISABLE_THREADS)
+ }
+ }
+
+ ec = asio::error_code();
+ return ec;
+}
+
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ // || defined(__MACH__) && defined(__APPLE__)
+
+inline asio::error_code translate_addrinfo_error(int error)
+{
+ switch (error)
+ {
+ case 0:
+ return asio::error_code();
+ case EAI_AGAIN:
+ return asio::error::host_not_found_try_again;
+ case EAI_BADFLAGS:
+ return asio::error::invalid_argument;
+ case EAI_FAIL:
+ return asio::error::no_recovery;
+ case EAI_FAMILY:
+ return asio::error::address_family_not_supported;
+ case EAI_MEMORY:
+ return asio::error::no_memory;
+ case EAI_NONAME:
+#if defined(EAI_ADDRFAMILY)
+ case EAI_ADDRFAMILY:
+#endif
+#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
+ case EAI_NODATA:
+#endif
+ return asio::error::host_not_found;
+ case EAI_SERVICE:
+ return asio::error::service_not_found;
+ case EAI_SOCKTYPE:
+ return asio::error::socket_type_not_supported;
+ default: // Possibly the non-portable EAI_SYSTEM.
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ return asio::error_code(
+ WSAGetLastError(), asio::error::get_system_category());
+#else
+ return asio::error_code(
+ errno, asio::error::get_system_category());
+#endif
+ }
+}
+
+ASIO_DECL
+asio::error_code getaddrinfo(const char* host,
+ const char* service, const addrinfo_type& hints,
+ addrinfo_type** result, asio::error_code& ec)
+{
+ host = (host && *host) ? host : 0;
+ service = (service && *service) ? service : 0;
+ clear_last_error();
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE)
+ // Building for Windows XP, Windows Server 2003, or later.
+ int error = ::getaddrinfo(host, service, &hints, result);
+ return ec = translate_addrinfo_error(error);
+# else
+ // Building for Windows 2000 or earlier.
+ typedef int (WSAAPI *gai_t)(const char*,
+ const char*, const addrinfo_type*, addrinfo_type**);
+ if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32"))
+ {
+ if (gai_t gai = (gai_t)::GetProcAddress(winsock_module, "getaddrinfo"))
+ {
+ int error = gai(host, service, &hints, result);
+ return ec = translate_addrinfo_error(error);
+ }
+ }
+ int error = getaddrinfo_emulation(host, service, &hints, result);
+ return ec = translate_addrinfo_error(error);
+# endif
+#elif defined(__MACH__) && defined(__APPLE__)
+ int error = getaddrinfo_emulation(host, service, &hints, result);
+ return ec = translate_addrinfo_error(error);
+#else
+ int error = ::getaddrinfo(host, service, &hints, result);
+ return ec = translate_addrinfo_error(error);
+#endif
+}
+
+ASIO_DECL
+asio::error_code background_getaddrinfo(
+ const weak_cancel_token_type& cancel_token, const char* host,
+ const char* service, const addrinfo_type& hints,
+ addrinfo_type** result, asio::error_code& ec)
+{
+ if (cancel_token.expired())
+ ec = asio::error::operation_aborted;
+ else
+ socket_ops::getaddrinfo(host, service, hints, result, ec);
+ return ec;
+}
+
+ASIO_DECL
+void freeaddrinfo(addrinfo_type* ai)
+{
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE)
+ // Building for Windows XP, Windows Server 2003, or later.
+ ::freeaddrinfo(ai);
+# else
+ // Building for Windows 2000 or earlier.
+ typedef int (WSAAPI *fai_t)(addrinfo_type*);
+ if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32"))
+ {
+ if (fai_t fai = (fai_t)::GetProcAddress(winsock_module, "freeaddrinfo"))
+ {
+ fai(ai);
+ return;
+ }
+ }
+ freeaddrinfo_emulation(ai);
+# endif
+#elif defined(__MACH__) && defined(__APPLE__)
+ freeaddrinfo_emulation(ai);
+#else
+ ::freeaddrinfo(ai);
+#endif
+}
+
+ASIO_DECL
+asio::error_code getnameinfo(const socket_addr_type* addr,
+ std::size_t addrlen, char* host, std::size_t hostlen,
+ char* serv, std::size_t servlen, int flags, asio::error_code& ec)
+{
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE)
+ // Building for Windows XP, Windows Server 2003, or later.
+ clear_last_error();
+ int error = ::getnameinfo(addr, static_cast<socklen_t>(addrlen),
+ host, static_cast<DWORD>(hostlen),
+ serv, static_cast<DWORD>(servlen), flags);
+ return ec = translate_addrinfo_error(error);
+# else
+ // Building for Windows 2000 or earlier.
+ typedef int (WSAAPI *gni_t)(const socket_addr_type*,
+ int, char*, DWORD, char*, DWORD, int);
+ if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32"))
+ {
+ if (gni_t gni = (gni_t)::GetProcAddress(winsock_module, "getnameinfo"))
+ {
+ clear_last_error();
+ int error = gni(addr, static_cast<int>(addrlen),
+ host, static_cast<DWORD>(hostlen),
+ serv, static_cast<DWORD>(servlen), flags);
+ return ec = translate_addrinfo_error(error);
+ }
+ }
+ clear_last_error();
+ return getnameinfo_emulation(addr, addrlen,
+ host, hostlen, serv, servlen, flags, ec);
+# endif
+#elif defined(__MACH__) && defined(__APPLE__)
+ using namespace std; // For memcpy.
+ sockaddr_storage_type tmp_addr;
+ memcpy(&tmp_addr, addr, addrlen);
+ tmp_addr.ss_len = addrlen;
+ addr = reinterpret_cast<socket_addr_type*>(&tmp_addr);
+ clear_last_error();
+ return getnameinfo_emulation(addr, addrlen,
+ host, hostlen, serv, servlen, flags, ec);
+#else
+ clear_last_error();
+ int error = ::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags);
+ return ec = translate_addrinfo_error(error);
+#endif
+}
+
+ASIO_DECL
+asio::error_code sync_getnameinfo(
+ const socket_addr_type* addr, std::size_t addrlen,
+ char* host, std::size_t hostlen, char* serv,
+ std::size_t servlen, int sock_type, asio::error_code& ec)
+{
+ // First try resolving with the service name. If that fails try resolving
+ // but allow the service to be returned as a number.
+ int flags = (sock_type == SOCK_DGRAM) ? NI_DGRAM : 0;
+ socket_ops::getnameinfo(addr, addrlen, host,
+ hostlen, serv, servlen, flags, ec);
+ if (ec)
+ {
+ socket_ops::getnameinfo(addr, addrlen, host, hostlen,
+ serv, servlen, flags | NI_NUMERICSERV, ec);
+ }
+
+ return ec;
+}
+
+ASIO_DECL
+asio::error_code background_getnameinfo(
+ const weak_cancel_token_type& cancel_token,
+ const socket_addr_type* addr, std::size_t addrlen,
+ char* host, std::size_t hostlen, char* serv,
+ std::size_t servlen, int sock_type, asio::error_code& ec)
+{
+ if (cancel_token.expired())
+ {
+ ec = asio::error::operation_aborted;
+ }
+ else
+ {
+ // First try resolving with the service name. If that fails try resolving
+ // but allow the service to be returned as a number.
+ int flags = (sock_type == SOCK_DGRAM) ? NI_DGRAM : 0;
+ socket_ops::getnameinfo(addr, addrlen, host,
+ hostlen, serv, servlen, flags, ec);
+ if (ec)
+ {
+ socket_ops::getnameinfo(addr, addrlen, host, hostlen,
+ serv, servlen, flags | NI_NUMERICSERV, ec);
+ }
+ }
+
+ return ec;
+}
+
+ASIO_DECL
+u_long_type network_to_host_long(u_long_type value)
+{
+ return ntohl(value);
+}
+
+ASIO_DECL
+u_long_type host_to_network_long(u_long_type value)
+{
+ return htonl(value);
+}
+
+ASIO_DECL
+u_short_type network_to_host_short(u_short_type value)
+{
+ return ntohs(value);
+}
+
+ASIO_DECL
+u_short_type host_to_network_short(u_short_type value)
+{
+ return htons(value);
+}
+
+} // namespace socket_ops
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_SOCKET_OPS_IPP
diff --git a/ext/asio/asio/detail/impl/socket_select_interrupter.ipp b/ext/asio/asio/detail/impl/socket_select_interrupter.ipp
new file mode 100644
index 0000000..181c543
--- /dev/null
+++ b/ext/asio/asio/detail/impl/socket_select_interrupter.ipp
@@ -0,0 +1,151 @@
+//
+// detail/impl/socket_select_interrupter.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP
+#define ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(BOOST_WINDOWS) \
+ || defined(__CYGWIN__) \
+ || defined(__SYMBIAN32__)
+
+#include <cstdlib>
+#include "asio/detail/socket_holder.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/socket_select_interrupter.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+socket_select_interrupter::socket_select_interrupter()
+{
+ asio::error_code ec;
+ socket_holder acceptor(socket_ops::socket(
+ AF_INET, SOCK_STREAM, IPPROTO_TCP, ec));
+ if (acceptor.get() == invalid_socket)
+ asio::detail::throw_error(ec, "socket_select_interrupter");
+
+ int opt = 1;
+ socket_ops::state_type acceptor_state = 0;
+ socket_ops::setsockopt(acceptor.get(), acceptor_state,
+ SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt), ec);
+
+ using namespace std; // For memset.
+ sockaddr_in4_type addr;
+ std::size_t addr_len = sizeof(addr);
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+ addr.sin_port = 0;
+ if (socket_ops::bind(acceptor.get(), (const socket_addr_type*)&addr,
+ addr_len, ec) == socket_error_retval)
+ asio::detail::throw_error(ec, "socket_select_interrupter");
+
+ if (socket_ops::getsockname(acceptor.get(), (socket_addr_type*)&addr,
+ &addr_len, ec) == socket_error_retval)
+ asio::detail::throw_error(ec, "socket_select_interrupter");
+
+ // Some broken firewalls on Windows will intermittently cause getsockname to
+ // return 0.0.0.0 when the socket is actually bound to 127.0.0.1. We
+ // explicitly specify the target address here to work around this problem.
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+
+ if (socket_ops::listen(acceptor.get(),
+ SOMAXCONN, ec) == socket_error_retval)
+ asio::detail::throw_error(ec, "socket_select_interrupter");
+
+ socket_holder client(socket_ops::socket(
+ AF_INET, SOCK_STREAM, IPPROTO_TCP, ec));
+ if (client.get() == invalid_socket)
+ asio::detail::throw_error(ec, "socket_select_interrupter");
+
+ if (socket_ops::connect(client.get(), (const socket_addr_type*)&addr,
+ addr_len, ec) == socket_error_retval)
+ asio::detail::throw_error(ec, "socket_select_interrupter");
+
+ socket_holder server(socket_ops::accept(acceptor.get(), 0, 0, ec));
+ if (server.get() == invalid_socket)
+ asio::detail::throw_error(ec, "socket_select_interrupter");
+
+ ioctl_arg_type non_blocking = 1;
+ socket_ops::state_type client_state = 0;
+ if (socket_ops::ioctl(client.get(), client_state,
+ FIONBIO, &non_blocking, ec))
+ asio::detail::throw_error(ec, "socket_select_interrupter");
+
+ opt = 1;
+ socket_ops::setsockopt(client.get(), client_state,
+ IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec);
+
+ non_blocking = 1;
+ socket_ops::state_type server_state = 0;
+ if (socket_ops::ioctl(server.get(), server_state,
+ FIONBIO, &non_blocking, ec))
+ asio::detail::throw_error(ec, "socket_select_interrupter");
+
+ opt = 1;
+ socket_ops::setsockopt(server.get(), server_state,
+ IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec);
+
+ read_descriptor_ = server.release();
+ write_descriptor_ = client.release();
+}
+
+socket_select_interrupter::~socket_select_interrupter()
+{
+ asio::error_code ec;
+ socket_ops::state_type state = socket_ops::internal_non_blocking;
+ if (read_descriptor_ != invalid_socket)
+ socket_ops::close(read_descriptor_, state, true, ec);
+ if (write_descriptor_ != invalid_socket)
+ socket_ops::close(write_descriptor_, state, true, ec);
+}
+
+void socket_select_interrupter::interrupt()
+{
+ char byte = 0;
+ socket_ops::buf b;
+ socket_ops::init_buf(b, &byte, 1);
+ asio::error_code ec;
+ socket_ops::send(write_descriptor_, &b, 1, 0, ec);
+}
+
+bool socket_select_interrupter::reset()
+{
+ char data[1024];
+ socket_ops::buf b;
+ socket_ops::init_buf(b, data, sizeof(data));
+ asio::error_code ec;
+ int bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec);
+ bool was_interrupted = (bytes_read > 0);
+ while (bytes_read == sizeof(data))
+ bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec);
+ return was_interrupted;
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(BOOST_WINDOWS)
+ // || defined(__CYGWIN__)
+ // || defined(__SYMBIAN32__)
+
+#endif // ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP
diff --git a/ext/asio/asio/detail/impl/strand_service.hpp b/ext/asio/asio/detail/impl/strand_service.hpp
new file mode 100644
index 0000000..81f438a
--- /dev/null
+++ b/ext/asio/asio/detail/impl/strand_service.hpp
@@ -0,0 +1,140 @@
+//
+// detail/impl/strand_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_STRAND_SERVICE_HPP
+#define ASIO_DETAIL_IMPL_STRAND_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/call_stack.hpp"
+#include "asio/detail/completion_handler.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+inline strand_service::strand_impl::strand_impl()
+ : operation(&strand_service::do_complete),
+ count_(0)
+{
+}
+
+struct strand_service::on_dispatch_exit
+{
+ io_service_impl* io_service_;
+ strand_impl* impl_;
+
+ ~on_dispatch_exit()
+ {
+ impl_->mutex_.lock();
+ bool more_handlers = (--impl_->count_ > 0);
+ impl_->mutex_.unlock();
+
+ if (more_handlers)
+ io_service_->post_immediate_completion(impl_);
+ }
+};
+
+inline void strand_service::destroy(strand_service::implementation_type& impl)
+{
+ impl = 0;
+}
+
+template <typename Handler>
+void strand_service::dispatch(strand_service::implementation_type& impl,
+ Handler handler)
+{
+ // If we are already in the strand then the handler can run immediately.
+ if (call_stack<strand_impl>::contains(impl))
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler);
+ return;
+ }
+
+ // Allocate and construct an operation to wrap the handler.
+ typedef completion_handler<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(handler);
+
+ // If we are running inside the io_service, and no other handler is queued
+ // or running, then the handler can run immediately.
+ bool can_dispatch = call_stack<io_service_impl>::contains(&io_service_);
+ impl->mutex_.lock();
+ bool first = (++impl->count_ == 1);
+ if (can_dispatch && first)
+ {
+ // Immediate invocation is allowed.
+ impl->mutex_.unlock();
+
+ // Memory must be releaesed before any upcall is made.
+ p.reset();
+
+ // Indicate that this strand is executing on the current thread.
+ call_stack<strand_impl>::context ctx(impl);
+
+ // Ensure the next handler, if any, is scheduled on block exit.
+ on_dispatch_exit on_exit = { &io_service_, impl };
+ (void)on_exit;
+
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler);
+ return;
+ }
+
+ // Immediate invocation is not allowed, so enqueue for later.
+ impl->queue_.push(p.p);
+ impl->mutex_.unlock();
+ p.v = p.p = 0;
+
+ // The first handler to be enqueued is responsible for scheduling the
+ // strand.
+ if (first)
+ io_service_.post_immediate_completion(impl);
+}
+
+// Request the io_service to invoke the given handler and return immediately.
+template <typename Handler>
+void strand_service::post(strand_service::implementation_type& impl,
+ Handler handler)
+{
+ // Allocate and construct an operation to wrap the handler.
+ typedef completion_handler<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(handler);
+
+ // Add the handler to the queue.
+ impl->mutex_.lock();
+ bool first = (++impl->count_ == 1);
+ impl->queue_.push(p.p);
+ impl->mutex_.unlock();
+ p.v = p.p = 0;
+
+ // The first handler to be enqueue is responsible for scheduling the strand.
+ if (first)
+ io_service_.post_immediate_completion(impl);
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_IMPL_STRAND_SERVICE_HPP
diff --git a/ext/asio/asio/detail/impl/strand_service.ipp b/ext/asio/asio/detail/impl/strand_service.ipp
new file mode 100644
index 0000000..84b806a
--- /dev/null
+++ b/ext/asio/asio/detail/impl/strand_service.ipp
@@ -0,0 +1,106 @@
+//
+// detail/impl/strand_service.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP
+#define ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include "asio/detail/call_stack.hpp"
+#include "asio/detail/strand_service.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+struct strand_service::on_do_complete_exit
+{
+ io_service_impl* owner_;
+ strand_impl* impl_;
+
+ ~on_do_complete_exit()
+ {
+ impl_->mutex_.lock();
+ bool more_handlers = (--impl_->count_ > 0);
+ impl_->mutex_.unlock();
+
+ if (more_handlers)
+ owner_->post_immediate_completion(impl_);
+ }
+};
+
+strand_service::strand_service(asio::io_service& io_service)
+ : asio::detail::service_base<strand_service>(io_service),
+ io_service_(asio::use_service<io_service_impl>(io_service)),
+ mutex_(),
+ salt_(0)
+{
+}
+
+void strand_service::shutdown_service()
+{
+ op_queue<operation> ops;
+
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ for (std::size_t i = 0; i < num_implementations; ++i)
+ if (strand_impl* impl = implementations_[i].get())
+ ops.push(impl->queue_);
+}
+
+void strand_service::construct(strand_service::implementation_type& impl)
+{
+ std::size_t salt = salt_++;
+ std::size_t index = reinterpret_cast<std::size_t>(&impl);
+ index += (reinterpret_cast<std::size_t>(&impl) >> 3);
+ index ^= salt + 0x9e3779b9 + (index << 6) + (index >> 2);
+ index = index % num_implementations;
+
+ asio::detail::mutex::scoped_lock lock(mutex_);
+
+ if (!implementations_[index])
+ implementations_[index].reset(new strand_impl);
+ impl = implementations_[index].get();
+}
+
+void strand_service::do_complete(io_service_impl* owner, operation* base,
+ asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
+{
+ if (owner)
+ {
+ strand_impl* impl = static_cast<strand_impl*>(base);
+
+ // Get the next handler to be executed.
+ impl->mutex_.lock();
+ operation* o = impl->queue_.front();
+ impl->queue_.pop();
+ impl->mutex_.unlock();
+
+ // Indicate that this strand is executing on the current thread.
+ call_stack<strand_impl>::context ctx(impl);
+
+ // Ensure the next handler, if any, is scheduled on block exit.
+ on_do_complete_exit on_exit = { owner, impl };
+ (void)on_exit;
+
+ o->complete(*owner);
+ }
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP
diff --git a/ext/asio/asio/detail/impl/task_io_service.hpp b/ext/asio/asio/detail/impl/task_io_service.hpp
new file mode 100644
index 0000000..20ffd61
--- /dev/null
+++ b/ext/asio/asio/detail/impl/task_io_service.hpp
@@ -0,0 +1,60 @@
+//
+// detail/impl/task_io_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_TASK_IO_SERVICE_HPP
+#define ASIO_DETAIL_IMPL_TASK_IO_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/call_stack.hpp"
+#include "asio/detail/completion_handler.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Handler>
+void task_io_service::dispatch(Handler handler)
+{
+ if (call_stack<task_io_service>::contains(this))
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler);
+ }
+ else
+ post(handler);
+}
+
+template <typename Handler>
+void task_io_service::post(Handler handler)
+{
+ // Allocate and construct an operation to wrap the handler.
+ typedef completion_handler<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(handler);
+
+ post_immediate_completion(p.p);
+ p.v = p.p = 0;
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_IMPL_TASK_IO_SERVICE_HPP
diff --git a/ext/asio/asio/detail/impl/task_io_service.ipp b/ext/asio/asio/detail/impl/task_io_service.ipp
new file mode 100644
index 0000000..12a22be
--- /dev/null
+++ b/ext/asio/asio/detail/impl/task_io_service.ipp
@@ -0,0 +1,354 @@
+//
+// detail/impl/task_io_service.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_TASK_IO_SERVICE_IPP
+#define ASIO_DETAIL_IMPL_TASK_IO_SERVICE_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if !defined(ASIO_HAS_IOCP)
+
+#include <boost/limits.hpp>
+#include "asio/detail/call_stack.hpp"
+#include "asio/detail/event.hpp"
+#include "asio/detail/reactor.hpp"
+#include "asio/detail/task_io_service.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+struct task_io_service::task_cleanup
+{
+ ~task_cleanup()
+ {
+ // Enqueue the completed operations and reinsert the task at the end of
+ // the operation queue.
+ lock_->lock();
+ task_io_service_->task_interrupted_ = true;
+ task_io_service_->op_queue_.push(*ops_);
+ task_io_service_->op_queue_.push(&task_io_service_->task_operation_);
+ }
+
+ task_io_service* task_io_service_;
+ mutex::scoped_lock* lock_;
+ op_queue<operation>* ops_;
+};
+
+struct task_io_service::work_finished_on_block_exit
+{
+ ~work_finished_on_block_exit()
+ {
+ task_io_service_->work_finished();
+ }
+
+ task_io_service* task_io_service_;
+};
+
+struct task_io_service::idle_thread_info
+{
+ event wakeup_event;
+ idle_thread_info* next;
+};
+
+task_io_service::task_io_service(asio::io_service& io_service)
+ : asio::detail::service_base<task_io_service>(io_service),
+ mutex_(),
+ task_(0),
+ task_interrupted_(true),
+ outstanding_work_(0),
+ stopped_(false),
+ shutdown_(false),
+ first_idle_thread_(0)
+{
+}
+
+void task_io_service::init(std::size_t /*concurrency_hint*/)
+{
+}
+
+void task_io_service::shutdown_service()
+{
+ mutex::scoped_lock lock(mutex_);
+ shutdown_ = true;
+ lock.unlock();
+
+ // Destroy handler objects.
+ while (!op_queue_.empty())
+ {
+ operation* o = op_queue_.front();
+ op_queue_.pop();
+ if (o != &task_operation_)
+ o->destroy();
+ }
+
+ // Reset to initial state.
+ task_ = 0;
+}
+
+void task_io_service::init_task()
+{
+ mutex::scoped_lock lock(mutex_);
+ if (!shutdown_ && !task_)
+ {
+ task_ = &use_service<reactor>(this->get_io_service());
+ op_queue_.push(&task_operation_);
+ wake_one_thread_and_unlock(lock);
+ }
+}
+
+std::size_t task_io_service::run(asio::error_code& ec)
+{
+ ec = asio::error_code();
+ if (outstanding_work_ == 0)
+ {
+ stop();
+ return 0;
+ }
+
+ call_stack<task_io_service>::context ctx(this);
+
+ idle_thread_info this_idle_thread;
+ this_idle_thread.next = 0;
+
+ mutex::scoped_lock lock(mutex_);
+
+ std::size_t n = 0;
+ for (; do_one(lock, &this_idle_thread); lock.lock())
+ if (n != (std::numeric_limits<std::size_t>::max)())
+ ++n;
+ return n;
+}
+
+std::size_t task_io_service::run_one(asio::error_code& ec)
+{
+ ec = asio::error_code();
+ if (outstanding_work_ == 0)
+ {
+ stop();
+ return 0;
+ }
+
+ call_stack<task_io_service>::context ctx(this);
+
+ idle_thread_info this_idle_thread;
+ this_idle_thread.next = 0;
+
+ mutex::scoped_lock lock(mutex_);
+
+ return do_one(lock, &this_idle_thread);
+}
+
+std::size_t task_io_service::poll(asio::error_code& ec)
+{
+ if (outstanding_work_ == 0)
+ {
+ stop();
+ ec = asio::error_code();
+ return 0;
+ }
+
+ call_stack<task_io_service>::context ctx(this);
+
+ mutex::scoped_lock lock(mutex_);
+
+ std::size_t n = 0;
+ for (; do_one(lock, 0); lock.lock())
+ if (n != (std::numeric_limits<std::size_t>::max)())
+ ++n;
+ return n;
+}
+
+std::size_t task_io_service::poll_one(asio::error_code& ec)
+{
+ ec = asio::error_code();
+ if (outstanding_work_ == 0)
+ {
+ stop();
+ return 0;
+ }
+
+ call_stack<task_io_service>::context ctx(this);
+
+ mutex::scoped_lock lock(mutex_);
+
+ return do_one(lock, 0);
+}
+
+void task_io_service::stop()
+{
+ mutex::scoped_lock lock(mutex_);
+ stop_all_threads(lock);
+}
+
+void task_io_service::reset()
+{
+ mutex::scoped_lock lock(mutex_);
+ stopped_ = false;
+}
+
+void task_io_service::post_immediate_completion(task_io_service::operation* op)
+{
+ work_started();
+ post_deferred_completion(op);
+}
+
+void task_io_service::post_deferred_completion(task_io_service::operation* op)
+{
+ mutex::scoped_lock lock(mutex_);
+ op_queue_.push(op);
+ wake_one_thread_and_unlock(lock);
+}
+
+void task_io_service::post_deferred_completions(
+ op_queue<task_io_service::operation>& ops)
+{
+ if (!ops.empty())
+ {
+ mutex::scoped_lock lock(mutex_);
+ op_queue_.push(ops);
+ wake_one_thread_and_unlock(lock);
+ }
+}
+
+std::size_t task_io_service::do_one(mutex::scoped_lock& lock,
+ task_io_service::idle_thread_info* this_idle_thread)
+{
+ bool polling = !this_idle_thread;
+ bool task_has_run = false;
+ while (!stopped_)
+ {
+ if (!op_queue_.empty())
+ {
+ // Prepare to execute first handler from queue.
+ operation* o = op_queue_.front();
+ op_queue_.pop();
+ bool more_handlers = (!op_queue_.empty());
+
+ if (o == &task_operation_)
+ {
+ task_interrupted_ = more_handlers || polling;
+
+ // If the task has already run and we're polling then we're done.
+ if (task_has_run && polling)
+ {
+ task_interrupted_ = true;
+ op_queue_.push(&task_operation_);
+ return 0;
+ }
+ task_has_run = true;
+
+ if (!more_handlers || !wake_one_idle_thread_and_unlock(lock))
+ lock.unlock();
+
+ op_queue<operation> completed_ops;
+ task_cleanup c = { this, &lock, &completed_ops };
+ (void)c;
+
+ // Run the task. May throw an exception. Only block if the operation
+ // queue is empty and we're not polling, otherwise we want to return
+ // as soon as possible.
+ task_->run(!more_handlers && !polling, completed_ops);
+ }
+ else
+ {
+ if (more_handlers)
+ wake_one_thread_and_unlock(lock);
+ else
+ lock.unlock();
+
+ // Ensure the count of outstanding work is decremented on block exit.
+ work_finished_on_block_exit on_exit = { this };
+ (void)on_exit;
+
+ // Complete the operation. May throw an exception.
+ o->complete(*this); // deletes the operation object
+
+ return 1;
+ }
+ }
+ else if (this_idle_thread)
+ {
+ // Nothing to run right now, so just wait for work to do.
+ this_idle_thread->next = first_idle_thread_;
+ first_idle_thread_ = this_idle_thread;
+ this_idle_thread->wakeup_event.clear(lock);
+ this_idle_thread->wakeup_event.wait(lock);
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+void task_io_service::stop_all_threads(
+ mutex::scoped_lock& lock)
+{
+ stopped_ = true;
+
+ while (first_idle_thread_)
+ {
+ idle_thread_info* idle_thread = first_idle_thread_;
+ first_idle_thread_ = idle_thread->next;
+ idle_thread->next = 0;
+ idle_thread->wakeup_event.signal(lock);
+ }
+
+ if (!task_interrupted_ && task_)
+ {
+ task_interrupted_ = true;
+ task_->interrupt();
+ }
+}
+
+bool task_io_service::wake_one_idle_thread_and_unlock(
+ mutex::scoped_lock& lock)
+{
+ if (first_idle_thread_)
+ {
+ idle_thread_info* idle_thread = first_idle_thread_;
+ first_idle_thread_ = idle_thread->next;
+ idle_thread->next = 0;
+ idle_thread->wakeup_event.signal_and_unlock(lock);
+ return true;
+ }
+ return false;
+}
+
+void task_io_service::wake_one_thread_and_unlock(
+ mutex::scoped_lock& lock)
+{
+ if (!wake_one_idle_thread_and_unlock(lock))
+ {
+ if (!task_interrupted_ && task_)
+ {
+ task_interrupted_ = true;
+ task_->interrupt();
+ }
+ lock.unlock();
+ }
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // !defined(ASIO_HAS_IOCP)
+
+#endif // ASIO_DETAIL_IMPL_TASK_IO_SERVICE_IPP
diff --git a/ext/asio/asio/detail/impl/throw_error.ipp b/ext/asio/asio/detail/impl/throw_error.ipp
new file mode 100644
index 0000000..a04fbf4
--- /dev/null
+++ b/ext/asio/asio/detail/impl/throw_error.ipp
@@ -0,0 +1,47 @@
+//
+// detail/impl/throw_error.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_THROW_ERROR_IPP
+#define ASIO_DETAIL_IMPL_THROW_ERROR_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <boost/throw_exception.hpp>
+#include "asio/detail/throw_error.hpp"
+#include "asio/system_error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+ASIO_DECL
+void do_throw_error(const asio::error_code& err)
+{
+ asio::system_error e(err);
+ boost::throw_exception(e);
+}
+
+ASIO_DECL
+void do_throw_error(const asio::error_code& err, const char* location)
+{
+ asio::system_error e(err, location);
+ boost::throw_exception(e);
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_IMPL_THROW_ERROR_IPP
diff --git a/ext/asio/asio/detail/impl/timer_queue.ipp b/ext/asio/asio/detail/impl/timer_queue.ipp
new file mode 100644
index 0000000..63fbd7e
--- /dev/null
+++ b/ext/asio/asio/detail/impl/timer_queue.ipp
@@ -0,0 +1,85 @@
+//
+// detail/impl/timer_queue.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_TIMER_QUEUE_IPP
+#define ASIO_DETAIL_IMPL_TIMER_QUEUE_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if !defined(ASIO_HEADER_ONLY)
+
+#include "asio/detail/timer_queue.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+timer_queue<time_traits<boost::posix_time::ptime> >::timer_queue()
+{
+}
+
+timer_queue<time_traits<boost::posix_time::ptime> >::~timer_queue()
+{
+}
+
+bool timer_queue<time_traits<boost::posix_time::ptime> >::enqueue_timer(
+ const time_type& time, per_timer_data& timer, timer_op* op)
+{
+ return impl_.enqueue_timer(time, timer, op);
+}
+
+bool timer_queue<time_traits<boost::posix_time::ptime> >::empty() const
+{
+ return impl_.empty();
+}
+
+long timer_queue<time_traits<boost::posix_time::ptime> >::wait_duration_msec(
+ long max_duration) const
+{
+ return impl_.wait_duration_msec(max_duration);
+}
+
+long timer_queue<time_traits<boost::posix_time::ptime> >::wait_duration_usec(
+ long max_duration) const
+{
+ return impl_.wait_duration_usec(max_duration);
+}
+
+void timer_queue<time_traits<boost::posix_time::ptime> >::get_ready_timers(
+ op_queue<operation>& ops)
+{
+ impl_.get_ready_timers(ops);
+}
+
+void timer_queue<time_traits<boost::posix_time::ptime> >::get_all_timers(
+ op_queue<operation>& ops)
+{
+ impl_.get_all_timers(ops);
+}
+
+std::size_t timer_queue<time_traits<boost::posix_time::ptime> >::cancel_timer(
+ per_timer_data& timer, op_queue<operation>& ops)
+{
+ return impl_.cancel_timer(timer, ops);
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // !defined(ASIO_HEADER_ONLY)
+
+#endif // ASIO_DETAIL_IMPL_TIMER_QUEUE_IPP
diff --git a/ext/asio/asio/detail/impl/timer_queue_set.ipp b/ext/asio/asio/detail/impl/timer_queue_set.ipp
new file mode 100644
index 0000000..81ab5a9
--- /dev/null
+++ b/ext/asio/asio/detail/impl/timer_queue_set.ipp
@@ -0,0 +1,101 @@
+//
+// detail/impl/timer_queue_set.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_TIMER_QUEUE_SET_IPP
+#define ASIO_DETAIL_IMPL_TIMER_QUEUE_SET_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include "asio/detail/timer_queue_set.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+timer_queue_set::timer_queue_set()
+ : first_(0)
+{
+}
+
+void timer_queue_set::insert(timer_queue_base* q)
+{
+ q->next_ = first_;
+ first_ = q;
+}
+
+void timer_queue_set::erase(timer_queue_base* q)
+{
+ if (first_)
+ {
+ if (q == first_)
+ {
+ first_ = q->next_;
+ q->next_ = 0;
+ return;
+ }
+
+ for (timer_queue_base* p = first_; p->next_; p = p->next_)
+ {
+ if (p->next_ == q)
+ {
+ p->next_ = q->next_;
+ q->next_ = 0;
+ return;
+ }
+ }
+ }
+}
+
+bool timer_queue_set::all_empty() const
+{
+ for (timer_queue_base* p = first_; p; p = p->next_)
+ if (!p->empty())
+ return false;
+ return true;
+}
+
+long timer_queue_set::wait_duration_msec(long max_duration) const
+{
+ long min_duration = max_duration;
+ for (timer_queue_base* p = first_; p; p = p->next_)
+ min_duration = p->wait_duration_msec(min_duration);
+ return min_duration;
+}
+
+long timer_queue_set::wait_duration_usec(long max_duration) const
+{
+ long min_duration = max_duration;
+ for (timer_queue_base* p = first_; p; p = p->next_)
+ min_duration = p->wait_duration_usec(min_duration);
+ return min_duration;
+}
+
+void timer_queue_set::get_ready_timers(op_queue<operation>& ops)
+{
+ for (timer_queue_base* p = first_; p; p = p->next_)
+ p->get_ready_timers(ops);
+}
+
+void timer_queue_set::get_all_timers(op_queue<operation>& ops)
+{
+ for (timer_queue_base* p = first_; p; p = p->next_)
+ p->get_all_timers(ops);
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_IMPL_TIMER_QUEUE_SET_IPP
diff --git a/ext/asio/asio/detail/impl/win_event.ipp b/ext/asio/asio/detail/impl/win_event.ipp
new file mode 100644
index 0000000..2b72f6e
--- /dev/null
+++ b/ext/asio/asio/detail/impl/win_event.ipp
@@ -0,0 +1,50 @@
+//
+// detail/win_event.ipp
+// ~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_WIN_EVENT_IPP
+#define ASIO_DETAIL_IMPL_WIN_EVENT_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(BOOST_WINDOWS)
+
+#include "asio/detail/throw_error.hpp"
+#include "asio/detail/win_event.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+win_event::win_event()
+ : event_(::CreateEvent(0, true, false, 0))
+{
+ if (!event_)
+ {
+ DWORD last_error = ::GetLastError();
+ asio::error_code ec(last_error,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "event");
+ }
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(BOOST_WINDOWS)
+
+#endif // ASIO_DETAIL_IMPL_WIN_EVENT_IPP
diff --git a/ext/asio/asio/detail/impl/win_iocp_handle_service.ipp b/ext/asio/asio/detail/impl/win_iocp_handle_service.ipp
new file mode 100644
index 0000000..3b30b1d
--- /dev/null
+++ b/ext/asio/asio/detail/impl/win_iocp_handle_service.ipp
@@ -0,0 +1,452 @@
+//
+// detail/impl/win_iocp_handle_service.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2008 Rep Invariant Systems, Inc. (info at repinvariant.com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP
+#define ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+
+#include "asio/detail/win_iocp_handle_service.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+class win_iocp_handle_service::overlapped_wrapper
+ : public OVERLAPPED
+{
+public:
+ explicit overlapped_wrapper(asio::error_code& ec)
+ {
+ Internal = 0;
+ InternalHigh = 0;
+ Offset = 0;
+ OffsetHigh = 0;
+
+ // Create a non-signalled manual-reset event, for GetOverlappedResult.
+ hEvent = ::CreateEvent(0, TRUE, FALSE, 0);
+ if (hEvent)
+ {
+ // As documented in GetQueuedCompletionStatus, setting the low order
+ // bit of this event prevents our synchronous writes from being treated
+ // as completion port events.
+ *reinterpret_cast<DWORD_PTR*>(&hEvent) |= 1;
+ }
+ else
+ {
+ DWORD last_error = ::GetLastError();
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ }
+ }
+
+ ~overlapped_wrapper()
+ {
+ if (hEvent)
+ {
+ ::CloseHandle(hEvent);
+ }
+ }
+};
+
+win_iocp_handle_service::win_iocp_handle_service(
+ asio::io_service& io_service)
+ : iocp_service_(asio::use_service<win_iocp_io_service>(io_service)),
+ mutex_(),
+ impl_list_(0)
+{
+}
+
+void win_iocp_handle_service::shutdown_service()
+{
+ // Close all implementations, causing all operations to complete.
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ implementation_type* impl = impl_list_;
+ while (impl)
+ {
+ close_for_destruction(*impl);
+ impl = impl->next_;
+ }
+}
+
+void win_iocp_handle_service::construct(
+ win_iocp_handle_service::implementation_type& impl)
+{
+ impl.handle_ = INVALID_HANDLE_VALUE;
+ impl.safe_cancellation_thread_id_ = 0;
+
+ // Insert implementation into linked list of all implementations.
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ impl.next_ = impl_list_;
+ impl.prev_ = 0;
+ if (impl_list_)
+ impl_list_->prev_ = &impl;
+ impl_list_ = &impl;
+}
+
+void win_iocp_handle_service::destroy(
+ win_iocp_handle_service::implementation_type& impl)
+{
+ close_for_destruction(impl);
+
+ // Remove implementation from linked list of all implementations.
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ if (impl_list_ == &impl)
+ impl_list_ = impl.next_;
+ if (impl.prev_)
+ impl.prev_->next_ = impl.next_;
+ if (impl.next_)
+ impl.next_->prev_= impl.prev_;
+ impl.next_ = 0;
+ impl.prev_ = 0;
+}
+
+asio::error_code win_iocp_handle_service::assign(
+ win_iocp_handle_service::implementation_type& impl,
+ const native_type& native_handle, asio::error_code& ec)
+{
+ if (is_open(impl))
+ {
+ ec = asio::error::already_open;
+ return ec;
+ }
+
+ if (iocp_service_.register_handle(native_handle, ec))
+ return ec;
+
+ impl.handle_ = native_handle;
+ ec = asio::error_code();
+ return ec;
+}
+
+asio::error_code win_iocp_handle_service::close(
+ win_iocp_handle_service::implementation_type& impl,
+ asio::error_code& ec)
+{
+ if (is_open(impl))
+ {
+ if (!::CloseHandle(impl.handle_))
+ {
+ DWORD last_error = ::GetLastError();
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ return ec;
+ }
+
+ impl.handle_ = INVALID_HANDLE_VALUE;
+ impl.safe_cancellation_thread_id_ = 0;
+ }
+
+ ec = asio::error_code();
+ return ec;
+}
+
+asio::error_code win_iocp_handle_service::cancel(
+ win_iocp_handle_service::implementation_type& impl,
+ asio::error_code& ec)
+{
+ if (!is_open(impl))
+ {
+ ec = asio::error::bad_descriptor;
+ }
+ else if (FARPROC cancel_io_ex_ptr = ::GetProcAddress(
+ ::GetModuleHandleA("KERNEL32"), "CancelIoEx"))
+ {
+ // The version of Windows supports cancellation from any thread.
+ typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED);
+ cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr;
+ if (!cancel_io_ex(impl.handle_, 0))
+ {
+ DWORD last_error = ::GetLastError();
+ if (last_error == ERROR_NOT_FOUND)
+ {
+ // ERROR_NOT_FOUND means that there were no operations to be
+ // cancelled. We swallow this error to match the behaviour on other
+ // platforms.
+ ec = asio::error_code();
+ }
+ else
+ {
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ }
+ }
+ else
+ {
+ ec = asio::error_code();
+ }
+ }
+ else if (impl.safe_cancellation_thread_id_ == 0)
+ {
+ // No operations have been started, so there's nothing to cancel.
+ ec = asio::error_code();
+ }
+ else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId())
+ {
+ // Asynchronous operations have been started from the current thread only,
+ // so it is safe to try to cancel them using CancelIo.
+ if (!::CancelIo(impl.handle_))
+ {
+ DWORD last_error = ::GetLastError();
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ }
+ else
+ {
+ ec = asio::error_code();
+ }
+ }
+ else
+ {
+ // Asynchronous operations have been started from more than one thread,
+ // so cancellation is not safe.
+ ec = asio::error::operation_not_supported;
+ }
+
+ return ec;
+}
+
+size_t win_iocp_handle_service::do_write(
+ win_iocp_handle_service::implementation_type& impl, boost::uint64_t offset,
+ const asio::const_buffer& buffer, asio::error_code& ec)
+{
+ if (!is_open(impl))
+ {
+ ec = asio::error::bad_descriptor;
+ return 0;
+ }
+
+ // A request to write 0 bytes on a handle is a no-op.
+ if (asio::buffer_size(buffer) == 0)
+ {
+ ec = asio::error_code();
+ return 0;
+ }
+
+ overlapped_wrapper overlapped(ec);
+ if (ec)
+ {
+ return 0;
+ }
+
+ // Write the data.
+ overlapped.Offset = offset & 0xFFFFFFFF;
+ overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
+ BOOL ok = ::WriteFile(impl.handle_,
+ asio::buffer_cast<LPCVOID>(buffer),
+ static_cast<DWORD>(asio::buffer_size(buffer)), 0, &overlapped);
+ if (!ok)
+ {
+ DWORD last_error = ::GetLastError();
+ if (last_error != ERROR_IO_PENDING)
+ {
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ return 0;
+ }
+ }
+
+ // Wait for the operation to complete.
+ DWORD bytes_transferred = 0;
+ ok = ::GetOverlappedResult(impl.handle_,
+ &overlapped, &bytes_transferred, TRUE);
+ if (!ok)
+ {
+ DWORD last_error = ::GetLastError();
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ return 0;
+ }
+
+ ec = asio::error_code();
+ return bytes_transferred;
+}
+
+void win_iocp_handle_service::start_write_op(
+ win_iocp_handle_service::implementation_type& impl, boost::uint64_t offset,
+ const asio::const_buffer& buffer, operation* op)
+{
+ update_cancellation_thread_id(impl);
+ iocp_service_.work_started();
+
+ if (!is_open(impl))
+ {
+ iocp_service_.on_completion(op, asio::error::bad_descriptor);
+ }
+ else if (asio::buffer_size(buffer) == 0)
+ {
+ // A request to write 0 bytes on a handle is a no-op.
+ iocp_service_.on_completion(op);
+ }
+ else
+ {
+ DWORD bytes_transferred = 0;
+ op->Offset = offset & 0xFFFFFFFF;
+ op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
+ BOOL ok = ::WriteFile(impl.handle_,
+ asio::buffer_cast<LPCVOID>(buffer),
+ static_cast<DWORD>(asio::buffer_size(buffer)),
+ &bytes_transferred, op);
+ DWORD last_error = ::GetLastError();
+ if (!ok && last_error != ERROR_IO_PENDING
+ && last_error != ERROR_MORE_DATA)
+ {
+ iocp_service_.on_completion(op, last_error, bytes_transferred);
+ }
+ else
+ {
+ iocp_service_.on_pending(op);
+ }
+ }
+}
+
+size_t win_iocp_handle_service::do_read(
+ win_iocp_handle_service::implementation_type& impl, boost::uint64_t offset,
+ const asio::mutable_buffer& buffer, asio::error_code& ec)
+{
+ if (!is_open(impl))
+ {
+ ec = asio::error::bad_descriptor;
+ return 0;
+ }
+
+ // A request to read 0 bytes on a stream handle is a no-op.
+ if (asio::buffer_size(buffer) == 0)
+ {
+ ec = asio::error_code();
+ return 0;
+ }
+
+ overlapped_wrapper overlapped(ec);
+ if (ec)
+ {
+ return 0;
+ }
+
+ // Read some data.
+ overlapped.Offset = offset & 0xFFFFFFFF;
+ overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
+ BOOL ok = ::ReadFile(impl.handle_,
+ asio::buffer_cast<LPVOID>(buffer),
+ static_cast<DWORD>(asio::buffer_size(buffer)), 0, &overlapped);
+ if (!ok)
+ {
+ DWORD last_error = ::GetLastError();
+ if (last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA)
+ {
+ if (last_error == ERROR_HANDLE_EOF)
+ {
+ ec = asio::error::eof;
+ }
+ else
+ {
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ }
+ return 0;
+ }
+ }
+
+ // Wait for the operation to complete.
+ DWORD bytes_transferred = 0;
+ ok = ::GetOverlappedResult(impl.handle_,
+ &overlapped, &bytes_transferred, TRUE);
+ if (!ok)
+ {
+ DWORD last_error = ::GetLastError();
+ if (last_error == ERROR_HANDLE_EOF)
+ {
+ ec = asio::error::eof;
+ }
+ else
+ {
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ }
+ return 0;
+ }
+
+ ec = asio::error_code();
+ return bytes_transferred;
+}
+
+void win_iocp_handle_service::start_read_op(
+ win_iocp_handle_service::implementation_type& impl, boost::uint64_t offset,
+ const asio::mutable_buffer& buffer, operation* op)
+{
+ update_cancellation_thread_id(impl);
+ iocp_service_.work_started();
+
+ if (!is_open(impl))
+ {
+ iocp_service_.on_completion(op, asio::error::bad_descriptor);
+ }
+ else if (asio::buffer_size(buffer) == 0)
+ {
+ // A request to read 0 bytes on a handle is a no-op.
+ iocp_service_.on_completion(op);
+ }
+ else
+ {
+ DWORD bytes_transferred = 0;
+ op->Offset = offset & 0xFFFFFFFF;
+ op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
+ BOOL ok = ::ReadFile(impl.handle_,
+ asio::buffer_cast<LPVOID>(buffer),
+ static_cast<DWORD>(asio::buffer_size(buffer)),
+ &bytes_transferred, op);
+ DWORD last_error = ::GetLastError();
+ if (!ok && last_error != ERROR_IO_PENDING
+ && last_error != ERROR_MORE_DATA)
+ {
+ iocp_service_.on_completion(op, last_error, bytes_transferred);
+ }
+ else
+ {
+ iocp_service_.on_pending(op);
+ }
+ }
+}
+
+void win_iocp_handle_service::update_cancellation_thread_id(
+ win_iocp_handle_service::implementation_type& impl)
+{
+ if (impl.safe_cancellation_thread_id_ == 0)
+ impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
+ else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId())
+ impl.safe_cancellation_thread_id_ = ~DWORD(0);
+}
+
+void win_iocp_handle_service::close_for_destruction(implementation_type& impl)
+{
+ if (is_open(impl))
+ {
+ ::CloseHandle(impl.handle_);
+ impl.handle_ = INVALID_HANDLE_VALUE;
+ impl.safe_cancellation_thread_id_ = 0;
+ }
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_IOCP)
+
+#endif // ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP
diff --git a/ext/asio/asio/detail/impl/win_iocp_io_service.hpp b/ext/asio/asio/detail/impl/win_iocp_io_service.hpp
new file mode 100644
index 0000000..ab23547
--- /dev/null
+++ b/ext/asio/asio/detail/impl/win_iocp_io_service.hpp
@@ -0,0 +1,115 @@
+//
+// detail/impl/win_iocp_io_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_HPP
+#define ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+
+#include "asio/detail/call_stack.hpp"
+#include "asio/detail/completion_handler.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Handler>
+void win_iocp_io_service::dispatch(Handler handler)
+{
+ if (call_stack<win_iocp_io_service>::contains(this))
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler);
+ }
+ else
+ post(handler);
+}
+
+template <typename Handler>
+void win_iocp_io_service::post(Handler handler)
+{
+ // Allocate and construct an operation to wrap the handler.
+ typedef completion_handler<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(handler);
+
+ post_immediate_completion(p.p);
+ p.v = p.p = 0;
+}
+
+template <typename Time_Traits>
+void win_iocp_io_service::add_timer_queue(
+ timer_queue<Time_Traits>& queue)
+{
+ do_add_timer_queue(queue);
+}
+
+template <typename Time_Traits>
+void win_iocp_io_service::remove_timer_queue(
+ timer_queue<Time_Traits>& queue)
+{
+ do_remove_timer_queue(queue);
+}
+
+template <typename Time_Traits>
+void win_iocp_io_service::schedule_timer(timer_queue<Time_Traits>& queue,
+ const typename Time_Traits::time_type& time,
+ typename timer_queue<Time_Traits>::per_timer_data& timer, timer_op* op)
+{
+ // If the service has been shut down we silently discard the timer.
+ if (::InterlockedExchangeAdd(&shutdown_, 0) != 0)
+ {
+ post_immediate_completion(op);
+ return;
+ }
+
+ mutex::scoped_lock lock(dispatch_mutex_);
+
+ bool earliest = queue.enqueue_timer(time, timer, op);
+ work_started();
+ if (earliest)
+ update_timeout();
+}
+
+template <typename Time_Traits>
+std::size_t win_iocp_io_service::cancel_timer(timer_queue<Time_Traits>& queue,
+ typename timer_queue<Time_Traits>::per_timer_data& timer)
+{
+ // If the service has been shut down we silently ignore the cancellation.
+ if (::InterlockedExchangeAdd(&shutdown_, 0) != 0)
+ return 0;
+
+ mutex::scoped_lock lock(dispatch_mutex_);
+ op_queue<win_iocp_operation> ops;
+ std::size_t n = queue.cancel_timer(timer, ops);
+ post_deferred_completions(ops);
+ return n;
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_IOCP)
+
+#endif // ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_HPP
diff --git a/ext/asio/asio/detail/impl/win_iocp_io_service.ipp b/ext/asio/asio/detail/impl/win_iocp_io_service.ipp
new file mode 100644
index 0000000..890e32f
--- /dev/null
+++ b/ext/asio/asio/detail/impl/win_iocp_io_service.ipp
@@ -0,0 +1,496 @@
+//
+// detail/impl/win_iocp_io_service.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_IPP
+#define ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+
+#include <boost/limits.hpp>
+#include "asio/error.hpp"
+#include "asio/io_service.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/detail/win_iocp_io_service.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+struct win_iocp_io_service::work_finished_on_block_exit
+{
+ ~work_finished_on_block_exit()
+ {
+ io_service_->work_finished();
+ }
+
+ win_iocp_io_service* io_service_;
+};
+
+struct win_iocp_io_service::timer_thread_function
+{
+ void operator()()
+ {
+ while (::InterlockedExchangeAdd(&io_service_->shutdown_, 0) == 0)
+ {
+ if (::WaitForSingleObject(io_service_->waitable_timer_.handle,
+ INFINITE) == WAIT_OBJECT_0)
+ {
+ ::InterlockedExchange(&io_service_->dispatch_required_, 1);
+ ::PostQueuedCompletionStatus(io_service_->iocp_.handle,
+ 0, wake_for_dispatch, 0);
+ }
+ }
+ }
+
+ win_iocp_io_service* io_service_;
+};
+
+win_iocp_io_service::win_iocp_io_service(asio::io_service& io_service)
+ : asio::detail::service_base<win_iocp_io_service>(io_service),
+ iocp_(),
+ outstanding_work_(0),
+ stopped_(0),
+ shutdown_(0),
+ dispatch_required_(0)
+{
+}
+
+void win_iocp_io_service::init(size_t concurrency_hint)
+{
+ iocp_.handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0,
+ static_cast<DWORD>((std::min<size_t>)(concurrency_hint, DWORD(~0))));
+ if (!iocp_.handle)
+ {
+ DWORD last_error = ::GetLastError();
+ asio::error_code ec(last_error,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "iocp");
+ }
+}
+
+void win_iocp_io_service::shutdown_service()
+{
+ ::InterlockedExchange(&shutdown_, 1);
+
+ if (timer_thread_)
+ {
+ LARGE_INTEGER timeout;
+ timeout.QuadPart = 1;
+ ::SetWaitableTimer(waitable_timer_.handle, &timeout, 1, 0, 0, FALSE);
+ }
+
+ while (::InterlockedExchangeAdd(&outstanding_work_, 0) > 0)
+ {
+ op_queue<win_iocp_operation> ops;
+ timer_queues_.get_all_timers(ops);
+ ops.push(completed_ops_);
+ if (!ops.empty())
+ {
+ while (win_iocp_operation* op = ops.front())
+ {
+ ops.pop();
+ ::InterlockedDecrement(&outstanding_work_);
+ op->destroy();
+ }
+ }
+ else
+ {
+ DWORD bytes_transferred = 0;
+ dword_ptr_t completion_key = 0;
+ LPOVERLAPPED overlapped = 0;
+ ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred,
+ &completion_key, &overlapped, gqcs_timeout);
+ if (overlapped)
+ {
+ ::InterlockedDecrement(&outstanding_work_);
+ static_cast<win_iocp_operation*>(overlapped)->destroy();
+ }
+ }
+ }
+
+ if (timer_thread_)
+ timer_thread_->join();
+}
+
+asio::error_code win_iocp_io_service::register_handle(
+ HANDLE handle, asio::error_code& ec)
+{
+ if (::CreateIoCompletionPort(handle, iocp_.handle, 0, 0) == 0)
+ {
+ DWORD last_error = ::GetLastError();
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ }
+ else
+ {
+ ec = asio::error_code();
+ }
+ return ec;
+}
+
+size_t win_iocp_io_service::run(asio::error_code& ec)
+{
+ if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
+ {
+ stop();
+ ec = asio::error_code();
+ return 0;
+ }
+
+ call_stack<win_iocp_io_service>::context ctx(this);
+
+ size_t n = 0;
+ while (do_one(true, ec))
+ if (n != (std::numeric_limits<size_t>::max)())
+ ++n;
+ return n;
+}
+
+size_t win_iocp_io_service::run_one(asio::error_code& ec)
+{
+ if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
+ {
+ stop();
+ ec = asio::error_code();
+ return 0;
+ }
+
+ call_stack<win_iocp_io_service>::context ctx(this);
+
+ return do_one(true, ec);
+}
+
+size_t win_iocp_io_service::poll(asio::error_code& ec)
+{
+ if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
+ {
+ stop();
+ ec = asio::error_code();
+ return 0;
+ }
+
+ call_stack<win_iocp_io_service>::context ctx(this);
+
+ size_t n = 0;
+ while (do_one(false, ec))
+ if (n != (std::numeric_limits<size_t>::max)())
+ ++n;
+ return n;
+}
+
+size_t win_iocp_io_service::poll_one(asio::error_code& ec)
+{
+ if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
+ {
+ stop();
+ ec = asio::error_code();
+ return 0;
+ }
+
+ call_stack<win_iocp_io_service>::context ctx(this);
+
+ return do_one(false, ec);
+}
+
+void win_iocp_io_service::stop()
+{
+ if (::InterlockedExchange(&stopped_, 1) == 0)
+ {
+ if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0))
+ {
+ DWORD last_error = ::GetLastError();
+ asio::error_code ec(last_error,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "pqcs");
+ }
+ }
+}
+
+void win_iocp_io_service::post_deferred_completion(win_iocp_operation* op)
+{
+ // Flag the operation as ready.
+ op->ready_ = 1;
+
+ // Enqueue the operation on the I/O completion port.
+ if (!::PostQueuedCompletionStatus(iocp_.handle,
+ 0, overlapped_contains_result, op))
+ {
+ // Out of resources. Put on completed queue instead.
+ mutex::scoped_lock lock(dispatch_mutex_);
+ completed_ops_.push(op);
+ ::InterlockedExchange(&dispatch_required_, 1);
+ }
+}
+
+void win_iocp_io_service::post_deferred_completions(
+ op_queue<win_iocp_operation>& ops)
+{
+ while (win_iocp_operation* op = ops.front())
+ {
+ ops.pop();
+
+ // Flag the operation as ready.
+ op->ready_ = 1;
+
+ // Enqueue the operation on the I/O completion port.
+ if (!::PostQueuedCompletionStatus(iocp_.handle,
+ 0, overlapped_contains_result, op))
+ {
+ // Out of resources. Put on completed queue instead.
+ mutex::scoped_lock lock(dispatch_mutex_);
+ completed_ops_.push(op);
+ completed_ops_.push(ops);
+ ::InterlockedExchange(&dispatch_required_, 1);
+ }
+ }
+}
+
+void win_iocp_io_service::on_pending(win_iocp_operation* op)
+{
+ if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1)
+ {
+ // Enqueue the operation on the I/O completion port.
+ if (!::PostQueuedCompletionStatus(iocp_.handle,
+ 0, overlapped_contains_result, op))
+ {
+ // Out of resources. Put on completed queue instead.
+ mutex::scoped_lock lock(dispatch_mutex_);
+ completed_ops_.push(op);
+ ::InterlockedExchange(&dispatch_required_, 1);
+ }
+ }
+}
+
+void win_iocp_io_service::on_completion(win_iocp_operation* op,
+ DWORD last_error, DWORD bytes_transferred)
+{
+ // Flag that the operation is ready for invocation.
+ op->ready_ = 1;
+
+ // Store results in the OVERLAPPED structure.
+ op->Internal = asio::error::get_system_category();
+ op->Offset = last_error;
+ op->OffsetHigh = bytes_transferred;
+
+ // Enqueue the operation on the I/O completion port.
+ if (!::PostQueuedCompletionStatus(iocp_.handle,
+ 0, overlapped_contains_result, op))
+ {
+ // Out of resources. Put on completed queue instead.
+ mutex::scoped_lock lock(dispatch_mutex_);
+ completed_ops_.push(op);
+ ::InterlockedExchange(&dispatch_required_, 1);
+ }
+}
+
+void win_iocp_io_service::on_completion(win_iocp_operation* op,
+ const asio::error_code& ec, DWORD bytes_transferred)
+{
+ // Flag that the operation is ready for invocation.
+ op->ready_ = 1;
+
+ // Store results in the OVERLAPPED structure.
+ op->Internal = ec.category();
+ op->Offset = ec.value();
+ op->OffsetHigh = bytes_transferred;
+
+ // Enqueue the operation on the I/O completion port.
+ if (!::PostQueuedCompletionStatus(iocp_.handle,
+ 0, overlapped_contains_result, op))
+ {
+ // Out of resources. Put on completed queue instead.
+ mutex::scoped_lock lock(dispatch_mutex_);
+ completed_ops_.push(op);
+ ::InterlockedExchange(&dispatch_required_, 1);
+ }
+}
+
+size_t win_iocp_io_service::do_one(bool block, asio::error_code& ec)
+{
+ for (;;)
+ {
+ // Try to acquire responsibility for dispatching timers and completed ops.
+ if (::InterlockedCompareExchange(&dispatch_required_, 0, 1) == 1)
+ {
+ mutex::scoped_lock lock(dispatch_mutex_);
+
+ // Dispatch pending timers and operations.
+ op_queue<win_iocp_operation> ops;
+ ops.push(completed_ops_);
+ timer_queues_.get_ready_timers(ops);
+ post_deferred_completions(ops);
+ update_timeout();
+ }
+
+ // Get the next operation from the queue.
+ DWORD bytes_transferred = 0;
+ dword_ptr_t completion_key = 0;
+ LPOVERLAPPED overlapped = 0;
+ ::SetLastError(0);
+ BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred,
+ &completion_key, &overlapped, block ? gqcs_timeout : 0);
+ DWORD last_error = ::GetLastError();
+
+ if (overlapped)
+ {
+ win_iocp_operation* op = static_cast<win_iocp_operation*>(overlapped);
+ asio::error_code result_ec(last_error,
+ asio::error::get_system_category());
+
+ // We may have been passed the last_error and bytes_transferred in the
+ // OVERLAPPED structure itself.
+ if (completion_key == overlapped_contains_result)
+ {
+ result_ec = asio::error_code(static_cast<int>(op->Offset),
+ static_cast<asio::error_category>(op->Internal));
+ bytes_transferred = op->OffsetHigh;
+ }
+
+ // Otherwise ensure any result has been saved into the OVERLAPPED
+ // structure.
+ else
+ {
+ op->Internal = result_ec.category();
+ op->Offset = result_ec.value();
+ op->OffsetHigh = bytes_transferred;
+ }
+
+ // Dispatch the operation only if ready. The operation may not be ready
+ // if the initiating function (e.g. a call to WSARecv) has not yet
+ // returned. This is because the initiating function still wants access
+ // to the operation's OVERLAPPED structure.
+ if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1)
+ {
+ // Ensure the count of outstanding work is decremented on block exit.
+ work_finished_on_block_exit on_exit = { this };
+ (void)on_exit;
+
+ op->complete(*this, result_ec, bytes_transferred);
+ ec = asio::error_code();
+ return 1;
+ }
+ }
+ else if (!ok)
+ {
+ if (last_error != WAIT_TIMEOUT)
+ {
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ return 0;
+ }
+
+ // If we're not polling we need to keep going until we get a real handler.
+ if (block)
+ continue;
+
+ ec = asio::error_code();
+ return 0;
+ }
+ else if (completion_key == wake_for_dispatch)
+ {
+ // We have been woken up to try to acquire responsibility for dispatching
+ // timers and completed operations.
+ }
+ else
+ {
+ // The stopped_ flag is always checked to ensure that any leftover
+ // interrupts from a previous run invocation are ignored.
+ if (::InterlockedExchangeAdd(&stopped_, 0) != 0)
+ {
+ // Wake up next thread that is blocked on GetQueuedCompletionStatus.
+ if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0))
+ {
+ last_error = ::GetLastError();
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ return 0;
+ }
+
+ ec = asio::error_code();
+ return 0;
+ }
+ }
+ }
+}
+
+void win_iocp_io_service::do_add_timer_queue(timer_queue_base& queue)
+{
+ mutex::scoped_lock lock(dispatch_mutex_);
+
+ timer_queues_.insert(&queue);
+
+ if (!waitable_timer_.handle)
+ {
+ waitable_timer_.handle = ::CreateWaitableTimer(0, FALSE, 0);
+ if (waitable_timer_.handle == 0)
+ {
+ DWORD last_error = ::GetLastError();
+ asio::error_code ec(last_error,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "timer");
+ }
+
+ LARGE_INTEGER timeout;
+ timeout.QuadPart = -max_timeout_usec;
+ timeout.QuadPart *= 10;
+ ::SetWaitableTimer(waitable_timer_.handle,
+ &timeout, max_timeout_msec, 0, 0, FALSE);
+ }
+
+ if (!timer_thread_)
+ {
+ timer_thread_function thread_function = { this };
+ timer_thread_.reset(new thread(thread_function, 65536));
+ }
+}
+
+void win_iocp_io_service::do_remove_timer_queue(timer_queue_base& queue)
+{
+ mutex::scoped_lock lock(dispatch_mutex_);
+
+ timer_queues_.erase(&queue);
+}
+
+void win_iocp_io_service::update_timeout()
+{
+ if (timer_thread_)
+ {
+ // There's no point updating the waitable timer if the new timeout period
+ // exceeds the maximum timeout. In that case, we might as well wait for the
+ // existing period of the timer to expire.
+ long timeout_usec = timer_queues_.wait_duration_usec(max_timeout_usec);
+ if (timeout_usec < max_timeout_usec)
+ {
+ LARGE_INTEGER timeout;
+ timeout.QuadPart = -timeout_usec;
+ timeout.QuadPart *= 10;
+ ::SetWaitableTimer(waitable_timer_.handle,
+ &timeout, max_timeout_msec, 0, 0, FALSE);
+ }
+ }
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_IOCP)
+
+#endif // ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_IPP
diff --git a/ext/asio/asio/detail/impl/win_iocp_serial_port_service.ipp b/ext/asio/asio/detail/impl/win_iocp_serial_port_service.ipp
new file mode 100644
index 0000000..7631514
--- /dev/null
+++ b/ext/asio/asio/detail/impl/win_iocp_serial_port_service.ipp
@@ -0,0 +1,180 @@
+//
+// detail/impl/win_iocp_serial_port_service.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2008 Rep Invariant Systems, Inc. (info at repinvariant.com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_SERIAL_PORT_SERVICE_IPP
+#define ASIO_DETAIL_IMPL_WIN_IOCP_SERIAL_PORT_SERVICE_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT)
+
+#include <cstring>
+#include "asio/detail/win_iocp_serial_port_service.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+win_iocp_serial_port_service::win_iocp_serial_port_service(
+ asio::io_service& io_service)
+ : handle_service_(io_service)
+{
+}
+
+void win_iocp_serial_port_service::shutdown_service()
+{
+}
+
+asio::error_code win_iocp_serial_port_service::open(
+ win_iocp_serial_port_service::implementation_type& impl,
+ const std::string& device, asio::error_code& ec)
+{
+ if (is_open(impl))
+ {
+ ec = asio::error::already_open;
+ return ec;
+ }
+
+ // For convenience, add a leading \\.\ sequence if not already present.
+ std::string name = (device[0] == '\\') ? device : "\\\\.\\" + device;
+
+ // Open a handle to the serial port.
+ ::HANDLE handle = ::CreateFileA(name.c_str(),
+ GENERIC_READ | GENERIC_WRITE, 0, 0,
+ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+ if (handle == INVALID_HANDLE_VALUE)
+ {
+ DWORD last_error = ::GetLastError();
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ return ec;
+ }
+
+ // Determine the initial serial port parameters.
+ using namespace std; // For memset.
+ ::DCB dcb;
+ memset(&dcb, 0, sizeof(DCB));
+ dcb.DCBlength = sizeof(DCB);
+ if (!::GetCommState(handle, &dcb))
+ {
+ DWORD last_error = ::GetLastError();
+ ::CloseHandle(handle);
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ return ec;
+ }
+
+ // Set some default serial port parameters. This implementation does not
+ // support changing these, so they might as well be in a known state.
+ dcb.fBinary = TRUE; // Win32 only supports binary mode.
+ dcb.fDsrSensitivity = FALSE;
+ dcb.fNull = FALSE; // Do not ignore NULL characters.
+ dcb.fAbortOnError = FALSE; // Ignore serial framing errors.
+ if (!::SetCommState(handle, &dcb))
+ {
+ DWORD last_error = ::GetLastError();
+ ::CloseHandle(handle);
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ return ec;
+ }
+
+ // Set up timeouts so that the serial port will behave similarly to a
+ // network socket. Reads wait for at least one byte, then return with
+ // whatever they have. Writes return once everything is out the door.
+ ::COMMTIMEOUTS timeouts;
+ timeouts.ReadIntervalTimeout = 1;
+ timeouts.ReadTotalTimeoutMultiplier = 0;
+ timeouts.ReadTotalTimeoutConstant = 0;
+ timeouts.WriteTotalTimeoutMultiplier = 0;
+ timeouts.WriteTotalTimeoutConstant = 0;
+ if (!::SetCommTimeouts(handle, &timeouts))
+ {
+ DWORD last_error = ::GetLastError();
+ ::CloseHandle(handle);
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ return ec;
+ }
+
+ // We're done. Take ownership of the serial port handle.
+ if (handle_service_.assign(impl, handle, ec))
+ ::CloseHandle(handle);
+ return ec;
+}
+
+asio::error_code win_iocp_serial_port_service::do_set_option(
+ win_iocp_serial_port_service::implementation_type& impl,
+ win_iocp_serial_port_service::store_function_type store,
+ const void* option, asio::error_code& ec)
+{
+ using namespace std; // For memcpy.
+
+ ::DCB dcb;
+ memset(&dcb, 0, sizeof(DCB));
+ dcb.DCBlength = sizeof(DCB);
+ if (!::GetCommState(handle_service_.native(impl), &dcb))
+ {
+ DWORD last_error = ::GetLastError();
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ return ec;
+ }
+
+ if (store(option, dcb, ec))
+ return ec;
+
+ if (!::SetCommState(handle_service_.native(impl), &dcb))
+ {
+ DWORD last_error = ::GetLastError();
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ return ec;
+ }
+
+ ec = asio::error_code();
+ return ec;
+}
+
+asio::error_code win_iocp_serial_port_service::do_get_option(
+ const win_iocp_serial_port_service::implementation_type& impl,
+ win_iocp_serial_port_service::load_function_type load,
+ void* option, asio::error_code& ec) const
+{
+ using namespace std; // For memset.
+
+ ::DCB dcb;
+ memset(&dcb, 0, sizeof(DCB));
+ dcb.DCBlength = sizeof(DCB);
+ if (!::GetCommState(handle_service_.native(impl), &dcb))
+ {
+ DWORD last_error = ::GetLastError();
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ return ec;
+ }
+
+ return load(option, dcb, ec);
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT)
+
+#endif // ASIO_DETAIL_IMPL_WIN_IOCP_SERIAL_PORT_SERVICE_IPP
diff --git a/ext/asio/asio/detail/impl/win_iocp_socket_service_base.ipp b/ext/asio/asio/detail/impl/win_iocp_socket_service_base.ipp
new file mode 100644
index 0000000..37c6f63
--- /dev/null
+++ b/ext/asio/asio/detail/impl/win_iocp_socket_service_base.ipp
@@ -0,0 +1,575 @@
+//
+// detail/impl/win_iocp_socket_service_base.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP
+#define ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+
+#include "asio/detail/win_iocp_socket_service_base.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+win_iocp_socket_service_base::win_iocp_socket_service_base(
+ asio::io_service& io_service)
+ : io_service_(io_service),
+ iocp_service_(use_service<win_iocp_io_service>(io_service)),
+ reactor_(0),
+ mutex_(),
+ impl_list_(0)
+{
+}
+
+void win_iocp_socket_service_base::shutdown_service()
+{
+ // Close all implementations, causing all operations to complete.
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ base_implementation_type* impl = impl_list_;
+ while (impl)
+ {
+ asio::error_code ignored_ec;
+ close_for_destruction(*impl);
+ impl = impl->next_;
+ }
+}
+
+void win_iocp_socket_service_base::construct(
+ win_iocp_socket_service_base::base_implementation_type& impl)
+{
+ impl.socket_ = invalid_socket;
+ impl.state_ = 0;
+ impl.cancel_token_.reset();
+#if defined(ASIO_ENABLE_CANCELIO)
+ impl.safe_cancellation_thread_id_ = 0;
+#endif // defined(ASIO_ENABLE_CANCELIO)
+
+ // Insert implementation into linked list of all implementations.
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ impl.next_ = impl_list_;
+ impl.prev_ = 0;
+ if (impl_list_)
+ impl_list_->prev_ = &impl;
+ impl_list_ = &impl;
+}
+
+void win_iocp_socket_service_base::destroy(
+ win_iocp_socket_service_base::base_implementation_type& impl)
+{
+ close_for_destruction(impl);
+
+ // Remove implementation from linked list of all implementations.
+ asio::detail::mutex::scoped_lock lock(mutex_);
+ if (impl_list_ == &impl)
+ impl_list_ = impl.next_;
+ if (impl.prev_)
+ impl.prev_->next_ = impl.next_;
+ if (impl.next_)
+ impl.next_->prev_= impl.prev_;
+ impl.next_ = 0;
+ impl.prev_ = 0;
+}
+
+asio::error_code win_iocp_socket_service_base::close(
+ win_iocp_socket_service_base::base_implementation_type& impl,
+ asio::error_code& ec)
+{
+ if (is_open(impl))
+ {
+ // Check if the reactor was created, in which case we need to close the
+ // socket on the reactor as well to cancel any operations that might be
+ // running there.
+ reactor* r = static_cast<reactor*>(
+ interlocked_compare_exchange_pointer(
+ reinterpret_cast<void**>(&reactor_), 0, 0));
+ if (r)
+ r->close_descriptor(impl.socket_, impl.reactor_data_);
+ }
+
+ if (socket_ops::close(impl.socket_, impl.state_, false, ec) == 0)
+ {
+ impl.socket_ = invalid_socket;
+ impl.state_ = 0;
+ impl.cancel_token_.reset();
+#if defined(ASIO_ENABLE_CANCELIO)
+ impl.safe_cancellation_thread_id_ = 0;
+#endif // defined(ASIO_ENABLE_CANCELIO)
+ }
+
+ return ec;
+}
+
+asio::error_code win_iocp_socket_service_base::cancel(
+ win_iocp_socket_service_base::base_implementation_type& impl,
+ asio::error_code& ec)
+{
+ if (!is_open(impl))
+ {
+ ec = asio::error::bad_descriptor;
+ return ec;
+ }
+ else if (FARPROC cancel_io_ex_ptr = ::GetProcAddress(
+ ::GetModuleHandleA("KERNEL32"), "CancelIoEx"))
+ {
+ // The version of Windows supports cancellation from any thread.
+ typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED);
+ cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr;
+ socket_type sock = impl.socket_;
+ HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock);
+ if (!cancel_io_ex(sock_as_handle, 0))
+ {
+ DWORD last_error = ::GetLastError();
+ if (last_error == ERROR_NOT_FOUND)
+ {
+ // ERROR_NOT_FOUND means that there were no operations to be
+ // cancelled. We swallow this error to match the behaviour on other
+ // platforms.
+ ec = asio::error_code();
+ }
+ else
+ {
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ }
+ }
+ else
+ {
+ ec = asio::error_code();
+ }
+ }
+#if defined(ASIO_ENABLE_CANCELIO)
+ else if (impl.safe_cancellation_thread_id_ == 0)
+ {
+ // No operations have been started, so there's nothing to cancel.
+ ec = asio::error_code();
+ }
+ else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId())
+ {
+ // Asynchronous operations have been started from the current thread only,
+ // so it is safe to try to cancel them using CancelIo.
+ socket_type sock = impl.socket_;
+ HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock);
+ if (!::CancelIo(sock_as_handle))
+ {
+ DWORD last_error = ::GetLastError();
+ ec = asio::error_code(last_error,
+ asio::error::get_system_category());
+ }
+ else
+ {
+ ec = asio::error_code();
+ }
+ }
+ else
+ {
+ // Asynchronous operations have been started from more than one thread,
+ // so cancellation is not safe.
+ ec = asio::error::operation_not_supported;
+ }
+#else // defined(ASIO_ENABLE_CANCELIO)
+ else
+ {
+ // Cancellation is not supported as CancelIo may not be used.
+ ec = asio::error::operation_not_supported;
+ }
+#endif // defined(ASIO_ENABLE_CANCELIO)
+
+ // Cancel any operations started via the reactor.
+ if (!ec)
+ {
+ reactor* r = static_cast<reactor*>(
+ interlocked_compare_exchange_pointer(
+ reinterpret_cast<void**>(&reactor_), 0, 0));
+ if (r)
+ r->cancel_ops(impl.socket_, impl.reactor_data_);
+ }
+
+ return ec;
+}
+
+asio::error_code win_iocp_socket_service_base::do_open(
+ win_iocp_socket_service_base::base_implementation_type& impl,
+ int family, int type, int protocol, asio::error_code& ec)
+{
+ if (is_open(impl))
+ {
+ ec = asio::error::already_open;
+ return ec;
+ }
+
+ socket_holder sock(socket_ops::socket(family, type, protocol, ec));
+ if (sock.get() == invalid_socket)
+ return ec;
+
+ HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock.get());
+ if (iocp_service_.register_handle(sock_as_handle, ec))
+ return ec;
+
+ impl.socket_ = sock.release();
+ switch (type)
+ {
+ case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break;
+ case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break;
+ default: impl.state_ = 0; break;
+ }
+ impl.cancel_token_.reset(static_cast<void*>(0), socket_ops::noop_deleter());
+ ec = asio::error_code();
+ return ec;
+}
+
+asio::error_code win_iocp_socket_service_base::do_assign(
+ win_iocp_socket_service_base::base_implementation_type& impl,
+ int type, socket_type native_socket, asio::error_code& ec)
+{
+ if (is_open(impl))
+ {
+ ec = asio::error::already_open;
+ return ec;
+ }
+
+ HANDLE sock_as_handle = reinterpret_cast<HANDLE>(native_socket);
+ if (iocp_service_.register_handle(sock_as_handle, ec))
+ return ec;
+
+ impl.socket_ = native_socket;
+ switch (type)
+ {
+ case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break;
+ case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break;
+ default: impl.state_ = 0; break;
+ }
+ impl.cancel_token_.reset(static_cast<void*>(0), socket_ops::noop_deleter());
+ ec = asio::error_code();
+ return ec;
+}
+
+void win_iocp_socket_service_base::start_send_op(
+ win_iocp_socket_service_base::base_implementation_type& impl,
+ WSABUF* buffers, std::size_t buffer_count,
+ socket_base::message_flags flags, bool noop, operation* op)
+{
+ update_cancellation_thread_id(impl);
+ iocp_service_.work_started();
+
+ if (noop)
+ iocp_service_.on_completion(op);
+ else if (!is_open(impl))
+ iocp_service_.on_completion(op, asio::error::bad_descriptor);
+ else
+ {
+ DWORD bytes_transferred = 0;
+ int result = ::WSASend(impl.socket_, buffers,
+ static_cast<DWORD>(buffer_count), &bytes_transferred, flags, op, 0);
+ DWORD last_error = ::WSAGetLastError();
+ if (last_error == ERROR_PORT_UNREACHABLE)
+ last_error = WSAECONNREFUSED;
+ if (result != 0 && last_error != WSA_IO_PENDING)
+ iocp_service_.on_completion(op, last_error, bytes_transferred);
+ else
+ iocp_service_.on_pending(op);
+ }
+}
+
+void win_iocp_socket_service_base::start_send_to_op(
+ win_iocp_socket_service_base::base_implementation_type& impl,
+ WSABUF* buffers, std::size_t buffer_count,
+ const socket_addr_type* addr, int addrlen,
+ socket_base::message_flags flags, operation* op)
+{
+ update_cancellation_thread_id(impl);
+ iocp_service_.work_started();
+
+ if (!is_open(impl))
+ iocp_service_.on_completion(op, asio::error::bad_descriptor);
+ else
+ {
+ DWORD bytes_transferred = 0;
+ int result = ::WSASendTo(impl.socket_, buffers,
+ static_cast<DWORD>(buffer_count),
+ &bytes_transferred, flags, addr, addrlen, op, 0);
+ DWORD last_error = ::WSAGetLastError();
+ if (last_error == ERROR_PORT_UNREACHABLE)
+ last_error = WSAECONNREFUSED;
+ if (result != 0 && last_error != WSA_IO_PENDING)
+ iocp_service_.on_completion(op, last_error, bytes_transferred);
+ else
+ iocp_service_.on_pending(op);
+ }
+}
+
+void win_iocp_socket_service_base::start_receive_op(
+ win_iocp_socket_service_base::base_implementation_type& impl,
+ WSABUF* buffers, std::size_t buffer_count,
+ socket_base::message_flags flags, bool noop, operation* op)
+{
+ update_cancellation_thread_id(impl);
+ iocp_service_.work_started();
+
+ if (noop)
+ iocp_service_.on_completion(op);
+ else if (!is_open(impl))
+ iocp_service_.on_completion(op, asio::error::bad_descriptor);
+ else
+ {
+ DWORD bytes_transferred = 0;
+ DWORD recv_flags = flags;
+ int result = ::WSARecv(impl.socket_, buffers,
+ static_cast<DWORD>(buffer_count),
+ &bytes_transferred, &recv_flags, op, 0);
+ DWORD last_error = ::WSAGetLastError();
+ if (last_error == ERROR_NETNAME_DELETED)
+ last_error = WSAECONNRESET;
+ else if (last_error == ERROR_PORT_UNREACHABLE)
+ last_error = WSAECONNREFUSED;
+ if (result != 0 && last_error != WSA_IO_PENDING)
+ iocp_service_.on_completion(op, last_error, bytes_transferred);
+ else
+ iocp_service_.on_pending(op);
+ }
+}
+
+void win_iocp_socket_service_base::start_null_buffers_receive_op(
+ win_iocp_socket_service_base::base_implementation_type& impl,
+ socket_base::message_flags flags, reactor_op* op)
+{
+ if ((impl.state_ & socket_ops::stream_oriented) != 0)
+ {
+ // For stream sockets on Windows, we may issue a 0-byte overlapped
+ // WSARecv to wait until there is data available on the socket.
+ ::WSABUF buf = { 0, 0 };
+ start_receive_op(impl, &buf, 1, flags, false, op);
+ }
+ else
+ {
+ start_reactor_op(impl,
+ (flags & socket_base::message_out_of_band)
+ ? reactor::except_op : reactor::read_op,
+ op);
+ }
+}
+
+void win_iocp_socket_service_base::start_receive_from_op(
+ win_iocp_socket_service_base::base_implementation_type& impl,
+ WSABUF* buffers, std::size_t buffer_count, socket_addr_type* addr,
+ socket_base::message_flags flags, int* addrlen, operation* op)
+{
+ update_cancellation_thread_id(impl);
+ iocp_service_.work_started();
+
+ if (!is_open(impl))
+ iocp_service_.on_completion(op, asio::error::bad_descriptor);
+ else
+ {
+ DWORD bytes_transferred = 0;
+ DWORD recv_flags = flags;
+ int result = ::WSARecvFrom(impl.socket_, buffers,
+ static_cast<DWORD>(buffer_count),
+ &bytes_transferred, &recv_flags, addr, addrlen, op, 0);
+ DWORD last_error = ::WSAGetLastError();
+ if (last_error == ERROR_PORT_UNREACHABLE)
+ last_error = WSAECONNREFUSED;
+ if (result != 0 && last_error != WSA_IO_PENDING)
+ iocp_service_.on_completion(op, last_error, bytes_transferred);
+ else
+ iocp_service_.on_pending(op);
+ }
+}
+
+void win_iocp_socket_service_base::start_accept_op(
+ win_iocp_socket_service_base::base_implementation_type& impl,
+ bool peer_is_open, socket_holder& new_socket, int family, int type,
+ int protocol, void* output_buffer, DWORD address_length, operation* op)
+{
+ update_cancellation_thread_id(impl);
+ iocp_service_.work_started();
+
+ if (!is_open(impl))
+ iocp_service_.on_completion(op, asio::error::bad_descriptor);
+ else if (peer_is_open)
+ iocp_service_.on_completion(op, asio::error::already_open);
+ else
+ {
+ asio::error_code ec;
+ new_socket.reset(socket_ops::socket(family, type, protocol, ec));
+ if (new_socket.get() == invalid_socket)
+ iocp_service_.on_completion(op, ec);
+ else
+ {
+ DWORD bytes_read = 0;
+ BOOL result = ::AcceptEx(impl.socket_, new_socket.get(), output_buffer,
+ 0, address_length, address_length, &bytes_read, op);
+ DWORD last_error = ::WSAGetLastError();
+ if (!result && last_error != WSA_IO_PENDING)
+ iocp_service_.on_completion(op, last_error);
+ else
+ iocp_service_.on_pending(op);
+ }
+ }
+}
+
+void win_iocp_socket_service_base::restart_accept_op(
+ socket_type s, socket_holder& new_socket, int family, int type,
+ int protocol, void* output_buffer, DWORD address_length, operation* op)
+{
+ new_socket.reset();
+ iocp_service_.work_started();
+
+ asio::error_code ec;
+ new_socket.reset(socket_ops::socket(family, type, protocol, ec));
+ if (new_socket.get() == invalid_socket)
+ iocp_service_.on_completion(op, ec);
+ else
+ {
+ DWORD bytes_read = 0;
+ BOOL result = ::AcceptEx(s, new_socket.get(), output_buffer,
+ 0, address_length, address_length, &bytes_read, op);
+ DWORD last_error = ::WSAGetLastError();
+ if (!result && last_error != WSA_IO_PENDING)
+ iocp_service_.on_completion(op, last_error);
+ else
+ iocp_service_.on_pending(op);
+ }
+}
+
+void win_iocp_socket_service_base::start_reactor_op(
+ win_iocp_socket_service_base::base_implementation_type& impl,
+ int op_type, reactor_op* op)
+{
+ reactor& r = get_reactor();
+ update_cancellation_thread_id(impl);
+
+ if (is_open(impl))
+ {
+ r.start_op(op_type, impl.socket_, impl.reactor_data_, op, false);
+ return;
+ }
+ else
+ op->ec_ = asio::error::bad_descriptor;
+
+ iocp_service_.post_immediate_completion(op);
+}
+
+void win_iocp_socket_service_base::start_connect_op(
+ win_iocp_socket_service_base::base_implementation_type& impl,
+ reactor_op* op, const socket_addr_type* addr, std::size_t addrlen)
+{
+ reactor& r = get_reactor();
+ update_cancellation_thread_id(impl);
+
+ if ((impl.state_ & socket_ops::non_blocking) != 0
+ || socket_ops::set_internal_non_blocking(
+ impl.socket_, impl.state_, op->ec_))
+ {
+ if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0)
+ {
+ if (op->ec_ == asio::error::in_progress
+ || op->ec_ == asio::error::would_block)
+ {
+ op->ec_ = asio::error_code();
+ r.start_op(reactor::connect_op, impl.socket_,
+ impl.reactor_data_, op, false);
+ return;
+ }
+ }
+ }
+
+ r.post_immediate_completion(op);
+}
+
+void win_iocp_socket_service_base::close_for_destruction(
+ win_iocp_socket_service_base::base_implementation_type& impl)
+{
+ if (is_open(impl))
+ {
+ // Check if the reactor was created, in which case we need to close the
+ // socket on the reactor as well to cancel any operations that might be
+ // running there.
+ reactor* r = static_cast<reactor*>(
+ interlocked_compare_exchange_pointer(
+ reinterpret_cast<void**>(&reactor_), 0, 0));
+ if (r)
+ r->close_descriptor(impl.socket_, impl.reactor_data_);
+ }
+
+ asio::error_code ignored_ec;
+ socket_ops::close(impl.socket_, impl.state_, true, ignored_ec);
+ impl.socket_ = invalid_socket;
+ impl.state_ = 0;
+ impl.cancel_token_.reset();
+#if defined(ASIO_ENABLE_CANCELIO)
+ impl.safe_cancellation_thread_id_ = 0;
+#endif // defined(ASIO_ENABLE_CANCELIO)
+}
+
+void win_iocp_socket_service_base::update_cancellation_thread_id(
+ win_iocp_socket_service_base::base_implementation_type& impl)
+{
+#if defined(ASIO_ENABLE_CANCELIO)
+ if (impl.safe_cancellation_thread_id_ == 0)
+ impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
+ else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId())
+ impl.safe_cancellation_thread_id_ = ~DWORD(0);
+#else // defined(ASIO_ENABLE_CANCELIO)
+ (void)impl;
+#endif // defined(ASIO_ENABLE_CANCELIO)
+}
+
+reactor& win_iocp_socket_service_base::get_reactor()
+{
+ reactor* r = static_cast<reactor*>(
+ interlocked_compare_exchange_pointer(
+ reinterpret_cast<void**>(&reactor_), 0, 0));
+ if (!r)
+ {
+ r = &(use_service<reactor>(io_service_));
+ interlocked_exchange_pointer(reinterpret_cast<void**>(&reactor_), r);
+ }
+ return *r;
+}
+
+void* win_iocp_socket_service_base::interlocked_compare_exchange_pointer(
+ void** dest, void* exch, void* cmp)
+{
+#if defined(_M_IX86)
+ return reinterpret_cast<void*>(InterlockedCompareExchange(
+ reinterpret_cast<PLONG>(dest), reinterpret_cast<LONG>(exch),
+ reinterpret_cast<LONG>(cmp)));
+#else
+ return InterlockedCompareExchangePointer(dest, exch, cmp);
+#endif
+}
+
+void* win_iocp_socket_service_base::interlocked_exchange_pointer(
+ void** dest, void* val)
+{
+#if defined(_M_IX86)
+ return reinterpret_cast<void*>(InterlockedExchange(
+ reinterpret_cast<PLONG>(dest), reinterpret_cast<LONG>(val)));
+#else
+ return InterlockedExchangePointer(dest, val);
+#endif
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_IOCP)
+
+#endif // ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP
diff --git a/ext/asio/asio/detail/impl/win_mutex.ipp b/ext/asio/asio/detail/impl/win_mutex.ipp
new file mode 100644
index 0000000..910cc23
--- /dev/null
+++ b/ext/asio/asio/detail/impl/win_mutex.ipp
@@ -0,0 +1,78 @@
+//
+// detail/impl/win_mutex.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_WIN_MUTEX_IPP
+#define ASIO_DETAIL_IMPL_WIN_MUTEX_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(BOOST_WINDOWS)
+
+#include "asio/detail/throw_error.hpp"
+#include "asio/detail/win_mutex.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+win_mutex::win_mutex()
+{
+ int error = do_init();
+ asio::error_code ec(error,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "mutex");
+}
+
+int win_mutex::do_init()
+{
+#if defined(__MINGW32__)
+ // Not sure if MinGW supports structured exception handling, so for now
+ // we'll just call the Windows API and hope.
+# if defined(UNDER_CE)
+ ::InitializeCriticalSection(&crit_section_);
+# else
+ if (!::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000))
+ return ::GetLastError();
+# endif
+ return 0;
+#else
+ __try
+ {
+# if defined(UNDER_CE)
+ ::InitializeCriticalSection(&crit_section_);
+# else
+ if (!::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000))
+ return ::GetLastError();
+# endif
+ }
+ __except(GetExceptionCode() == STATUS_NO_MEMORY
+ ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
+ {
+ return ERROR_OUTOFMEMORY;
+ }
+
+ return 0;
+#endif
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(BOOST_WINDOWS)
+
+#endif // ASIO_DETAIL_IMPL_WIN_MUTEX_IPP
diff --git a/ext/asio/asio/detail/impl/win_thread.ipp b/ext/asio/asio/detail/impl/win_thread.ipp
new file mode 100644
index 0000000..65844c8
--- /dev/null
+++ b/ext/asio/asio/detail/impl/win_thread.ipp
@@ -0,0 +1,138 @@
+//
+// detail/impl/win_thread.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_WIN_THREAD_IPP
+#define ASIO_DETAIL_IMPL_WIN_THREAD_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(BOOST_WINDOWS) && !defined(UNDER_CE)
+
+#include <process.h>
+#include "asio/detail/throw_error.hpp"
+#include "asio/detail/win_thread.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+win_thread::~win_thread()
+{
+ ::CloseHandle(thread_);
+
+ // The exit_event_ handle is deliberately allowed to leak here since it
+ // is an error for the owner of an internal thread not to join() it.
+}
+
+void win_thread::join()
+{
+ HANDLE handles[2] = { exit_event_, thread_ };
+ ::WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+ ::CloseHandle(exit_event_);
+ if (terminate_threads())
+ {
+ ::TerminateThread(thread_, 0);
+ }
+ else
+ {
+ ::QueueUserAPC(apc_function, thread_, 0);
+ ::WaitForSingleObject(thread_, INFINITE);
+ }
+}
+
+void win_thread::start_thread(func_base* arg, unsigned int stack_size)
+{
+ ::HANDLE entry_event = 0;
+ arg->entry_event_ = entry_event = ::CreateEvent(0, true, false, 0);
+ if (!entry_event)
+ {
+ DWORD last_error = ::GetLastError();
+ delete arg;
+ asio::error_code ec(last_error,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "thread.entry_event");
+ }
+
+ arg->exit_event_ = exit_event_ = ::CreateEvent(0, true, false, 0);
+ if (!exit_event_)
+ {
+ DWORD last_error = ::GetLastError();
+ delete arg;
+ asio::error_code ec(last_error,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "thread.exit_event");
+ }
+
+ unsigned int thread_id = 0;
+ thread_ = reinterpret_cast<HANDLE>(::_beginthreadex(0,
+ stack_size, win_thread_function, arg, 0, &thread_id));
+ if (!thread_)
+ {
+ DWORD last_error = ::GetLastError();
+ delete arg;
+ if (entry_event)
+ ::CloseHandle(entry_event);
+ if (exit_event_)
+ ::CloseHandle(exit_event_);
+ asio::error_code ec(last_error,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "thread");
+ }
+
+ if (entry_event)
+ {
+ ::WaitForSingleObject(entry_event, INFINITE);
+ ::CloseHandle(entry_event);
+ }
+}
+
+unsigned int __stdcall win_thread_function(void* arg)
+{
+ std::auto_ptr<win_thread::func_base> func(
+ static_cast<win_thread::func_base*>(arg));
+
+ ::SetEvent(func->entry_event_);
+
+ func->run();
+
+ // Signal that the thread has finished its work, but rather than returning go
+ // to sleep to put the thread into a well known state. If the thread is being
+ // joined during global object destruction then it may be killed using
+ // TerminateThread (to avoid a deadlock in DllMain). Otherwise, the SleepEx
+ // call will be interrupted using QueueUserAPC and the thread will shut down
+ // cleanly.
+ HANDLE exit_event = func->exit_event_;
+ func.reset();
+ ::SetEvent(exit_event);
+ ::SleepEx(INFINITE, TRUE);
+
+ return 0;
+}
+
+#if defined(WINVER) && (WINVER < 0x0500)
+void __stdcall apc_function(ULONG) {}
+#else
+void __stdcall apc_function(ULONG_PTR) {}
+#endif
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(BOOST_WINDOWS) && !defined(UNDER_CE)
+
+#endif // ASIO_DETAIL_IMPL_WIN_THREAD_IPP
diff --git a/ext/asio/asio/detail/impl/win_tss_ptr.ipp b/ext/asio/asio/detail/impl/win_tss_ptr.ipp
new file mode 100644
index 0000000..de3ea3c
--- /dev/null
+++ b/ext/asio/asio/detail/impl/win_tss_ptr.ipp
@@ -0,0 +1,57 @@
+//
+// detail/impl/win_tss_ptr.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_WIN_TSS_PTR_IPP
+#define ASIO_DETAIL_IMPL_WIN_TSS_PTR_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(BOOST_WINDOWS)
+
+#include "asio/detail/throw_error.hpp"
+#include "asio/detail/win_tss_ptr.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+DWORD win_tss_ptr_create()
+{
+#if defined(UNDER_CE)
+ enum { out_of_indexes = 0xFFFFFFFF };
+#else
+ enum { out_of_indexes = TLS_OUT_OF_INDEXES };
+#endif
+
+ DWORD tss_key = ::TlsAlloc();
+ if (tss_key == out_of_indexes)
+ {
+ DWORD last_error = ::GetLastError();
+ asio::error_code ec(last_error,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "tss");
+ }
+ return tss_key;
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(BOOST_WINDOWS)
+
+#endif // ASIO_DETAIL_IMPL_WIN_TSS_PTR_IPP
diff --git a/ext/asio/asio/detail/impl/winsock_init.ipp b/ext/asio/asio/detail/impl/winsock_init.ipp
new file mode 100644
index 0000000..3b20c98
--- /dev/null
+++ b/ext/asio/asio/detail/impl/winsock_init.ipp
@@ -0,0 +1,69 @@
+//
+// detail/impl/winsock_init.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_IMPL_WINSOCK_INIT_IPP
+#define ASIO_DETAIL_IMPL_WINSOCK_INIT_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+#include "asio/detail/socket_types.hpp"
+#include "asio/detail/winsock_init.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+void winsock_init_base::startup(data& d,
+ unsigned char major, unsigned char minor)
+{
+ if (::InterlockedIncrement(&d.init_count_) == 1)
+ {
+ WSADATA wsa_data;
+ long result = ::WSAStartup(MAKEWORD(major, minor), &wsa_data);
+ ::InterlockedExchange(&d.result_, result);
+ }
+}
+
+void winsock_init_base::cleanup(data& d)
+{
+ if (::InterlockedDecrement(&d.init_count_) == 0)
+ {
+ ::WSACleanup();
+ }
+}
+
+void winsock_init_base::throw_on_error(data& d)
+{
+ long result = ::InterlockedExchangeAdd(&d.result_, 0);
+ if (result != 0)
+ {
+ asio::error_code ec(result,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "winsock");
+ }
+}
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+#endif // ASIO_DETAIL_IMPL_WINSOCK_INIT_IPP
diff --git a/ext/asio/asio/detail/io_control.hpp b/ext/asio/asio/detail/io_control.hpp
index df7171c..b3ef844 100644
--- a/ext/asio/asio/detail/io_control.hpp
+++ b/ext/asio/asio/detail/io_control.hpp
@@ -1,8 +1,8 @@
//
-// io_control.hpp
-// ~~~~~~~~~~~~~~
+// detail/io_control.hpp
+// ~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,15 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/detail/socket_types.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
namespace io_control {
diff --git a/ext/asio/asio/detail/kqueue_reactor.hpp b/ext/asio/asio/detail/kqueue_reactor.hpp
index bfa004d..07087dd 100644
--- a/ext/asio/asio/detail/kqueue_reactor.hpp
+++ b/ext/asio/asio/detail/kqueue_reactor.hpp
@@ -1,8 +1,8 @@
//
-// kqueue_reactor.hpp
-// ~~~~~~~~~~~~~~~~~~
+// detail/kqueue_reactor.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
@@ -16,41 +16,35 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/kqueue_reactor_fwd.hpp"
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_KQUEUE)
-#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
-#include <boost/config.hpp>
-#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error.hpp"
-#include "asio/io_service.hpp"
-#include "asio/system_error.hpp"
-#include "asio/detail/hash_map.hpp"
+#include "asio/detail/kqueue_reactor_fwd.hpp"
#include "asio/detail/mutex.hpp"
+#include "asio/detail/object_pool.hpp"
#include "asio/detail/op_queue.hpp"
#include "asio/detail/reactor_op.hpp"
#include "asio/detail/select_interrupter.hpp"
-#include "asio/detail/service_base.hpp"
#include "asio/detail/socket_types.hpp"
#include "asio/detail/timer_op.hpp"
#include "asio/detail/timer_queue_base.hpp"
#include "asio/detail/timer_queue_fwd.hpp"
#include "asio/detail/timer_queue_set.hpp"
+#include "asio/error.hpp"
+#include "asio/io_service.hpp"
// Older versions of Mac OS X may not define EV_OOBAND.
#if !defined(EV_OOBAND)
# define EV_OOBAND EV_FLAG1
#endif // !defined(EV_OOBAND)
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -64,384 +58,98 @@ public:
// Per-descriptor queues.
struct descriptor_state
{
- descriptor_state() {}
- descriptor_state(const descriptor_state&) {}
- void operator=(const descriptor_state&) {}
-
+ friend class kqueue_reactor;
+ friend class object_pool_access;
mutex mutex_;
op_queue<reactor_op> op_queue_[max_ops];
bool shutdown_;
+ descriptor_state* next_;
+ descriptor_state* prev_;
};
// Per-descriptor data.
typedef descriptor_state* per_descriptor_data;
// Constructor.
- kqueue_reactor(asio::io_service& io_service)
- : asio::detail::service_base<kqueue_reactor>(io_service),
- io_service_(use_service<io_service_impl>(io_service)),
- mutex_(),
- kqueue_fd_(do_kqueue_create()),
- interrupter_(),
- shutdown_(false)
- {
- // The interrupter is put into a permanently readable state. Whenever we
- // want to interrupt the blocked kevent call we register a one-shot read
- // operation against the descriptor.
- interrupter_.interrupt();
- }
+ ASIO_DECL kqueue_reactor(asio::io_service& io_service);
// Destructor.
- ~kqueue_reactor()
- {
- close(kqueue_fd_);
- }
+ ASIO_DECL ~kqueue_reactor();
// Destroy all user-defined handler objects owned by the service.
- void shutdown_service()
- {
- mutex::scoped_lock lock(mutex_);
- shutdown_ = true;
- lock.unlock();
-
- op_queue<operation> ops;
-
- descriptor_map::iterator iter = registered_descriptors_.begin();
- descriptor_map::iterator end = registered_descriptors_.end();
- while (iter != end)
- {
- for (int i = 0; i < max_ops; ++i)
- ops.push(iter->second.op_queue_[i]);
- iter->second.shutdown_ = true;
- ++iter;
- }
-
- timer_queues_.get_all_timers(ops);
- }
+ ASIO_DECL void shutdown_service();
// Initialise the task.
- void init_task()
- {
- io_service_.init_task();
- }
+ ASIO_DECL void init_task();
// Register a socket with the reactor. Returns 0 on success, system error
// code on failure.
- int register_descriptor(socket_type descriptor,
- per_descriptor_data& descriptor_data)
- {
- mutex::scoped_lock lock(registered_descriptors_mutex_);
-
- descriptor_map::iterator new_entry = registered_descriptors_.insert(
- std::make_pair(descriptor, descriptor_state())).first;
- descriptor_data = &new_entry->second;
+ ASIO_DECL int register_descriptor(socket_type descriptor,
+ per_descriptor_data& descriptor_data);
- descriptor_data->shutdown_ = false;
-
- return 0;
+ // Post a reactor operation for immediate completion.
+ void post_immediate_completion(reactor_op* op)
+ {
+ io_service_.post_immediate_completion(op);
}
// Start a new operation. The reactor operation will be performed when the
// given descriptor is flagged as ready, or an error has occurred.
- void start_op(int op_type, socket_type descriptor,
+ ASIO_DECL void start_op(int op_type, socket_type descriptor,
per_descriptor_data& descriptor_data,
- reactor_op* op, bool allow_speculative)
- {
- mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
- if (descriptor_data->shutdown_)
- return;
-
- bool first = descriptor_data->op_queue_[op_type].empty();
- if (first)
- {
- if (allow_speculative)
- {
- if (op_type != read_op || descriptor_data->op_queue_[except_op].empty())
- {
- if (op->perform())
- {
- descriptor_lock.unlock();
- io_service_.post_immediate_completion(op);
- return;
- }
- }
- }
- }
-
- descriptor_data->op_queue_[op_type].push(op);
- io_service_.work_started();
-
- if (first)
- {
- struct kevent event;
- switch (op_type)
- {
- case read_op:
- EV_SET(&event, descriptor, EVFILT_READ,
- EV_ADD | EV_ONESHOT, 0, 0, descriptor_data);
- break;
- case write_op:
- EV_SET(&event, descriptor, EVFILT_WRITE,
- EV_ADD | EV_ONESHOT, 0, 0, descriptor_data);
- break;
- case except_op:
- if (!descriptor_data->op_queue_[read_op].empty())
- return; // Already registered for read events.
- EV_SET(&event, descriptor, EVFILT_READ,
- EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data);
- break;
- }
-
- if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
- {
- op->ec_ = asio::error_code(errno,
- asio::error::get_system_category());
- descriptor_data->op_queue_[op_type].pop();
- io_service_.post_deferred_completion(op);
- }
- }
- }
+ reactor_op* op, bool allow_speculative);
// Cancel all operations associated with the given descriptor. The
// handlers associated with the descriptor will be invoked with the
// operation_aborted error.
- void cancel_ops(socket_type , per_descriptor_data& descriptor_data)
- {
- mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
-
- op_queue<operation> ops;
- for (int i = 0; i < max_ops; ++i)
- {
- while (reactor_op* op = descriptor_data->op_queue_[i].front())
- {
- op->ec_ = asio::error::operation_aborted;
- descriptor_data->op_queue_[i].pop();
- ops.push(op);
- }
- }
-
- descriptor_lock.unlock();
-
- io_service_.post_deferred_completions(ops);
- }
+ ASIO_DECL void cancel_ops(socket_type descriptor,
+ per_descriptor_data& descriptor_data);
// Cancel any operations that are running against the descriptor and remove
// its registration from the reactor.
- void close_descriptor(socket_type descriptor,
- per_descriptor_data& descriptor_data)
- {
- mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
- mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_);
-
- // Remove the descriptor from the set of known descriptors. The descriptor
- // will be automatically removed from the kqueue set when it is closed.
- descriptor_data->shutdown_ = true;
-
- op_queue<operation> ops;
- for (int i = 0; i < max_ops; ++i)
- {
- while (reactor_op* op = descriptor_data->op_queue_[i].front())
- {
- op->ec_ = asio::error::operation_aborted;
- descriptor_data->op_queue_[i].pop();
- ops.push(op);
- }
- }
-
- descriptor_lock.unlock();
-
- registered_descriptors_.erase(descriptor);
-
- descriptors_lock.unlock();
-
- io_service_.post_deferred_completions(ops);
- }
+ ASIO_DECL void close_descriptor(socket_type descriptor,
+ per_descriptor_data& descriptor_data);
// Add a new timer queue to the reactor.
template <typename Time_Traits>
- void add_timer_queue(timer_queue<Time_Traits>& timer_queue)
- {
- mutex::scoped_lock lock(mutex_);
- timer_queues_.insert(&timer_queue);
- }
+ void add_timer_queue(timer_queue<Time_Traits>& queue);
// Remove a timer queue from the reactor.
template <typename Time_Traits>
- void remove_timer_queue(timer_queue<Time_Traits>& timer_queue)
- {
- mutex::scoped_lock lock(mutex_);
- timer_queues_.erase(&timer_queue);
- }
+ void remove_timer_queue(timer_queue<Time_Traits>& queue);
// Schedule a new operation in the given timer queue to expire at the
// specified absolute time.
template <typename Time_Traits>
- void schedule_timer(timer_queue<Time_Traits>& timer_queue,
- const typename Time_Traits::time_type& time, timer_op* op, void* token)
- {
- mutex::scoped_lock lock(mutex_);
- if (!shutdown_)
- {
- bool earliest = timer_queue.enqueue_timer(time, op, token);
- io_service_.work_started();
- if (earliest)
- interrupt();
- }
- }
+ void schedule_timer(timer_queue<Time_Traits>& queue,
+ const typename Time_Traits::time_type& time,
+ typename timer_queue<Time_Traits>::per_timer_data& timer, timer_op* op);
// Cancel the timer operations associated with the given token. Returns the
// number of operations that have been posted or dispatched.
template <typename Time_Traits>
- std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token)
- {
- mutex::scoped_lock lock(mutex_);
- op_queue<operation> ops;
- std::size_t n = timer_queue.cancel_timer(token, ops);
- lock.unlock();
- io_service_.post_deferred_completions(ops);
- return n;
- }
+ std::size_t cancel_timer(timer_queue<Time_Traits>& queue,
+ typename timer_queue<Time_Traits>::per_timer_data& timer);
// Run the kqueue loop.
- void run(bool block, op_queue<operation>& ops)
- {
- mutex::scoped_lock lock(mutex_);
-
- // Determine how long to block while waiting for events.
- timespec timeout_buf = { 0, 0 };
- timespec* timeout = block ? get_timeout(timeout_buf) : &timeout_buf;
-
- lock.unlock();
-
- // Block on the kqueue descriptor.
- struct kevent events[128];
- int num_events = kevent(kqueue_fd_, 0, 0, events, 128, timeout);
-
- // Dispatch the waiting events.
- for (int i = 0; i < num_events; ++i)
- {
- int descriptor = events[i].ident;
- void* ptr = events[i].udata;
- if (ptr == &interrupter_)
- {
- // No need to reset the interrupter since we're leaving the descriptor
- // in a ready-to-read state and relying on one-shot notifications.
- }
- else
- {
- descriptor_state* descriptor_data = static_cast<descriptor_state*>(ptr);
- mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
-
- // Exception operations must be processed first to ensure that any
- // out-of-band data is read before normal data.
- static const int filter[max_ops] =
- { EVFILT_READ, EVFILT_WRITE, EVFILT_READ };
- for (int j = max_ops - 1; j >= 0; --j)
- {
- if (events[i].filter == filter[j])
- {
- if (j != except_op || events[i].flags & EV_OOBAND)
- {
- while (reactor_op* op = descriptor_data->op_queue_[j].front())
- {
- if (events[i].flags & EV_ERROR)
- {
- op->ec_ = asio::error_code(events[i].data,
- asio::error::get_system_category());
- descriptor_data->op_queue_[j].pop();
- ops.push(op);
- }
- if (op->perform())
- {
- descriptor_data->op_queue_[j].pop();
- ops.push(op);
- }
- else
- break;
- }
- }
- }
- }
-
- // Renew registration for event notifications.
- struct kevent event;
- switch (events[i].filter)
- {
- case EVFILT_READ:
- if (!descriptor_data->op_queue_[read_op].empty())
- EV_SET(&event, descriptor, EVFILT_READ,
- EV_ADD | EV_ONESHOT, 0, 0, descriptor_data);
- else if (!descriptor_data->op_queue_[except_op].empty())
- EV_SET(&event, descriptor, EVFILT_READ,
- EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data);
- else
- continue;
- case EVFILT_WRITE:
- if (!descriptor_data->op_queue_[write_op].empty())
- EV_SET(&event, descriptor, EVFILT_WRITE,
- EV_ADD | EV_ONESHOT, 0, 0, descriptor_data);
- else
- continue;
- default:
- break;
- }
- if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1)
- {
- asio::error_code error(errno,
- asio::error::get_system_category());
- for (int j = 0; j < max_ops; ++j)
- {
- while (reactor_op* op = descriptor_data->op_queue_[j].front())
- {
- op->ec_ = error;
- descriptor_data->op_queue_[j].pop();
- ops.push(op);
- }
- }
- }
- }
- }
-
- lock.lock();
- timer_queues_.get_ready_timers(ops);
- }
+ ASIO_DECL void run(bool block, op_queue<operation>& ops);
// Interrupt the kqueue loop.
- void interrupt()
- {
- struct kevent event;
- EV_SET(&event, interrupter_.read_descriptor(),
- EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, &interrupter_);
- ::kevent(kqueue_fd_, &event, 1, 0, 0, 0);
- }
+ ASIO_DECL void interrupt();
private:
// Create the kqueue file descriptor. Throws an exception if the descriptor
// cannot be created.
- static int do_kqueue_create()
- {
- int fd = kqueue();
- if (fd == -1)
- {
- boost::throw_exception(
- asio::system_error(
- asio::error_code(errno,
- asio::error::get_system_category()),
- "kqueue"));
- }
- return fd;
- }
+ ASIO_DECL static int do_kqueue_create();
+
+ // Helper function to add a new timer queue.
+ ASIO_DECL void do_add_timer_queue(timer_queue_base& queue);
+
+ // Helper function to remove a timer queue.
+ ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue);
// Get the timeout value for the kevent call.
- timespec* get_timeout(timespec& ts)
- {
- // By default we will wait no longer than 5 minutes. This will ensure that
- // any changes to the system clock are detected after no longer than this.
- long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000);
- ts.tv_sec = usec / 1000000;
- ts.tv_nsec = (usec % 1000000) * 1000;
- return &ts;
- }
+ ASIO_DECL timespec* get_timeout(timespec& ts);
// The io_service implementation used to post completions.
io_service_impl& io_service_;
@@ -464,22 +172,20 @@ private:
// Mutex to protect access to the registered descriptors.
mutex registered_descriptors_mutex_;
- // Keep track of all registered descriptors. This code relies on the fact that
- // the hash_map implementation pools deleted nodes, meaning that we can assume
- // our descriptor_state pointer remains valid even after the entry is removed.
- // Technically this is not true for C++98, as that standard says that spliced
- // elements in a list are invalidated. However, C++0x fixes this shortcoming
- // so we'll just assume that C++98 std::list implementations will do the right
- // thing anyway.
- typedef detail::hash_map<socket_type, descriptor_state> descriptor_map;
- descriptor_map registered_descriptors_;
+ // Keep track of all registered descriptors.
+ object_pool<descriptor_state> registered_descriptors_;
};
} // namespace detail
} // namespace asio
-#endif // defined(ASIO_HAS_KQUEUE)
-
#include "asio/detail/pop_options.hpp"
+#include "asio/detail/impl/kqueue_reactor.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/kqueue_reactor.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(ASIO_HAS_KQUEUE)
+
#endif // ASIO_DETAIL_KQUEUE_REACTOR_HPP
diff --git a/ext/asio/asio/detail/kqueue_reactor_fwd.hpp b/ext/asio/asio/detail/kqueue_reactor_fwd.hpp
index abbc0c7..fda59ce 100644
--- a/ext/asio/asio/detail/kqueue_reactor_fwd.hpp
+++ b/ext/asio/asio/detail/kqueue_reactor_fwd.hpp
@@ -1,8 +1,8 @@
//
-// kqueue_reactor_fwd.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~
+// detail/kqueue_reactor_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
@@ -16,15 +16,9 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#if !defined(ASIO_DISABLE_KQUEUE)
-
-#if (defined(__MACH__) && defined(__APPLE__)) \
- || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
-
-// Define this to indicate that kqueue is supported on the target platform.
-#define ASIO_HAS_KQUEUE 1
+#if defined(ASIO_HAS_KQUEUE)
namespace asio {
namespace detail {
@@ -34,11 +28,6 @@ class kqueue_reactor;
} // namespace detail
} // namespace asio
-#endif // (defined(__MACH__) && defined(__APPLE__))
- // || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
-
-#endif // !defined(ASIO_DISABLE_KQUEUE)
-
-#include "asio/detail/pop_options.hpp"
+#endif // defined(ASIO_HAS_KQUEUE)
#endif // ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP
diff --git a/ext/asio/asio/detail/local_free_on_block_exit.hpp b/ext/asio/asio/detail/local_free_on_block_exit.hpp
index 554943c..a55bd4b 100644
--- a/ext/asio/asio/detail/local_free_on_block_exit.hpp
+++ b/ext/asio/asio/detail/local_free_on_block_exit.hpp
@@ -1,8 +1,8 @@
//
-// local_free_on_block_exit.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/local_free_on_block_exit.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/socket_types.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -52,8 +50,8 @@ private:
} // namespace detail
} // namespace asio
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-
#include "asio/detail/pop_options.hpp"
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
#endif // ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP
diff --git a/ext/asio/asio/detail/macos_fenced_block.hpp b/ext/asio/asio/detail/macos_fenced_block.hpp
index 3c303d6..fcb8bfc 100644
--- a/ext/asio/asio/detail/macos_fenced_block.hpp
+++ b/ext/asio/asio/detail/macos_fenced_block.hpp
@@ -1,8 +1,8 @@
//
-// macos_fenced_block.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~
+// detail/macos_fenced_block.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if defined(__MACH__) && defined(__APPLE__)
-#include "asio/detail/push_options.hpp"
#include <libkern/OSAtomic.h>
-#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
@@ -50,8 +46,8 @@ public:
} // namespace detail
} // namespace asio
-#endif // defined(__MACH__) && defined(__APPLE__)
-
#include "asio/detail/pop_options.hpp"
+#endif // defined(__MACH__) && defined(__APPLE__)
+
#endif // ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP
diff --git a/ext/asio/asio/detail/mutex.hpp b/ext/asio/asio/detail/mutex.hpp
index 024ec7f..dc478ff 100644
--- a/ext/asio/asio/detail/mutex.hpp
+++ b/ext/asio/asio/detail/mutex.hpp
@@ -1,8 +1,8 @@
//
-// mutex.hpp
-// ~~~~~~~~~
+// detail/mutex.hpp
+// ~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,11 +15,7 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
# include "asio/detail/null_mutex.hpp"
@@ -45,6 +41,4 @@ typedef posix_mutex mutex;
} // namespace detail
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_MUTEX_HPP
diff --git a/ext/asio/asio/detail/noncopyable.hpp b/ext/asio/asio/detail/noncopyable.hpp
index 8b73ff0..e7f6437 100644
--- a/ext/asio/asio/detail/noncopyable.hpp
+++ b/ext/asio/asio/detail/noncopyable.hpp
@@ -1,8 +1,8 @@
//
-// noncopyable.hpp
-// ~~~~~~~~~~~~~~~
+// detail/noncopyable.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,13 +15,11 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
+#include "asio/detail/config.hpp"
#include <boost/noncopyable.hpp>
#include <boost/detail/workaround.hpp>
-#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
diff --git a/ext/asio/asio/detail/null_event.hpp b/ext/asio/asio/detail/null_event.hpp
index bcea31b..db7b747 100644
--- a/ext/asio/asio/detail/null_event.hpp
+++ b/ext/asio/asio/detail/null_event.hpp
@@ -1,8 +1,8 @@
//
-// null_event.hpp
-// ~~~~~~~~~~~~~~
+// detail/null_event.hpp
+// ~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -70,8 +68,8 @@ public:
} // namespace detail
} // namespace asio
-#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
-
#include "asio/detail/pop_options.hpp"
+#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
+
#endif // ASIO_DETAIL_NULL_EVENT_HPP
diff --git a/ext/asio/asio/detail/null_fenced_block.hpp b/ext/asio/asio/detail/null_fenced_block.hpp
index dd9a095..fbd025f 100644
--- a/ext/asio/asio/detail/null_fenced_block.hpp
+++ b/ext/asio/asio/detail/null_fenced_block.hpp
@@ -1,8 +1,8 @@
//
-// null_fenced_block.hpp
-// ~~~~~~~~~~~~~~~~~~~~~
+// detail/null_fenced_block.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/ext/asio/asio/detail/null_mutex.hpp b/ext/asio/asio/detail/null_mutex.hpp
index 6661ef8..bbceb7c 100644
--- a/ext/asio/asio/detail/null_mutex.hpp
+++ b/ext/asio/asio/detail/null_mutex.hpp
@@ -1,8 +1,8 @@
//
-// null_mutex.hpp
-// ~~~~~~~~~~~~~~
+// detail/null_mutex.hpp
+// ~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/scoped_lock.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -59,8 +57,8 @@ public:
} // namespace detail
} // namespace asio
-#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
-
#include "asio/detail/pop_options.hpp"
+#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
+
#endif // ASIO_DETAIL_NULL_MUTEX_HPP
diff --git a/ext/asio/asio/detail/null_signal_blocker.hpp b/ext/asio/asio/detail/null_signal_blocker.hpp
index a5db315..e0d996f 100644
--- a/ext/asio/asio/detail/null_signal_blocker.hpp
+++ b/ext/asio/asio/detail/null_signal_blocker.hpp
@@ -1,8 +1,8 @@
//
-// null_signal_blocker.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~
+// detail/null_signal_blocker.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,18 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
-#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
+#if !defined(BOOST_HAS_THREADS) \
+ || defined(ASIO_DISABLE_THREADS) \
+ || defined(BOOST_WINDOWS) \
+ || defined(__CYGWIN__) \
+ || defined(__SYMBIAN32__)
#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -56,8 +58,12 @@ public:
} // namespace detail
} // namespace asio
-#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
-
#include "asio/detail/pop_options.hpp"
+#endif // !defined(BOOST_HAS_THREADS)
+ // || defined(ASIO_DISABLE_THREADS)
+ // || defined(BOOST_WINDOWS)
+ // || defined(__CYGWIN__)
+ // || defined(__SYMBIAN32__)
+
#endif // ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP
diff --git a/ext/asio/asio/detail/null_thread.hpp b/ext/asio/asio/detail/null_thread.hpp
index ce3d470..bfe918e 100644
--- a/ext/asio/asio/detail/null_thread.hpp
+++ b/ext/asio/asio/detail/null_thread.hpp
@@ -1,8 +1,8 @@
//
-// null_thread.hpp
-// ~~~~~~~~~~~~~~~
+// detail/null_thread.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,21 +15,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
-#include "asio/detail/push_options.hpp"
-#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error.hpp"
-#include "asio/system_error.hpp"
#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
@@ -40,11 +34,10 @@ class null_thread
public:
// Constructor.
template <typename Function>
- null_thread(Function )
+ null_thread(Function, unsigned int = 0)
{
- asio::system_error e(
+ asio::detail::throw_error(
asio::error::operation_not_supported, "thread");
- boost::throw_exception(e);
}
// Destructor.
@@ -61,8 +54,8 @@ public:
} // namespace detail
} // namespace asio
-#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
-
#include "asio/detail/pop_options.hpp"
+#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
+
#endif // ASIO_DETAIL_NULL_THREAD_HPP
diff --git a/ext/asio/asio/detail/null_tss_ptr.hpp b/ext/asio/asio/detail/null_tss_ptr.hpp
index 112b476..d3456e6 100644
--- a/ext/asio/asio/detail/null_tss_ptr.hpp
+++ b/ext/asio/asio/detail/null_tss_ptr.hpp
@@ -1,8 +1,8 @@
//
-// null_tss_ptr.hpp
-// ~~~~~~~~~~~~~~~~
+// detail/null_tss_ptr.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -63,8 +61,8 @@ private:
} // namespace detail
} // namespace asio
-#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
-
#include "asio/detail/pop_options.hpp"
+#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
+
#endif // ASIO_DETAIL_NULL_TSS_PTR_HPP
diff --git a/ext/asio/asio/detail/object_pool.hpp b/ext/asio/asio/detail/object_pool.hpp
new file mode 100644
index 0000000..dc50feb
--- /dev/null
+++ b/ext/asio/asio/detail/object_pool.hpp
@@ -0,0 +1,146 @@
+//
+// detail/object_pool.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_OBJECT_POOL_HPP
+#define ASIO_DETAIL_OBJECT_POOL_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/noncopyable.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Object>
+class object_pool;
+
+class object_pool_access
+{
+public:
+ template <typename Object>
+ static Object* create()
+ {
+ return new Object;
+ }
+
+ template <typename Object>
+ static void destroy(Object* o)
+ {
+ delete o;
+ }
+
+ template <typename Object>
+ static Object*& next(Object* o)
+ {
+ return o->next_;
+ }
+
+ template <typename Object>
+ static Object*& prev(Object* o)
+ {
+ return o->prev_;
+ }
+};
+
+template <typename Object>
+class object_pool
+ : private noncopyable
+{
+public:
+ // Constructor.
+ object_pool()
+ : live_list_(0),
+ free_list_(0)
+ {
+ }
+
+ // Destructor destroys all objects.
+ ~object_pool()
+ {
+ destroy_list(live_list_);
+ destroy_list(free_list_);
+ }
+
+ // Get the object at the start of the live list.
+ Object* first()
+ {
+ return live_list_;
+ }
+
+ // Allocate a new object.
+ Object* alloc()
+ {
+ Object* o = free_list_;
+ if (o)
+ free_list_ = object_pool_access::next(free_list_);
+ else
+ o = object_pool_access::create<Object>();
+
+ object_pool_access::next(o) = live_list_;
+ object_pool_access::prev(o) = 0;
+ if (live_list_)
+ object_pool_access::prev(live_list_) = o;
+ live_list_ = o;
+
+ return o;
+ }
+
+ // Free an object. Moves it to the free list. No destructors are run.
+ void free(Object* o)
+ {
+ if (live_list_ == o)
+ live_list_ = object_pool_access::next(o);
+
+ if (object_pool_access::prev(o))
+ {
+ object_pool_access::next(object_pool_access::prev(o))
+ = object_pool_access::next(o);
+ }
+
+ if (object_pool_access::next(o))
+ {
+ object_pool_access::prev(object_pool_access::next(o))
+ = object_pool_access::prev(o);
+ }
+
+ object_pool_access::next(o) = free_list_;
+ object_pool_access::prev(o) = 0;
+ free_list_ = o;
+ }
+
+private:
+ // Helper function to destroy all elements in a list.
+ void destroy_list(Object* list)
+ {
+ while (list)
+ {
+ Object* o = list;
+ list = object_pool_access::next(o);
+ object_pool_access::destroy(o);
+ }
+ }
+
+ // The list of live objects.
+ Object* live_list_;
+
+ // The free list.
+ Object* free_list_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_OBJECT_POOL_HPP
diff --git a/ext/asio/asio/detail/old_win_sdk_compat.hpp b/ext/asio/asio/detail/old_win_sdk_compat.hpp
index 70e5916..fb75579 100644
--- a/ext/asio/asio/detail/old_win_sdk_compat.hpp
+++ b/ext/asio/asio/detail/old_win_sdk_compat.hpp
@@ -1,8 +1,8 @@
//
-// old_win_sdk_compat.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~
+// detail/old_win_sdk_compat.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,11 +15,7 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
@@ -36,6 +32,8 @@
// a recent (i.e. Vista or later) SDK, as the SDK does not provide IPv6 support
// in that case.
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -321,6 +319,8 @@ inline int IN6_IS_ADDR_MC_GLOBAL(const in6_addr_emulation* a)
} // namespace detail
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#endif // defined(ASIO_HAS_OLD_WIN_SDK)
// Even newer Platform SDKs that support IPv6 may not define IPV6_V6ONLY.
@@ -335,6 +335,4 @@ inline int IN6_IS_ADDR_MC_GLOBAL(const in6_addr_emulation* a)
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP
diff --git a/ext/asio/asio/detail/op_queue.hpp b/ext/asio/asio/detail/op_queue.hpp
index ccf8b9a..38278ea 100644
--- a/ext/asio/asio/detail/op_queue.hpp
+++ b/ext/asio/asio/detail/op_queue.hpp
@@ -1,8 +1,8 @@
//
-// op_queue.hpp
-// ~~~~~~~~~~~~
+// detail/op_queue.hpp
+// ~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,10 +15,10 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
diff --git a/ext/asio/asio/detail/operation.hpp b/ext/asio/asio/detail/operation.hpp
index 6aba361..3435c05 100644
--- a/ext/asio/asio/detail/operation.hpp
+++ b/ext/asio/asio/detail/operation.hpp
@@ -1,8 +1,8 @@
//
-// operation.hpp
-// ~~~~~~~~~~~~~
+// detail/operation.hpp
+// ~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,14 +15,11 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/win_iocp_io_service_fwd.hpp"
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_IOCP)
# include "asio/detail/win_iocp_operation.hpp"
#else
-# include "asio/detail/reactor_fwd.hpp"
# include "asio/detail/task_io_service_operation.hpp"
#endif
@@ -32,12 +29,10 @@ namespace detail {
#if defined(ASIO_HAS_IOCP)
typedef win_iocp_operation operation;
#else
-typedef task_io_service_operation<reactor> operation;
+typedef task_io_service_operation operation;
#endif
} // namespace detail
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_OPERATION_HPP
diff --git a/ext/asio/asio/detail/pipe_select_interrupter.hpp b/ext/asio/asio/detail/pipe_select_interrupter.hpp
index 7469599..bf39426 100644
--- a/ext/asio/asio/detail/pipe_select_interrupter.hpp
+++ b/ext/asio/asio/detail/pipe_select_interrupter.hpp
@@ -1,8 +1,8 @@
//
-// pipe_select_interrupter.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/pipe_select_interrupter.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,22 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+#if !defined(BOOST_WINDOWS)
+#if !defined(__CYGWIN__)
+#if !defined(__SYMBIAN32__)
+#if !defined(ASIO_HAS_EVENTFD)
#include "asio/detail/push_options.hpp"
-#include <fcntl.h>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error.hpp"
-#include "asio/system_error.hpp"
-#include "asio/detail/socket_types.hpp"
namespace asio {
namespace detail {
@@ -39,57 +31,16 @@ class pipe_select_interrupter
{
public:
// Constructor.
- pipe_select_interrupter()
- {
- int pipe_fds[2];
- if (pipe(pipe_fds) == 0)
- {
- read_descriptor_ = pipe_fds[0];
- ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK);
- write_descriptor_ = pipe_fds[1];
- ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK);
- }
- else
- {
- asio::error_code ec(errno,
- asio::error::get_system_category());
- asio::system_error e(ec, "pipe_select_interrupter");
- boost::throw_exception(e);
- }
- }
+ ASIO_DECL pipe_select_interrupter();
// Destructor.
- ~pipe_select_interrupter()
- {
- if (read_descriptor_ != -1)
- ::close(read_descriptor_);
- if (write_descriptor_ != -1)
- ::close(write_descriptor_);
- }
+ ASIO_DECL ~pipe_select_interrupter();
// Interrupt the select call.
- void interrupt()
- {
- char byte = 0;
- int result = ::write(write_descriptor_, &byte, 1);
- (void)result;
- }
+ ASIO_DECL void interrupt();
// Reset the select interrupt. Returns true if the call was interrupted.
- bool reset()
- {
- for (;;)
- {
- char data[1024];
- int bytes_read = ::read(read_descriptor_, data, sizeof(data));
- if (bytes_read < 0 && errno == EINTR)
- continue;
- bool was_interrupted = (bytes_read > 0);
- while (bytes_read == sizeof(data))
- bytes_read = ::read(read_descriptor_, data, sizeof(data));
- return was_interrupted;
- }
- }
+ ASIO_DECL bool reset();
// Get the read descriptor to be passed to select.
int read_descriptor() const
@@ -113,8 +64,15 @@ private:
} // namespace detail
} // namespace asio
-#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
-
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/pipe_select_interrupter.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // !defined(ASIO_HAS_EVENTFD)
+#endif // !defined(__SYMBIAN32__)
+#endif // !defined(__CYGWIN__)
+#endif // !defined(BOOST_WINDOWS)
+
#endif // ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP
diff --git a/ext/asio/asio/detail/pop_options.hpp b/ext/asio/asio/detail/pop_options.hpp
index a26b203..4e19336 100644
--- a/ext/asio/asio/detail/pop_options.hpp
+++ b/ext/asio/asio/detail/pop_options.hpp
@@ -1,8 +1,8 @@
//
-// pop_options.hpp
-// ~~~~~~~~~~~~~~~
+// detail/pop_options.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -31,6 +31,16 @@
# pragma pack (pop)
# endif
+# if defined(__OBJC__)
+# if !defined(__APPLE_CC__) || (__APPLE_CC__ <= 1)
+# if defined(ASIO_OBJC_WORKAROUND)
+# undef Protocol
+# undef id
+# undef ASIO_OBJC_WORKAROUND
+# endif
+# endif
+# endif
+
#elif defined(__KCC)
// Kai C++
diff --git a/ext/asio/asio/detail/posix_event.hpp b/ext/asio/asio/detail/posix_event.hpp
index 49c15aa..d0ec8b0 100644
--- a/ext/asio/asio/detail/posix_event.hpp
+++ b/ext/asio/asio/detail/posix_event.hpp
@@ -1,8 +1,8 @@
//
-// posix_event.hpp
-// ~~~~~~~~~~~~~~~
+// detail/posix_event.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,24 +15,16 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
-#if defined(BOOST_HAS_PTHREADS)
+#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
-#include "asio/detail/push_options.hpp"
#include <boost/assert.hpp>
-#include <boost/throw_exception.hpp>
#include <pthread.h>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error.hpp"
-#include "asio/system_error.hpp"
#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -41,19 +33,7 @@ class posix_event
{
public:
// Constructor.
- posix_event()
- : signalled_(false)
- {
- int error = ::pthread_cond_init(&cond_, 0);
- if (error != 0)
- {
- asio::system_error e(
- asio::error_code(error,
- asio::error::get_system_category()),
- "event");
- boost::throw_exception(e);
- }
- }
+ ASIO_DECL posix_event();
// Destructor.
~posix_event()
@@ -107,8 +87,12 @@ private:
} // namespace detail
} // namespace asio
-#endif // defined(BOOST_HAS_PTHREADS)
-
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/posix_event.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
+
#endif // ASIO_DETAIL_POSIX_EVENT_HPP
diff --git a/ext/asio/asio/detail/posix_fd_set_adapter.hpp b/ext/asio/asio/detail/posix_fd_set_adapter.hpp
index 17ef269..92d6bba 100644
--- a/ext/asio/asio/detail/posix_fd_set_adapter.hpp
+++ b/ext/asio/asio/detail/posix_fd_set_adapter.hpp
@@ -1,8 +1,8 @@
//
-// posix_fd_set_adapter.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/posix_fd_set_adapter.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,15 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
-#include <cstring>
-#include "asio/detail/pop_options.hpp"
+#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+#include <cstring>
#include "asio/detail/socket_types.hpp"
-#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
@@ -74,8 +73,8 @@ private:
} // namespace detail
} // namespace asio
-#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
-
#include "asio/detail/pop_options.hpp"
+#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
#endif // ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP
diff --git a/ext/asio/asio/detail/posix_mutex.hpp b/ext/asio/asio/detail/posix_mutex.hpp
index 230b83a..bcdf6b2 100644
--- a/ext/asio/asio/detail/posix_mutex.hpp
+++ b/ext/asio/asio/detail/posix_mutex.hpp
@@ -1,8 +1,8 @@
//
-// posix_mutex.hpp
-// ~~~~~~~~~~~~~~~
+// detail/posix_mutex.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,24 +15,16 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
-#if defined(BOOST_HAS_PTHREADS)
+#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
-#include "asio/detail/push_options.hpp"
-#include <boost/throw_exception.hpp>
#include <pthread.h>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error.hpp"
-#include "asio/system_error.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/scoped_lock.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -45,18 +37,7 @@ public:
typedef asio::detail::scoped_lock<posix_mutex> scoped_lock;
// Constructor.
- posix_mutex()
- {
- int error = ::pthread_mutex_init(&mutex_, 0);
- if (error != 0)
- {
- asio::system_error e(
- asio::error_code(error,
- asio::error::get_system_category()),
- "mutex");
- boost::throw_exception(e);
- }
- }
+ ASIO_DECL posix_mutex();
// Destructor.
~posix_mutex()
@@ -84,8 +65,12 @@ private:
} // namespace detail
} // namespace asio
-#endif // defined(BOOST_HAS_PTHREADS)
-
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/posix_mutex.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
+
#endif // ASIO_DETAIL_POSIX_MUTEX_HPP
diff --git a/ext/asio/asio/detail/posix_signal_blocker.hpp b/ext/asio/asio/detail/posix_signal_blocker.hpp
index 135ca41..d41f128 100644
--- a/ext/asio/asio/detail/posix_signal_blocker.hpp
+++ b/ext/asio/asio/detail/posix_signal_blocker.hpp
@@ -1,8 +1,8 @@
//
-// posix_signal_blocker.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/posix_signal_blocker.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,22 +15,17 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
-#if defined(BOOST_HAS_PTHREADS)
+#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
-#include "asio/detail/push_options.hpp"
#include <csignal>
#include <pthread.h>
#include <signal.h>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -83,8 +78,8 @@ private:
} // namespace detail
} // namespace asio
-#endif // defined(BOOST_HAS_PTHREADS)
-
#include "asio/detail/pop_options.hpp"
+#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
+
#endif // ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP
diff --git a/ext/asio/asio/detail/posix_thread.hpp b/ext/asio/asio/detail/posix_thread.hpp
index e0fea75..743417c 100644
--- a/ext/asio/asio/detail/posix_thread.hpp
+++ b/ext/asio/asio/detail/posix_thread.hpp
@@ -1,8 +1,8 @@
//
-// posix_thread.hpp
-// ~~~~~~~~~~~~~~~~
+// detail/posix_thread.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,28 +15,22 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
-#if defined(BOOST_HAS_PTHREADS)
+#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
-#include "asio/detail/push_options.hpp"
-#include <memory>
-#include <boost/throw_exception.hpp>
#include <pthread.h>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error.hpp"
-#include "asio/system_error.hpp"
#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
-extern "C" void* asio_detail_posix_thread_function(void* arg);
+extern "C"
+{
+ ASIO_DECL void* asio_detail_posix_thread_function(void* arg);
+}
class posix_thread
: private noncopyable
@@ -47,36 +41,14 @@ public:
posix_thread(Function f)
: joined_(false)
{
- std::auto_ptr<func_base> arg(new func<Function>(f));
- int error = ::pthread_create(&thread_, 0,
- asio_detail_posix_thread_function, arg.get());
- if (error != 0)
- {
- asio::system_error e(
- asio::error_code(error,
- asio::error::get_system_category()),
- "thread");
- boost::throw_exception(e);
- }
- arg.release();
+ start_thread(new func<Function>(f));
}
// Destructor.
- ~posix_thread()
- {
- if (!joined_)
- ::pthread_detach(thread_);
- }
+ ASIO_DECL ~posix_thread();
// Wait for the thread to exit.
- void join()
- {
- if (!joined_)
- {
- ::pthread_join(thread_, 0);
- joined_ = true;
- }
- }
+ ASIO_DECL void join();
private:
friend void* asio_detail_posix_thread_function(void* arg);
@@ -88,6 +60,12 @@ private:
virtual void run() = 0;
};
+ struct auto_func_base_ptr
+ {
+ func_base* ptr;
+ ~auto_func_base_ptr() { delete ptr; }
+ };
+
template <typename Function>
class func
: public func_base
@@ -107,23 +85,21 @@ private:
Function f_;
};
+ ASIO_DECL void start_thread(func_base* arg);
+
::pthread_t thread_;
bool joined_;
};
-inline void* asio_detail_posix_thread_function(void* arg)
-{
- std::auto_ptr<posix_thread::func_base> f(
- static_cast<posix_thread::func_base*>(arg));
- f->run();
- return 0;
-}
-
} // namespace detail
} // namespace asio
-#endif // defined(BOOST_HAS_PTHREADS)
-
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/posix_thread.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
+
#endif // ASIO_DETAIL_POSIX_THREAD_HPP
diff --git a/ext/asio/asio/detail/posix_tss_ptr.hpp b/ext/asio/asio/detail/posix_tss_ptr.hpp
index 3b4ba07..30d9705 100644
--- a/ext/asio/asio/detail/posix_tss_ptr.hpp
+++ b/ext/asio/asio/detail/posix_tss_ptr.hpp
@@ -1,8 +1,8 @@
//
-// posix_tss_ptr.hpp
-// ~~~~~~~~~~~~~~~~~
+// detail/posix_tss_ptr.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,26 +15,21 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
-#if defined(BOOST_HAS_PTHREADS)
+#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
-#include "asio/detail/push_options.hpp"
-#include <boost/throw_exception.hpp>
#include <pthread.h>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error.hpp"
-#include "asio/system_error.hpp"
#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
+// Helper function to create thread-specific storage.
+ASIO_DECL void posix_tss_ptr_create(pthread_key_t& key);
+
template <typename T>
class posix_tss_ptr
: private noncopyable
@@ -43,15 +38,7 @@ public:
// Constructor.
posix_tss_ptr()
{
- int error = ::pthread_key_create(&tss_key_, 0);
- if (error != 0)
- {
- asio::system_error e(
- asio::error_code(error,
- asio::error::get_system_category()),
- "tss");
- boost::throw_exception(e);
- }
+ posix_tss_ptr_create(tss_key_);
}
// Destructor.
@@ -76,13 +63,18 @@ private:
// Thread-specific storage to allow unlocked access to determine whether a
// thread is a member of the pool.
pthread_key_t tss_key_;
+
};
} // namespace detail
} // namespace asio
-#endif // defined(BOOST_HAS_PTHREADS)
-
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/posix_tss_ptr.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS)
+
#endif // ASIO_DETAIL_POSIX_TSS_PTR_HPP
diff --git a/ext/asio/asio/detail/push_options.hpp b/ext/asio/asio/detail/push_options.hpp
index cb0e902..ebb3276 100644
--- a/ext/asio/asio/detail/push_options.hpp
+++ b/ext/asio/asio/detail/push_options.hpp
@@ -1,8 +1,8 @@
//
-// push_options.hpp
-// ~~~~~~~~~~~~~~~~
+// detail/push_options.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -31,6 +31,18 @@
# pragma pack (push, 8)
# endif
+# if defined(__OBJC__)
+# if !defined(__APPLE_CC__) || (__APPLE_CC__ <= 1)
+# if !defined(ASIO_DISABLE_OBJC_WORKAROUND)
+# if !defined(Protocol) && !defined(id)
+# define Protocol cpp_Protocol
+# define id cpp_id
+# define ASIO_OBJC_WORKAROUND
+# endif
+# endif
+# endif
+# endif
+
#elif defined(__KCC)
// Kai C++
diff --git a/ext/asio/asio/detail/reactive_descriptor_service.hpp b/ext/asio/asio/detail/reactive_descriptor_service.hpp
index 7ad368d..f7e8bf5 100644
--- a/ext/asio/asio/detail/reactive_descriptor_service.hpp
+++ b/ext/asio/asio/detail/reactive_descriptor_service.hpp
@@ -1,8 +1,8 @@
//
-// reactive_descriptor_service.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/reactive_descriptor_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,21 +15,24 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+
+#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+#include <boost/utility/addressof.hpp>
#include "asio/buffer.hpp"
-#include "asio/error.hpp"
#include "asio/io_service.hpp"
#include "asio/detail/bind_handler.hpp"
#include "asio/detail/buffer_sequence_adapter.hpp"
#include "asio/detail/descriptor_ops.hpp"
+#include "asio/detail/descriptor_read_op.hpp"
+#include "asio/detail/descriptor_write_op.hpp"
#include "asio/detail/fenced_block.hpp"
#include "asio/detail/noncopyable.hpp"
-#include "asio/detail/null_buffers_op.hpp"
+#include "asio/detail/reactive_null_buffers_op.hpp"
#include "asio/detail/reactor.hpp"
-#include "asio/detail/reactor_op.hpp"
-#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
@@ -48,7 +51,7 @@ public:
// Default constructor.
implementation_type()
: descriptor_(-1),
- flags_(0)
+ state_(0)
{
}
@@ -59,94 +62,29 @@ public:
// The native descriptor representation.
int descriptor_;
- enum
- {
- // The user wants a non-blocking descriptor.
- user_set_non_blocking = 1,
-
- // The descriptor has been set non-blocking.
- internal_non_blocking = 2,
-
- // Helper "flag" used to determine whether the descriptor is non-blocking.
- non_blocking = user_set_non_blocking | internal_non_blocking
- };
-
- // Flags indicating the current state of the descriptor.
- unsigned char flags_;
+ // The current state of the descriptor.
+ descriptor_ops::state_type state_;
// Per-descriptor data used by the reactor.
reactor::per_descriptor_data reactor_data_;
};
- // The maximum number of buffers to support in a single operation.
- enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len };
-
// Constructor.
- reactive_descriptor_service(asio::io_service& io_service)
- : io_service_impl_(asio::use_service<io_service_impl>(io_service)),
- reactor_(asio::use_service<reactor>(io_service))
- {
- reactor_.init_task();
- }
+ ASIO_DECL reactive_descriptor_service(
+ asio::io_service& io_service);
// Destroy all user-defined handler objects owned by the service.
- void shutdown_service()
- {
- }
+ ASIO_DECL void shutdown_service();
// Construct a new descriptor implementation.
- void construct(implementation_type& impl)
- {
- impl.descriptor_ = -1;
- impl.flags_ = 0;
- }
+ ASIO_DECL void construct(implementation_type& impl);
// Destroy a descriptor implementation.
- void destroy(implementation_type& impl)
- {
- if (impl.descriptor_ != -1)
- {
- reactor_.close_descriptor(impl.descriptor_, impl.reactor_data_);
-
- if (impl.flags_ & implementation_type::internal_non_blocking)
- {
- ioctl_arg_type non_blocking = 0;
- asio::error_code ignored_ec;
- descriptor_ops::ioctl(impl.descriptor_,
- FIONBIO, &non_blocking, ignored_ec);
- impl.flags_ &= ~implementation_type::internal_non_blocking;
- }
-
- asio::error_code ignored_ec;
- descriptor_ops::close(impl.descriptor_, ignored_ec);
-
- impl.descriptor_ = -1;
- }
- }
+ ASIO_DECL void destroy(implementation_type& impl);
// Assign a native descriptor to a descriptor implementation.
- asio::error_code assign(implementation_type& impl,
- const native_type& native_descriptor, asio::error_code& ec)
- {
- if (is_open(impl))
- {
- ec = asio::error::already_open;
- return ec;
- }
-
- if (int err = reactor_.register_descriptor(
- native_descriptor, impl.reactor_data_))
- {
- ec = asio::error_code(err,
- asio::error::get_system_category());
- return ec;
- }
-
- impl.descriptor_ = native_descriptor;
- impl.flags_ = 0;
- ec = asio::error_code();
- return ec;
- }
+ ASIO_DECL asio::error_code assign(implementation_type& impl,
+ const native_type& native_descriptor, asio::error_code& ec);
// Determine whether the descriptor is open.
bool is_open(const implementation_type& impl) const
@@ -155,31 +93,8 @@ public:
}
// Destroy a descriptor implementation.
- asio::error_code close(implementation_type& impl,
- asio::error_code& ec)
- {
- if (is_open(impl))
- {
- reactor_.close_descriptor(impl.descriptor_, impl.reactor_data_);
-
- if (impl.flags_ & implementation_type::internal_non_blocking)
- {
- ioctl_arg_type non_blocking = 0;
- asio::error_code ignored_ec;
- descriptor_ops::ioctl(impl.descriptor_,
- FIONBIO, &non_blocking, ignored_ec);
- impl.flags_ &= ~implementation_type::internal_non_blocking;
- }
-
- if (descriptor_ops::close(impl.descriptor_, ec) == -1)
- return ec;
-
- impl.descriptor_ = -1;
- }
-
- ec = asio::error_code();
- return ec;
- }
+ ASIO_DECL asio::error_code close(implementation_type& impl,
+ asio::error_code& ec);
// Get the native descriptor representation.
native_type native(const implementation_type& impl) const
@@ -188,56 +103,16 @@ public:
}
// Cancel all operations associated with the descriptor.
- asio::error_code cancel(implementation_type& impl,
- asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
- reactor_.cancel_ops(impl.descriptor_, impl.reactor_data_);
- ec = asio::error_code();
- return ec;
- }
+ ASIO_DECL asio::error_code cancel(implementation_type& impl,
+ asio::error_code& ec);
// Perform an IO control command on the descriptor.
template <typename IO_Control_Command>
asio::error_code io_control(implementation_type& impl,
IO_Control_Command& command, asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
- descriptor_ops::ioctl(impl.descriptor_, command.name(),
- static_cast<ioctl_arg_type*>(command.data()), ec);
-
- // When updating the non-blocking mode we always perform the ioctl syscall,
- // even if the flags would otherwise indicate that the descriptor is
- // already in the correct state. This ensures that the underlying
- // descriptor is put into the state that has been requested by the user. If
- // the ioctl syscall was successful then we need to update the flags to
- // match.
- if (!ec && command.name() == static_cast<int>(FIONBIO))
- {
- if (*static_cast<ioctl_arg_type*>(command.data()))
- {
- impl.flags_ |= implementation_type::user_set_non_blocking;
- }
- else
- {
- // Clearing the non-blocking mode always overrides any internally-set
- // non-blocking flag. Any subsequent asynchronous operations will need
- // to re-enable non-blocking I/O.
- impl.flags_ &= ~(implementation_type::user_set_non_blocking
- | implementation_type::internal_non_blocking);
- }
- }
-
+ descriptor_ops::ioctl(impl.descriptor_, impl.state_,
+ command.name(), static_cast<ioctl_arg_type*>(command.data()), ec);
return ec;
}
@@ -246,148 +121,23 @@ public:
size_t write_some(implementation_type& impl,
const ConstBufferSequence& buffers, asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
buffer_sequence_adapter<asio::const_buffer,
ConstBufferSequence> bufs(buffers);
- // A request to read_some 0 bytes on a stream is a no-op.
- if (bufs.all_empty())
- {
- ec = asio::error_code();
- return 0;
- }
-
- // Send the data.
- for (;;)
- {
- // Try to complete the operation without blocking.
- int bytes_sent = descriptor_ops::gather_write(
- impl.descriptor_, bufs.buffers(), bufs.count(), ec);
-
- // Check if operation succeeded.
- if (bytes_sent >= 0)
- return bytes_sent;
-
- // Operation failed.
- if ((impl.flags_ & implementation_type::user_set_non_blocking)
- || (ec != asio::error::would_block
- && ec != asio::error::try_again))
- return 0;
-
- // Wait for descriptor to become ready.
- if (descriptor_ops::poll_write(impl.descriptor_, ec) < 0)
- return 0;
- }
+ return descriptor_ops::sync_write(impl.descriptor_, impl.state_,
+ bufs.buffers(), bufs.count(), bufs.all_empty(), ec);
}
// Wait until data can be written without blocking.
size_t write_some(implementation_type& impl,
const null_buffers&, asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
// Wait for descriptor to become ready.
descriptor_ops::poll_write(impl.descriptor_, ec);
return 0;
}
- template <typename ConstBufferSequence>
- class write_op_base : public reactor_op
- {
- public:
- write_op_base(int descriptor,
- const ConstBufferSequence& buffers, func_type complete_func)
- : reactor_op(&write_op_base::do_perform, complete_func),
- descriptor_(descriptor),
- buffers_(buffers)
- {
- }
-
- static bool do_perform(reactor_op* base)
- {
- write_op_base* o(static_cast<write_op_base*>(base));
-
- buffer_sequence_adapter<asio::const_buffer,
- ConstBufferSequence> bufs(o->buffers_);
-
- for (;;)
- {
- // Write the data.
- asio::error_code ec;
- int bytes = descriptor_ops::gather_write(
- o->descriptor_, bufs.buffers(), bufs.count(), ec);
-
- // Retry operation if interrupted by signal.
- if (ec == asio::error::interrupted)
- continue;
-
- // Check if we need to run the operation again.
- if (ec == asio::error::would_block
- || ec == asio::error::try_again)
- return false;
-
- o->ec_ = ec;
- o->bytes_transferred_ = (bytes < 0 ? 0 : bytes);
- return true;
- }
- }
-
- private:
- int descriptor_;
- ConstBufferSequence buffers_;
- };
-
- template <typename ConstBufferSequence, typename Handler>
- class write_op : public write_op_base<ConstBufferSequence>
- {
- public:
- write_op(int descriptor,
- const ConstBufferSequence& buffers, Handler handler)
- : write_op_base<ConstBufferSequence>(
- descriptor, buffers, &write_op::do_complete),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
- {
- // Take ownership of the handler object.
- write_op* o(static_cast<write_op*>(base));
- typedef handler_alloc_traits<Handler, write_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder2<Handler, asio::error_code, std::size_t>
- handler(o->handler_, o->ec_, o->bytes_transferred_);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- Handler handler_;
- };
-
// Start an asynchronous write. The data being sent must be valid for the
// lifetime of the asynchronous operation.
template <typename ConstBufferSequence, typename Handler>
@@ -395,15 +145,16 @@ public:
const ConstBufferSequence& buffers, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef write_op<ConstBufferSequence, Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, impl.descriptor_, buffers, handler);
+ typedef descriptor_write_op<ConstBufferSequence, Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl.descriptor_, buffers, handler);
- start_op(impl, reactor::write_op, ptr.get(), true,
+ start_op(impl, reactor::write_op, p.p, true,
buffer_sequence_adapter<asio::const_buffer,
ConstBufferSequence>::all_empty(buffers));
- ptr.release();
+ p.v = p.p = 0;
}
// Start an asynchronous wait until data can be written without blocking.
@@ -412,13 +163,14 @@ public:
const null_buffers&, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef null_buffers_op<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, handler);
+ typedef reactive_null_buffers_op<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(handler);
- start_op(impl, reactor::write_op, ptr.get(), false, false);
- ptr.release();
+ start_op(impl, reactor::write_op, p.p, false, false);
+ p.v = p.p = 0;
}
// Read some data from the stream. Returns the number of bytes read.
@@ -426,157 +178,23 @@ public:
size_t read_some(implementation_type& impl,
const MutableBufferSequence& buffers, asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
buffer_sequence_adapter<asio::mutable_buffer,
MutableBufferSequence> bufs(buffers);
- // A request to read_some 0 bytes on a stream is a no-op.
- if (bufs.all_empty())
- {
- ec = asio::error_code();
- return 0;
- }
-
- // Read some data.
- for (;;)
- {
- // Try to complete the operation without blocking.
- int bytes_read = descriptor_ops::scatter_read(
- impl.descriptor_, bufs.buffers(), bufs.count(), ec);
-
- // Check if operation succeeded.
- if (bytes_read > 0)
- return bytes_read;
-
- // Check for EOF.
- if (bytes_read == 0)
- {
- ec = asio::error::eof;
- return 0;
- }
-
- // Operation failed.
- if ((impl.flags_ & implementation_type::user_set_non_blocking)
- || (ec != asio::error::would_block
- && ec != asio::error::try_again))
- return 0;
-
- // Wait for descriptor to become ready.
- if (descriptor_ops::poll_read(impl.descriptor_, ec) < 0)
- return 0;
- }
+ return descriptor_ops::sync_read(impl.descriptor_, impl.state_,
+ bufs.buffers(), bufs.count(), bufs.all_empty(), ec);
}
// Wait until data can be read without blocking.
size_t read_some(implementation_type& impl,
const null_buffers&, asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
// Wait for descriptor to become ready.
descriptor_ops::poll_read(impl.descriptor_, ec);
return 0;
}
- template <typename MutableBufferSequence>
- class read_op_base : public reactor_op
- {
- public:
- read_op_base(int descriptor,
- const MutableBufferSequence& buffers, func_type complete_func)
- : reactor_op(&read_op_base::do_perform, complete_func),
- descriptor_(descriptor),
- buffers_(buffers)
- {
- }
-
- static bool do_perform(reactor_op* base)
- {
- read_op_base* o(static_cast<read_op_base*>(base));
-
- buffer_sequence_adapter<asio::mutable_buffer,
- MutableBufferSequence> bufs(o->buffers_);
-
- for (;;)
- {
- // Read some data.
- asio::error_code ec;
- int bytes = descriptor_ops::scatter_read(
- o->descriptor_, bufs.buffers(), bufs.count(), ec);
- if (bytes == 0)
- ec = asio::error::eof;
-
- // Retry operation if interrupted by signal.
- if (ec == asio::error::interrupted)
- continue;
-
- // Check if we need to run the operation again.
- if (ec == asio::error::would_block
- || ec == asio::error::try_again)
- return false;
-
- o->ec_ = ec;
- o->bytes_transferred_ = (bytes < 0 ? 0 : bytes);
- return true;
- }
- }
-
- private:
- int descriptor_;
- MutableBufferSequence buffers_;
- };
-
- template <typename MutableBufferSequence, typename Handler>
- class read_op : public read_op_base<MutableBufferSequence>
- {
- public:
- read_op(int descriptor,
- const MutableBufferSequence& buffers, Handler handler)
- : read_op_base<MutableBufferSequence>(
- descriptor, buffers, &read_op::do_complete),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
- {
- // Take ownership of the handler object.
- read_op* o(static_cast<read_op*>(base));
- typedef handler_alloc_traits<Handler, read_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder2<Handler, asio::error_code, std::size_t>
- handler(o->handler_, o->ec_, o->bytes_transferred_);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- Handler handler_;
- };
-
// Start an asynchronous read. The buffer for the data being read must be
// valid for the lifetime of the asynchronous operation.
template <typename MutableBufferSequence, typename Handler>
@@ -584,16 +202,16 @@ public:
const MutableBufferSequence& buffers, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef read_op<MutableBufferSequence, Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr,
- impl.descriptor_, buffers, handler);
+ typedef descriptor_read_op<MutableBufferSequence, Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl.descriptor_, buffers, handler);
- start_op(impl, reactor::read_op, ptr.get(), true,
+ start_op(impl, reactor::read_op, p.p, true,
buffer_sequence_adapter<asio::mutable_buffer,
MutableBufferSequence>::all_empty(buffers));
- ptr.release();
+ p.v = p.p = 0;
}
// Wait until data can be read without blocking.
@@ -602,57 +220,20 @@ public:
const null_buffers&, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef null_buffers_op<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, handler);
+ typedef reactive_null_buffers_op<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(handler);
- start_op(impl, reactor::read_op, ptr.get(), false, false);
- ptr.release();
+ start_op(impl, reactor::read_op, p.p, false, false);
+ p.v = p.p = 0;
}
private:
// Start the asynchronous operation.
- void start_op(implementation_type& impl, int op_type,
- reactor_op* op, bool non_blocking, bool noop)
- {
- if (!noop)
- {
- if (is_open(impl))
- {
- if (is_non_blocking(impl) || set_non_blocking(impl, op->ec_))
- {
- reactor_.start_op(op_type, impl.descriptor_,
- impl.reactor_data_, op, non_blocking);
- return;
- }
- }
- else
- op->ec_ = asio::error::bad_descriptor;
- }
-
- io_service_impl_.post_immediate_completion(op);
- }
-
- // Determine whether the descriptor has been set non-blocking.
- bool is_non_blocking(implementation_type& impl) const
- {
- return (impl.flags_ & implementation_type::non_blocking);
- }
-
- // Set the internal non-blocking flag.
- bool set_non_blocking(implementation_type& impl,
- asio::error_code& ec)
- {
- ioctl_arg_type non_blocking = 1;
- if (descriptor_ops::ioctl(impl.descriptor_, FIONBIO, &non_blocking, ec))
- return false;
- impl.flags_ |= implementation_type::internal_non_blocking;
- return true;
- }
-
- // The io_service implementation used to post completions.
- io_service_impl& io_service_impl_;
+ ASIO_DECL void start_op(implementation_type& impl, int op_type,
+ reactor_op* op, bool non_blocking, bool noop);
// The selector that performs event demultiplexing for the service.
reactor& reactor_;
@@ -661,8 +242,12 @@ private:
} // namespace detail
} // namespace asio
-#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
-
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/reactive_descriptor_service.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
#endif // ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP
diff --git a/ext/asio/asio/detail/reactive_null_buffers_op.hpp b/ext/asio/asio/detail/reactive_null_buffers_op.hpp
new file mode 100644
index 0000000..0c83c61
--- /dev/null
+++ b/ext/asio/asio/detail/reactive_null_buffers_op.hpp
@@ -0,0 +1,83 @@
+//
+// detail/reactive_null_buffers_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_REACTIVE_NULL_BUFFERS_OP_HPP
+#define ASIO_DETAIL_REACTIVE_NULL_BUFFERS_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/reactor_op.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Handler>
+class reactive_null_buffers_op : public reactor_op
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(reactive_null_buffers_op);
+
+ reactive_null_buffers_op(Handler handler)
+ : reactor_op(&reactive_null_buffers_op::do_perform,
+ &reactive_null_buffers_op::do_complete),
+ handler_(handler)
+ {
+ }
+
+ static bool do_perform(reactor_op*)
+ {
+ return true;
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
+ {
+ // Take ownership of the handler object.
+ reactive_null_buffers_op* o(static_cast<reactive_null_buffers_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder2<Handler, asio::error_code, std::size_t>
+ handler(o->handler_, o->ec_, o->bytes_transferred_);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_REACTIVE_NULL_BUFFERS_OP_HPP
diff --git a/ext/asio/asio/detail/reactive_serial_port_service.hpp b/ext/asio/asio/detail/reactive_serial_port_service.hpp
index 186460f..556ea06 100644
--- a/ext/asio/asio/detail/reactive_serial_port_service.hpp
+++ b/ext/asio/asio/detail/reactive_serial_port_service.hpp
@@ -1,8 +1,8 @@
//
-// reactive_serial_port_service.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/reactive_serial_port_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
// Copyright (c) 2008 Rep Invariant Systems, Inc. (info at repinvariant.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
@@ -16,23 +16,20 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <cstring>
-#include <string>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/serial_port_base.hpp"
-
-#if defined(ASIO_HAS_SERIAL_PORT) \
- && !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+#if defined(ASIO_HAS_SERIAL_PORT)
+#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+#include <string>
#include "asio/error.hpp"
#include "asio/io_service.hpp"
+#include "asio/serial_port_base.hpp"
#include "asio/detail/descriptor_ops.hpp"
#include "asio/detail/reactive_descriptor_service.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -46,119 +43,55 @@ public:
// The implementation type of the serial port.
typedef reactive_descriptor_service::implementation_type implementation_type;
- reactive_serial_port_service(asio::io_service& io_service)
- : descriptor_service_(io_service)
- {
- }
+ ASIO_DECL reactive_serial_port_service(
+ asio::io_service& io_service);
// Destroy all user-defined handler objects owned by the service.
- void shutdown_service()
- {
- descriptor_service_.shutdown_service();
- }
+ ASIO_DECL void shutdown_service();
- // Construct a new handle implementation.
+ // Construct a new serial port implementation.
void construct(implementation_type& impl)
{
descriptor_service_.construct(impl);
}
- // Destroy a handle implementation.
+ // Destroy a serial port implementation.
void destroy(implementation_type& impl)
{
descriptor_service_.destroy(impl);
}
// Open the serial port using the specified device name.
- asio::error_code open(implementation_type& impl,
- const std::string& device, asio::error_code& ec)
- {
- if (is_open(impl))
- {
- ec = asio::error::already_open;
- return ec;
- }
-
- int fd = descriptor_ops::open(device.c_str(),
- O_RDWR | O_NONBLOCK | O_NOCTTY, ec);
- if (fd < 0)
- return ec;
-
- int s = descriptor_ops::fcntl(fd, F_GETFL, ec);
- if (s >= 0)
- s = descriptor_ops::fcntl(fd, F_SETFL, s | O_NONBLOCK, ec);
- if (s < 0)
- {
- asio::error_code ignored_ec;
- descriptor_ops::close(fd, ignored_ec);
- return ec;
- }
-
- // Set up default serial port options.
- termios ios;
- descriptor_ops::clear_error(ec);
- s = descriptor_ops::error_wrapper(::tcgetattr(fd, &ios), ec);
- if (s >= 0)
- {
-#if defined(_BSD_SOURCE)
- ::cfmakeraw(&ios);
-#else
- ios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK
- | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
- ios.c_oflag &= ~OPOST;
- ios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
- ios.c_cflag &= ~(CSIZE | PARENB);
- ios.c_cflag |= CS8;
-#endif
- ios.c_iflag |= IGNPAR;
- ios.c_cflag |= CREAD | CLOCAL;
- descriptor_ops::clear_error(ec);
- s = descriptor_ops::error_wrapper(::tcsetattr(fd, TCSANOW, &ios), ec);
- }
- if (s < 0)
- {
- asio::error_code ignored_ec;
- descriptor_ops::close(fd, ignored_ec);
- return ec;
- }
-
- // We're done. Take ownership of the serial port descriptor.
- if (descriptor_service_.assign(impl, fd, ec))
- {
- asio::error_code ignored_ec;
- descriptor_ops::close(fd, ignored_ec);
- }
-
- return ec;
- }
+ ASIO_DECL asio::error_code open(implementation_type& impl,
+ const std::string& device, asio::error_code& ec);
- // Assign a native handle to a handle implementation.
+ // Assign a native descriptor to a serial port implementation.
asio::error_code assign(implementation_type& impl,
const native_type& native_descriptor, asio::error_code& ec)
{
return descriptor_service_.assign(impl, native_descriptor, ec);
}
- // Determine whether the handle is open.
+ // Determine whether the serial port is open.
bool is_open(const implementation_type& impl) const
{
return descriptor_service_.is_open(impl);
}
- // Destroy a handle implementation.
+ // Destroy a serial port implementation.
asio::error_code close(implementation_type& impl,
asio::error_code& ec)
{
return descriptor_service_.close(impl, ec);
}
- // Get the native handle representation.
+ // Get the native serial port representation.
native_type native(implementation_type& impl)
{
return descriptor_service_.native(impl);
}
- // Cancel all operations associated with the handle.
+ // Cancel all operations associated with the serial port.
asio::error_code cancel(implementation_type& impl,
asio::error_code& ec)
{
@@ -170,20 +103,9 @@ public:
asio::error_code set_option(implementation_type& impl,
const SettableSerialPortOption& option, asio::error_code& ec)
{
- termios ios;
- descriptor_ops::clear_error(ec);
- descriptor_ops::error_wrapper(::tcgetattr(
- descriptor_service_.native(impl), &ios), ec);
- if (ec)
- return ec;
-
- if (option.store(ios, ec))
- return ec;
-
- descriptor_ops::clear_error(ec);
- descriptor_ops::error_wrapper(::tcsetattr(
- descriptor_service_.native(impl), TCSANOW, &ios), ec);
- return ec;
+ return do_set_option(impl,
+ &reactive_serial_port_service::store_option<SettableSerialPortOption>,
+ &option, ec);
}
// Get an option from the serial port.
@@ -191,21 +113,16 @@ public:
asio::error_code get_option(const implementation_type& impl,
GettableSerialPortOption& option, asio::error_code& ec) const
{
- termios ios;
- descriptor_ops::clear_error(ec);
- descriptor_ops::error_wrapper(::tcgetattr(
- descriptor_service_.native(impl), &ios), ec);
- if (ec)
- return ec;
-
- return option.load(ios, ec);
+ return do_get_option(impl,
+ &reactive_serial_port_service::load_option<GettableSerialPortOption>,
+ &option, ec);
}
// Send a break sequence to the serial port.
asio::error_code send_break(implementation_type& impl,
asio::error_code& ec)
{
- descriptor_ops::clear_error(ec);
+ errno = 0;
descriptor_ops::error_wrapper(::tcsendbreak(
descriptor_service_.native(impl), 0), ec);
return ec;
@@ -246,6 +163,41 @@ public:
}
private:
+ // Function pointer type for storing a serial port option.
+ typedef asio::error_code (*store_function_type)(
+ const void*, termios&, asio::error_code&);
+
+ // Helper function template to store a serial port option.
+ template <typename SettableSerialPortOption>
+ static asio::error_code store_option(const void* option,
+ termios& storage, asio::error_code& ec)
+ {
+ return static_cast<const SettableSerialPortOption*>(option)->store(
+ storage, ec);
+ }
+
+ // Helper function to set a serial port option.
+ ASIO_DECL asio::error_code do_set_option(
+ implementation_type& impl, store_function_type store,
+ const void* option, asio::error_code& ec);
+
+ // Function pointer type for loading a serial port option.
+ typedef asio::error_code (*load_function_type)(
+ void*, const termios&, asio::error_code&);
+
+ // Helper function template to load a serial port option.
+ template <typename GettableSerialPortOption>
+ static asio::error_code load_option(void* option,
+ const termios& storage, asio::error_code& ec)
+ {
+ return static_cast<GettableSerialPortOption*>(option)->load(storage, ec);
+ }
+
+ // Helper function to get a serial port option.
+ ASIO_DECL asio::error_code do_get_option(
+ const implementation_type& impl, load_function_type load,
+ void* option, asio::error_code& ec) const;
+
// The implementation used for initiating asynchronous operations.
reactive_descriptor_service descriptor_service_;
};
@@ -253,9 +205,13 @@ private:
} // namespace detail
} // namespace asio
-#endif // defined(ASIO_HAS_SERIAL_PORT)
- // && !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
-
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/reactive_serial_port_service.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+#endif // defined(ASIO_HAS_SERIAL_PORT)
+
#endif // ASIO_DETAIL_REACTIVE_SERIAL_PORT_SERVICE_HPP
diff --git a/ext/asio/asio/detail/reactive_socket_accept_op.hpp b/ext/asio/asio/detail/reactive_socket_accept_op.hpp
new file mode 100644
index 0000000..76c6741
--- /dev/null
+++ b/ext/asio/asio/detail/reactive_socket_accept_op.hpp
@@ -0,0 +1,131 @@
+//
+// detail/reactive_socket_accept_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_REACTIVE_SOCKET_ACCEPT_OP_HPP
+#define ASIO_DETAIL_REACTIVE_SOCKET_ACCEPT_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/reactor_op.hpp"
+#include "asio/detail/socket_holder.hpp"
+#include "asio/detail/socket_ops.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Socket, typename Protocol>
+class reactive_socket_accept_op_base : public reactor_op
+{
+public:
+ reactive_socket_accept_op_base(socket_type socket,
+ socket_ops::state_type state, Socket& peer, const Protocol& protocol,
+ typename Protocol::endpoint* peer_endpoint, func_type complete_func)
+ : reactor_op(&reactive_socket_accept_op_base::do_perform, complete_func),
+ socket_(socket),
+ state_(state),
+ peer_(peer),
+ protocol_(protocol),
+ peer_endpoint_(peer_endpoint)
+ {
+ }
+
+ static bool do_perform(reactor_op* base)
+ {
+ reactive_socket_accept_op_base* o(
+ static_cast<reactive_socket_accept_op_base*>(base));
+
+ std::size_t addrlen = o->peer_endpoint_ ? o->peer_endpoint_->capacity() : 0;
+ socket_type new_socket = invalid_socket;
+ bool result = socket_ops::non_blocking_accept(o->socket_,
+ o->state_, o->peer_endpoint_ ? o->peer_endpoint_->data() : 0,
+ o->peer_endpoint_ ? &addrlen : 0, o->ec_, new_socket);
+
+ // On success, assign new connection to peer socket object.
+ if (new_socket >= 0)
+ {
+ socket_holder new_socket_holder(new_socket);
+ if (o->peer_endpoint_)
+ o->peer_endpoint_->resize(addrlen);
+ if (!o->peer_.assign(o->protocol_, new_socket, o->ec_))
+ new_socket_holder.release();
+ }
+
+ return result;
+ }
+
+private:
+ socket_type socket_;
+ socket_ops::state_type state_;
+ Socket& peer_;
+ Protocol protocol_;
+ typename Protocol::endpoint* peer_endpoint_;
+};
+
+template <typename Socket, typename Protocol, typename Handler>
+class reactive_socket_accept_op :
+ public reactive_socket_accept_op_base<Socket, Protocol>
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(reactive_socket_accept_op);
+
+ reactive_socket_accept_op(socket_type socket,
+ socket_ops::state_type state, Socket& peer, const Protocol& protocol,
+ typename Protocol::endpoint* peer_endpoint, Handler handler)
+ : reactive_socket_accept_op_base<Socket, Protocol>(socket, state, peer,
+ protocol, peer_endpoint, &reactive_socket_accept_op::do_complete),
+ handler_(handler)
+ {
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
+ {
+ // Take ownership of the handler object.
+ reactive_socket_accept_op* o(static_cast<reactive_socket_accept_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder1<Handler, asio::error_code>
+ handler(o->handler_, o->ec_);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_REACTIVE_SOCKET_ACCEPT_OP_HPP
diff --git a/ext/asio/asio/detail/reactive_socket_connect_op.hpp b/ext/asio/asio/detail/reactive_socket_connect_op.hpp
new file mode 100644
index 0000000..8267091
--- /dev/null
+++ b/ext/asio/asio/detail/reactive_socket_connect_op.hpp
@@ -0,0 +1,101 @@
+//
+// detail/reactive_socket_connect_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_REACTIVE_SOCKET_CONNECT_OP_HPP
+#define ASIO_DETAIL_REACTIVE_SOCKET_CONNECT_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/reactor_op.hpp"
+#include "asio/detail/socket_ops.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+class reactive_socket_connect_op_base : public reactor_op
+{
+public:
+ reactive_socket_connect_op_base(socket_type socket, func_type complete_func)
+ : reactor_op(&reactive_socket_connect_op_base::do_perform, complete_func),
+ socket_(socket)
+ {
+ }
+
+ static bool do_perform(reactor_op* base)
+ {
+ reactive_socket_connect_op_base* o(
+ static_cast<reactive_socket_connect_op_base*>(base));
+
+ return socket_ops::non_blocking_connect(o->socket_, o->ec_);
+ }
+
+private:
+ socket_type socket_;
+};
+
+template <typename Handler>
+class reactive_socket_connect_op : public reactive_socket_connect_op_base
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(reactive_socket_connect_op);
+
+ reactive_socket_connect_op(socket_type socket, Handler handler)
+ : reactive_socket_connect_op_base(socket,
+ &reactive_socket_connect_op::do_complete),
+ handler_(handler)
+ {
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
+ {
+ // Take ownership of the handler object.
+ reactive_socket_connect_op* o
+ (static_cast<reactive_socket_connect_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder1<Handler, asio::error_code>
+ handler(o->handler_, o->ec_);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler);
+ }
+ }
+
+private:
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_REACTIVE_SOCKET_CONNECT_OP_HPP
diff --git a/ext/asio/asio/detail/reactive_socket_recv_op.hpp b/ext/asio/asio/detail/reactive_socket_recv_op.hpp
new file mode 100644
index 0000000..91a30a8
--- /dev/null
+++ b/ext/asio/asio/detail/reactive_socket_recv_op.hpp
@@ -0,0 +1,118 @@
+//
+// detail/reactive_socket_recv_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_HPP
+#define ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/reactor_op.hpp"
+#include "asio/detail/socket_ops.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename MutableBufferSequence>
+class reactive_socket_recv_op_base : public reactor_op
+{
+public:
+ reactive_socket_recv_op_base(socket_type socket,
+ socket_ops::state_type state, const MutableBufferSequence& buffers,
+ socket_base::message_flags flags, func_type complete_func)
+ : reactor_op(&reactive_socket_recv_op_base::do_perform, complete_func),
+ socket_(socket),
+ state_(state),
+ buffers_(buffers),
+ flags_(flags)
+ {
+ }
+
+ static bool do_perform(reactor_op* base)
+ {
+ reactive_socket_recv_op_base* o(
+ static_cast<reactive_socket_recv_op_base*>(base));
+
+ buffer_sequence_adapter<asio::mutable_buffer,
+ MutableBufferSequence> bufs(o->buffers_);
+
+ return socket_ops::non_blocking_recv(o->socket_,
+ bufs.buffers(), bufs.count(), o->flags_,
+ (o->state_ & socket_ops::stream_oriented),
+ o->ec_, o->bytes_transferred_);
+ }
+
+private:
+ socket_type socket_;
+ socket_ops::state_type state_;
+ MutableBufferSequence buffers_;
+ socket_base::message_flags flags_;
+};
+
+template <typename MutableBufferSequence, typename Handler>
+class reactive_socket_recv_op :
+ public reactive_socket_recv_op_base<MutableBufferSequence>
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(reactive_socket_recv_op);
+
+ reactive_socket_recv_op(socket_type socket,
+ socket_ops::state_type state, const MutableBufferSequence& buffers,
+ socket_base::message_flags flags, Handler handler)
+ : reactive_socket_recv_op_base<MutableBufferSequence>(socket, state,
+ buffers, flags, &reactive_socket_recv_op::do_complete),
+ handler_(handler)
+ {
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
+ {
+ // Take ownership of the handler object.
+ reactive_socket_recv_op* o(static_cast<reactive_socket_recv_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder2<Handler, asio::error_code, std::size_t>
+ handler(o->handler_, o->ec_, o->bytes_transferred_);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_HPP
diff --git a/ext/asio/asio/detail/reactive_socket_recvfrom_op.hpp b/ext/asio/asio/detail/reactive_socket_recvfrom_op.hpp
new file mode 100644
index 0000000..121d6ec
--- /dev/null
+++ b/ext/asio/asio/detail/reactive_socket_recvfrom_op.hpp
@@ -0,0 +1,128 @@
+//
+// detail/reactive_socket_recvfrom_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_HPP
+#define ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/reactor_op.hpp"
+#include "asio/detail/socket_ops.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename MutableBufferSequence, typename Endpoint>
+class reactive_socket_recvfrom_op_base : public reactor_op
+{
+public:
+ reactive_socket_recvfrom_op_base(socket_type socket, int protocol_type,
+ const MutableBufferSequence& buffers, Endpoint& endpoint,
+ socket_base::message_flags flags, func_type complete_func)
+ : reactor_op(&reactive_socket_recvfrom_op_base::do_perform, complete_func),
+ socket_(socket),
+ protocol_type_(protocol_type),
+ buffers_(buffers),
+ sender_endpoint_(endpoint),
+ flags_(flags)
+ {
+ }
+
+ static bool do_perform(reactor_op* base)
+ {
+ reactive_socket_recvfrom_op_base* o(
+ static_cast<reactive_socket_recvfrom_op_base*>(base));
+
+ buffer_sequence_adapter<asio::mutable_buffer,
+ MutableBufferSequence> bufs(o->buffers_);
+
+ std::size_t addr_len = o->sender_endpoint_.capacity();
+ bool result = socket_ops::non_blocking_recvfrom(o->socket_,
+ bufs.buffers(), bufs.count(), o->flags_,
+ o->sender_endpoint_.data(), &addr_len,
+ o->ec_, o->bytes_transferred_);
+
+ if (result && !o->ec_)
+ o->sender_endpoint_.resize(addr_len);
+
+ return result;
+ }
+
+private:
+ socket_type socket_;
+ int protocol_type_;
+ MutableBufferSequence buffers_;
+ Endpoint& sender_endpoint_;
+ socket_base::message_flags flags_;
+};
+
+template <typename MutableBufferSequence, typename Endpoint, typename Handler>
+class reactive_socket_recvfrom_op :
+ public reactive_socket_recvfrom_op_base<MutableBufferSequence, Endpoint>
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(reactive_socket_recvfrom_op);
+
+ reactive_socket_recvfrom_op(socket_type socket, int protocol_type,
+ const MutableBufferSequence& buffers, Endpoint& endpoint,
+ socket_base::message_flags flags, Handler handler)
+ : reactive_socket_recvfrom_op_base<MutableBufferSequence, Endpoint>(
+ socket, protocol_type, buffers, endpoint, flags,
+ &reactive_socket_recvfrom_op::do_complete),
+ handler_(handler)
+ {
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
+ {
+ // Take ownership of the handler object.
+ reactive_socket_recvfrom_op* o(
+ static_cast<reactive_socket_recvfrom_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder2<Handler, asio::error_code, std::size_t>
+ handler(o->handler_, o->ec_, o->bytes_transferred_);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_HPP
diff --git a/ext/asio/asio/detail/reactive_socket_send_op.hpp b/ext/asio/asio/detail/reactive_socket_send_op.hpp
new file mode 100644
index 0000000..9a68dac
--- /dev/null
+++ b/ext/asio/asio/detail/reactive_socket_send_op.hpp
@@ -0,0 +1,115 @@
+//
+// detail/reactive_socket_send_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_REACTIVE_SOCKET_SEND_OP_HPP
+#define ASIO_DETAIL_REACTIVE_SOCKET_SEND_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/reactor_op.hpp"
+#include "asio/detail/socket_ops.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename ConstBufferSequence>
+class reactive_socket_send_op_base : public reactor_op
+{
+public:
+ reactive_socket_send_op_base(socket_type socket,
+ const ConstBufferSequence& buffers,
+ socket_base::message_flags flags, func_type complete_func)
+ : reactor_op(&reactive_socket_send_op_base::do_perform, complete_func),
+ socket_(socket),
+ buffers_(buffers),
+ flags_(flags)
+ {
+ }
+
+ static bool do_perform(reactor_op* base)
+ {
+ reactive_socket_send_op_base* o(
+ static_cast<reactive_socket_send_op_base*>(base));
+
+ buffer_sequence_adapter<asio::const_buffer,
+ ConstBufferSequence> bufs(o->buffers_);
+
+ return socket_ops::non_blocking_send(o->socket_,
+ bufs.buffers(), bufs.count(), o->flags_,
+ o->ec_, o->bytes_transferred_);
+ }
+
+private:
+ socket_type socket_;
+ ConstBufferSequence buffers_;
+ socket_base::message_flags flags_;
+};
+
+template <typename ConstBufferSequence, typename Handler>
+class reactive_socket_send_op :
+ public reactive_socket_send_op_base<ConstBufferSequence>
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(reactive_socket_send_op);
+
+ reactive_socket_send_op(socket_type socket,
+ const ConstBufferSequence& buffers,
+ socket_base::message_flags flags, Handler handler)
+ : reactive_socket_send_op_base<ConstBufferSequence>(socket,
+ buffers, flags, &reactive_socket_send_op::do_complete),
+ handler_(handler)
+ {
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
+ {
+ // Take ownership of the handler object.
+ reactive_socket_send_op* o(static_cast<reactive_socket_send_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder2<Handler, asio::error_code, std::size_t>
+ handler(o->handler_, o->ec_, o->bytes_transferred_);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_REACTIVE_SOCKET_SEND_OP_HPP
diff --git a/ext/asio/asio/detail/reactive_socket_sendto_op.hpp b/ext/asio/asio/detail/reactive_socket_sendto_op.hpp
new file mode 100644
index 0000000..e466a25
--- /dev/null
+++ b/ext/asio/asio/detail/reactive_socket_sendto_op.hpp
@@ -0,0 +1,118 @@
+//
+// detail/reactive_socket_sendto_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_REACTIVE_SOCKET_SENDTO_OP_HPP
+#define ASIO_DETAIL_REACTIVE_SOCKET_SENDTO_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/reactor_op.hpp"
+#include "asio/detail/socket_ops.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename ConstBufferSequence, typename Endpoint>
+class reactive_socket_sendto_op_base : public reactor_op
+{
+public:
+ reactive_socket_sendto_op_base(socket_type socket,
+ const ConstBufferSequence& buffers, const Endpoint& endpoint,
+ socket_base::message_flags flags, func_type complete_func)
+ : reactor_op(&reactive_socket_sendto_op_base::do_perform, complete_func),
+ socket_(socket),
+ buffers_(buffers),
+ destination_(endpoint),
+ flags_(flags)
+ {
+ }
+
+ static bool do_perform(reactor_op* base)
+ {
+ reactive_socket_sendto_op_base* o(
+ static_cast<reactive_socket_sendto_op_base*>(base));
+
+ buffer_sequence_adapter<asio::const_buffer,
+ ConstBufferSequence> bufs(o->buffers_);
+
+ return socket_ops::non_blocking_sendto(o->socket_,
+ bufs.buffers(), bufs.count(), o->flags_,
+ o->destination_.data(), o->destination_.size(),
+ o->ec_, o->bytes_transferred_);
+ }
+
+private:
+ socket_type socket_;
+ ConstBufferSequence buffers_;
+ Endpoint destination_;
+ socket_base::message_flags flags_;
+};
+
+template <typename ConstBufferSequence, typename Endpoint, typename Handler>
+class reactive_socket_sendto_op :
+ public reactive_socket_sendto_op_base<ConstBufferSequence, Endpoint>
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(reactive_socket_sendto_op);
+
+ reactive_socket_sendto_op(socket_type socket,
+ const ConstBufferSequence& buffers, const Endpoint& endpoint,
+ socket_base::message_flags flags, Handler handler)
+ : reactive_socket_sendto_op_base<ConstBufferSequence, Endpoint>(socket,
+ buffers, endpoint, flags, &reactive_socket_sendto_op::do_complete),
+ handler_(handler)
+ {
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
+ {
+ // Take ownership of the handler object.
+ reactive_socket_sendto_op* o(static_cast<reactive_socket_sendto_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder2<Handler, asio::error_code, std::size_t>
+ handler(o->handler_, o->ec_, o->bytes_transferred_);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_REACTIVE_SOCKET_SENDTO_OP_HPP
diff --git a/ext/asio/asio/detail/reactive_socket_service.hpp b/ext/asio/asio/detail/reactive_socket_service.hpp
index 20bd512..daa9f60 100644
--- a/ext/asio/asio/detail/reactive_socket_service.hpp
+++ b/ext/asio/asio/detail/reactive_socket_service.hpp
@@ -1,8 +1,8 @@
//
-// reactive_socket_service.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/reactive_socket_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,28 +15,37 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+
+#if !defined(ASIO_HAS_IOCP)
+#include <boost/utility/addressof.hpp>
#include "asio/buffer.hpp"
#include "asio/error.hpp"
#include "asio/io_service.hpp"
#include "asio/socket_base.hpp"
-#include "asio/detail/bind_handler.hpp"
#include "asio/detail/buffer_sequence_adapter.hpp"
-#include "asio/detail/fenced_block.hpp"
#include "asio/detail/noncopyable.hpp"
-#include "asio/detail/null_buffers_op.hpp"
+#include "asio/detail/reactive_null_buffers_op.hpp"
+#include "asio/detail/reactive_socket_accept_op.hpp"
+#include "asio/detail/reactive_socket_connect_op.hpp"
+#include "asio/detail/reactive_socket_recvfrom_op.hpp"
+#include "asio/detail/reactive_socket_sendto_op.hpp"
+#include "asio/detail/reactive_socket_service_base.hpp"
#include "asio/detail/reactor.hpp"
#include "asio/detail/reactor_op.hpp"
#include "asio/detail/socket_holder.hpp"
#include "asio/detail/socket_ops.hpp"
#include "asio/detail/socket_types.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
template <typename Protocol>
-class reactive_socket_service
+class reactive_socket_service :
+ public reactive_socket_service_base
{
public:
// The protocol type.
@@ -49,132 +58,32 @@ public:
typedef socket_type native_type;
// The implementation type of the socket.
- class implementation_type
- : private asio::detail::noncopyable
+ struct implementation_type :
+ reactive_socket_service_base::base_implementation_type
{
- public:
// Default constructor.
implementation_type()
- : socket_(invalid_socket),
- flags_(0),
- protocol_(endpoint_type().protocol())
+ : protocol_(endpoint_type().protocol())
{
}
- private:
- // Only this service will have access to the internal values.
- friend class reactive_socket_service<Protocol>;
-
- // The native socket representation.
- socket_type socket_;
-
- enum
- {
- // The user wants a non-blocking socket.
- user_set_non_blocking = 1,
-
- // The implementation wants a non-blocking socket (in order to be able to
- // perform asynchronous read and write operations).
- internal_non_blocking = 2,
-
- // Helper "flag" used to determine whether the socket is non-blocking.
- non_blocking = user_set_non_blocking | internal_non_blocking,
-
- // User wants connection_aborted errors, which are disabled by default.
- enable_connection_aborted = 4,
-
- // The user set the linger option. Needs to be checked when closing.
- user_set_linger = 8
- };
-
- // Flags indicating the current state of the socket.
- unsigned char flags_;
-
// The protocol associated with the socket.
protocol_type protocol_;
-
- // Per-descriptor data used by the reactor.
- reactor::per_descriptor_data reactor_data_;
};
// Constructor.
reactive_socket_service(asio::io_service& io_service)
- : io_service_impl_(use_service<io_service_impl>(io_service)),
- reactor_(use_service<reactor>(io_service))
- {
- reactor_.init_task();
- }
-
- // Destroy all user-defined handler objects owned by the service.
- void shutdown_service()
- {
- }
-
- // Construct a new socket implementation.
- void construct(implementation_type& impl)
+ : reactive_socket_service_base(io_service)
{
- impl.socket_ = invalid_socket;
- impl.flags_ = 0;
- }
-
- // Destroy a socket implementation.
- void destroy(implementation_type& impl)
- {
- if (impl.socket_ != invalid_socket)
- {
- reactor_.close_descriptor(impl.socket_, impl.reactor_data_);
-
- if (impl.flags_ & implementation_type::non_blocking)
- {
- ioctl_arg_type non_blocking = 0;
- asio::error_code ignored_ec;
- socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec);
- impl.flags_ &= ~implementation_type::non_blocking;
- }
-
- if (impl.flags_ & implementation_type::user_set_linger)
- {
- ::linger opt;
- opt.l_onoff = 0;
- opt.l_linger = 0;
- asio::error_code ignored_ec;
- socket_ops::setsockopt(impl.socket_,
- SOL_SOCKET, SO_LINGER, &opt, sizeof(opt), ignored_ec);
- }
-
- asio::error_code ignored_ec;
- socket_ops::close(impl.socket_, ignored_ec);
-
- impl.socket_ = invalid_socket;
- }
}
// Open a new socket implementation.
asio::error_code open(implementation_type& impl,
const protocol_type& protocol, asio::error_code& ec)
{
- if (is_open(impl))
- {
- ec = asio::error::already_open;
- return ec;
- }
-
- socket_holder sock(socket_ops::socket(protocol.family(),
- protocol.type(), protocol.protocol(), ec));
- if (sock.get() == invalid_socket)
- return ec;
-
- if (int err = reactor_.register_descriptor(sock.get(), impl.reactor_data_))
- {
- ec = asio::error_code(err,
- asio::error::get_system_category());
- return ec;
- }
-
- impl.socket_ = sock.release();
- impl.flags_ = 0;
- impl.protocol_ = protocol;
- ec = asio::error_code();
+ if (!do_open(impl, protocol.family(),
+ protocol.type(), protocol.protocol(), ec))
+ impl.protocol_ = protocol;
return ec;
}
@@ -183,56 +92,8 @@ public:
const protocol_type& protocol, const native_type& native_socket,
asio::error_code& ec)
{
- if (is_open(impl))
- {
- ec = asio::error::already_open;
- return ec;
- }
-
- if (int err = reactor_.register_descriptor(
- native_socket, impl.reactor_data_))
- {
- ec = asio::error_code(err,
- asio::error::get_system_category());
- return ec;
- }
-
- impl.socket_ = native_socket;
- impl.flags_ = 0;
- impl.protocol_ = protocol;
- ec = asio::error_code();
- return ec;
- }
-
- // Determine whether the socket is open.
- bool is_open(const implementation_type& impl) const
- {
- return impl.socket_ != invalid_socket;
- }
-
- // Destroy a socket implementation.
- asio::error_code close(implementation_type& impl,
- asio::error_code& ec)
- {
- if (is_open(impl))
- {
- reactor_.close_descriptor(impl.socket_, impl.reactor_data_);
-
- if (impl.flags_ & implementation_type::non_blocking)
- {
- ioctl_arg_type non_blocking = 0;
- asio::error_code ignored_ec;
- socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec);
- impl.flags_ &= ~implementation_type::non_blocking;
- }
-
- if (socket_ops::close(impl.socket_, ec) == socket_error_retval)
- return ec;
-
- impl.socket_ = invalid_socket;
- }
-
- ec = asio::error_code();
+ if (!do_assign(impl, protocol.type(), native_socket, ec))
+ impl.protocol_ = protocol;
return ec;
}
@@ -242,153 +103,23 @@ public:
return impl.socket_;
}
- // Cancel all operations associated with the socket.
- asio::error_code cancel(implementation_type& impl,
- asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
- reactor_.cancel_ops(impl.socket_, impl.reactor_data_);
- ec = asio::error_code();
- return ec;
- }
-
- // Determine whether the socket is at the out-of-band data mark.
- bool at_mark(const implementation_type& impl,
- asio::error_code& ec) const
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return false;
- }
-
-#if defined(SIOCATMARK)
- asio::detail::ioctl_arg_type value = 0;
- socket_ops::ioctl(impl.socket_, SIOCATMARK, &value, ec);
-# if defined(ENOTTY)
- if (ec.value() == ENOTTY)
- ec = asio::error::not_socket;
-# endif // defined(ENOTTY)
-#else // defined(SIOCATMARK)
- int value = sockatmark(impl.socket_);
- if (value == -1)
- ec = asio::error_code(errno,
- asio::error::get_system_category());
- else
- ec = asio::error_code();
-#endif // defined(SIOCATMARK)
- return ec ? false : value != 0;
- }
-
- // Determine the number of bytes available for reading.
- std::size_t available(const implementation_type& impl,
- asio::error_code& ec) const
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
- asio::detail::ioctl_arg_type value = 0;
- socket_ops::ioctl(impl.socket_, FIONREAD, &value, ec);
-#if defined(ENOTTY)
- if (ec.value() == ENOTTY)
- ec = asio::error::not_socket;
-#endif // defined(ENOTTY)
- return ec ? static_cast<std::size_t>(0) : static_cast<std::size_t>(value);
- }
-
// Bind the socket to the specified local endpoint.
asio::error_code bind(implementation_type& impl,
const endpoint_type& endpoint, asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec);
return ec;
}
- // Place the socket into the state where it will listen for new connections.
- asio::error_code listen(implementation_type& impl, int backlog,
- asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
- socket_ops::listen(impl.socket_, backlog, ec);
- return ec;
- }
-
// Set a socket option.
template <typename Option>
asio::error_code set_option(implementation_type& impl,
const Option& option, asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
- if (option.level(impl.protocol_) == custom_socket_option_level
- && option.name(impl.protocol_) == enable_connection_aborted_option)
- {
- if (option.size(impl.protocol_) != sizeof(int))
- {
- ec = asio::error::invalid_argument;
- }
- else
- {
- if (*reinterpret_cast<const int*>(option.data(impl.protocol_)))
- impl.flags_ |= implementation_type::enable_connection_aborted;
- else
- impl.flags_ &= ~implementation_type::enable_connection_aborted;
- ec = asio::error_code();
- }
- return ec;
- }
- else
- {
- if (option.level(impl.protocol_) == SOL_SOCKET
- && option.name(impl.protocol_) == SO_LINGER)
- {
- impl.flags_ |= implementation_type::user_set_linger;
- }
-
- socket_ops::setsockopt(impl.socket_,
- option.level(impl.protocol_), option.name(impl.protocol_),
- option.data(impl.protocol_), option.size(impl.protocol_), ec);
-
-#if defined(__MACH__) && defined(__APPLE__) \
-|| defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
- // To implement portable behaviour for SO_REUSEADDR with UDP sockets we
- // need to also set SO_REUSEPORT on BSD-based platforms.
- if (!ec && impl.protocol_.type() == SOCK_DGRAM
- && option.level(impl.protocol_) == SOL_SOCKET
- && option.name(impl.protocol_) == SO_REUSEADDR)
- {
- asio::error_code ignored_ec;
- socket_ops::setsockopt(impl.socket_, SOL_SOCKET, SO_REUSEPORT,
- option.data(impl.protocol_), option.size(impl.protocol_),
- ignored_ec);
- }
-#endif
-
- return ec;
- }
+ socket_ops::setsockopt(impl.socket_, impl.state_,
+ option.level(impl.protocol_), option.name(impl.protocol_),
+ option.data(impl.protocol_), option.size(impl.protocol_), ec);
+ return ec;
}
// Set a socket option.
@@ -396,78 +127,12 @@ public:
asio::error_code get_option(const implementation_type& impl,
Option& option, asio::error_code& ec) const
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
- if (option.level(impl.protocol_) == custom_socket_option_level
- && option.name(impl.protocol_) == enable_connection_aborted_option)
- {
- if (option.size(impl.protocol_) != sizeof(int))
- {
- ec = asio::error::invalid_argument;
- }
- else
- {
- int* target = reinterpret_cast<int*>(option.data(impl.protocol_));
- if (impl.flags_ & implementation_type::enable_connection_aborted)
- *target = 1;
- else
- *target = 0;
- option.resize(impl.protocol_, sizeof(int));
- ec = asio::error_code();
- }
- return ec;
- }
- else
- {
- size_t size = option.size(impl.protocol_);
- socket_ops::getsockopt(impl.socket_,
- option.level(impl.protocol_), option.name(impl.protocol_),
- option.data(impl.protocol_), &size, ec);
- if (!ec)
- option.resize(impl.protocol_, size);
- return ec;
- }
- }
-
- // Perform an IO control command on the socket.
- template <typename IO_Control_Command>
- asio::error_code io_control(implementation_type& impl,
- IO_Control_Command& command, asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
- socket_ops::ioctl(impl.socket_, command.name(),
- static_cast<ioctl_arg_type*>(command.data()), ec);
-
- // When updating the non-blocking mode we always perform the ioctl
- // syscall, even if the flags would otherwise indicate that the socket is
- // already in the correct state. This ensures that the underlying socket
- // is put into the state that has been requested by the user. If the ioctl
- // syscall was successful then we need to update the flags to match.
- if (!ec && command.name() == static_cast<int>(FIONBIO))
- {
- if (*static_cast<ioctl_arg_type*>(command.data()))
- {
- impl.flags_ |= implementation_type::user_set_non_blocking;
- }
- else
- {
- // Clearing the non-blocking mode always overrides any internally-set
- // non-blocking flag. Any subsequent asynchronous operations will need
- // to re-enable non-blocking I/O.
- impl.flags_ &= ~(implementation_type::user_set_non_blocking
- | implementation_type::internal_non_blocking);
- }
- }
-
+ std::size_t size = option.size(impl.protocol_);
+ socket_ops::getsockopt(impl.socket_, impl.state_,
+ option.level(impl.protocol_), option.name(impl.protocol_),
+ option.data(impl.protocol_), &size, ec);
+ if (!ec)
+ option.resize(impl.protocol_, size);
return ec;
}
@@ -475,12 +140,6 @@ public:
endpoint_type local_endpoint(const implementation_type& impl,
asio::error_code& ec) const
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return endpoint_type();
- }
-
endpoint_type endpoint;
std::size_t addr_len = endpoint.capacity();
if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec))
@@ -493,218 +152,15 @@ public:
endpoint_type remote_endpoint(const implementation_type& impl,
asio::error_code& ec) const
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return endpoint_type();
- }
-
endpoint_type endpoint;
std::size_t addr_len = endpoint.capacity();
- if (socket_ops::getpeername(impl.socket_, endpoint.data(), &addr_len, ec))
+ if (socket_ops::getpeername(impl.socket_,
+ endpoint.data(), &addr_len, false, ec))
return endpoint_type();
endpoint.resize(addr_len);
return endpoint;
}
- /// Disable sends or receives on the socket.
- asio::error_code shutdown(implementation_type& impl,
- socket_base::shutdown_type what, asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
- socket_ops::shutdown(impl.socket_, what, ec);
- return ec;
- }
-
- // Send the given data to the peer.
- template <typename ConstBufferSequence>
- size_t send(implementation_type& impl, const ConstBufferSequence& buffers,
- socket_base::message_flags flags, asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
- buffer_sequence_adapter<asio::const_buffer,
- ConstBufferSequence> bufs(buffers);
-
- // A request to receive 0 bytes on a stream socket is a no-op.
- if (impl.protocol_.type() == SOCK_STREAM && bufs.all_empty())
- {
- ec = asio::error_code();
- return 0;
- }
-
- // Send the data.
- for (;;)
- {
- // Try to complete the operation without blocking.
- int bytes_sent = socket_ops::send(impl.socket_,
- bufs.buffers(), bufs.count(), flags, ec);
-
- // Check if operation succeeded.
- if (bytes_sent >= 0)
- return bytes_sent;
-
- // Operation failed.
- if ((impl.flags_ & implementation_type::user_set_non_blocking)
- || (ec != asio::error::would_block
- && ec != asio::error::try_again))
- return 0;
-
- // Wait for socket to become ready.
- if (socket_ops::poll_write(impl.socket_, ec) < 0)
- return 0;
- }
- }
-
- // Wait until data can be sent without blocking.
- size_t send(implementation_type& impl, const null_buffers&,
- socket_base::message_flags, asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
- // Wait for socket to become ready.
- socket_ops::poll_write(impl.socket_, ec);
-
- return 0;
- }
-
- template <typename ConstBufferSequence>
- class send_op_base : public reactor_op
- {
- public:
- send_op_base(socket_type socket, const ConstBufferSequence& buffers,
- socket_base::message_flags flags, func_type complete_func)
- : reactor_op(&send_op_base::do_perform, complete_func),
- socket_(socket),
- buffers_(buffers),
- flags_(flags)
- {
- }
-
- static bool do_perform(reactor_op* base)
- {
- send_op_base* o(static_cast<send_op_base*>(base));
-
- buffer_sequence_adapter<asio::const_buffer,
- ConstBufferSequence> bufs(o->buffers_);
-
- for (;;)
- {
- // Send the data.
- asio::error_code ec;
- int bytes = socket_ops::send(o->socket_,
- bufs.buffers(), bufs.count(), o->flags_, ec);
-
- // Retry operation if interrupted by signal.
- if (ec == asio::error::interrupted)
- continue;
-
- // Check if we need to run the operation again.
- if (ec == asio::error::would_block
- || ec == asio::error::try_again)
- return false;
-
- o->ec_ = ec;
- o->bytes_transferred_ = (bytes < 0 ? 0 : bytes);
- return true;
- }
- }
-
- private:
- socket_type socket_;
- ConstBufferSequence buffers_;
- socket_base::message_flags flags_;
- };
-
- template <typename ConstBufferSequence, typename Handler>
- class send_op : public send_op_base<ConstBufferSequence>
- {
- public:
- send_op(socket_type socket, const ConstBufferSequence& buffers,
- socket_base::message_flags flags, Handler handler)
- : send_op_base<ConstBufferSequence>(socket,
- buffers, flags, &send_op::do_complete),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
- {
- // Take ownership of the handler object.
- send_op* o(static_cast<send_op*>(base));
- typedef handler_alloc_traits<Handler, send_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder2<Handler, asio::error_code, std::size_t>
- handler(o->handler_, o->ec_, o->bytes_transferred_);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- Handler handler_;
- };
-
- // Start an asynchronous send. The data being sent must be valid for the
- // lifetime of the asynchronous operation.
- template <typename ConstBufferSequence, typename Handler>
- void async_send(implementation_type& impl, const ConstBufferSequence& buffers,
- socket_base::message_flags flags, Handler handler)
- {
- // Allocate and construct an operation to wrap the handler.
- typedef send_op<ConstBufferSequence, Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr,
- impl.socket_, buffers, flags, handler);
-
- start_op(impl, reactor::write_op, ptr.get(), true,
- (impl.protocol_.type() == SOCK_STREAM
- && buffer_sequence_adapter<asio::const_buffer,
- ConstBufferSequence>::all_empty(buffers)));
- ptr.release();
- }
-
- // Start an asynchronous wait until data can be sent without blocking.
- template <typename Handler>
- void async_send(implementation_type& impl, const null_buffers&,
- socket_base::message_flags, Handler handler)
- {
- // Allocate and construct an operation to wrap the handler.
- typedef null_buffers_op<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, handler);
-
- start_op(impl, reactor::write_op, ptr.get(), false, false);
- ptr.release();
- }
-
// Send a datagram to the specified endpoint. Returns the number of bytes
// sent.
template <typename ConstBufferSequence>
@@ -712,148 +168,25 @@ public:
const endpoint_type& destination, socket_base::message_flags flags,
asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
buffer_sequence_adapter<asio::const_buffer,
ConstBufferSequence> bufs(buffers);
- // Send the data.
- for (;;)
- {
- // Try to complete the operation without blocking.
- int bytes_sent = socket_ops::sendto(impl.socket_, bufs.buffers(),
- bufs.count(), flags, destination.data(), destination.size(), ec);
-
- // Check if operation succeeded.
- if (bytes_sent >= 0)
- return bytes_sent;
-
- // Operation failed.
- if ((impl.flags_ & implementation_type::user_set_non_blocking)
- || (ec != asio::error::would_block
- && ec != asio::error::try_again))
- return 0;
-
- // Wait for socket to become ready.
- if (socket_ops::poll_write(impl.socket_, ec) < 0)
- return 0;
- }
+ return socket_ops::sync_sendto(impl.socket_, impl.state_,
+ bufs.buffers(), bufs.count(), flags,
+ destination.data(), destination.size(), ec);
}
// Wait until data can be sent without blocking.
size_t send_to(implementation_type& impl, const null_buffers&,
- socket_base::message_flags, const endpoint_type&,
+ const endpoint_type&, socket_base::message_flags,
asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
// Wait for socket to become ready.
socket_ops::poll_write(impl.socket_, ec);
return 0;
}
- template <typename ConstBufferSequence>
- class send_to_op_base : public reactor_op
- {
- public:
- send_to_op_base(socket_type socket, const ConstBufferSequence& buffers,
- const endpoint_type& endpoint, socket_base::message_flags flags,
- func_type complete_func)
- : reactor_op(&send_to_op_base::do_perform, complete_func),
- socket_(socket),
- buffers_(buffers),
- destination_(endpoint),
- flags_(flags)
- {
- }
-
- static bool do_perform(reactor_op* base)
- {
- send_to_op_base* o(static_cast<send_to_op_base*>(base));
-
- buffer_sequence_adapter<asio::const_buffer,
- ConstBufferSequence> bufs(o->buffers_);
-
- for (;;)
- {
- // Send the data.
- asio::error_code ec;
- int bytes = socket_ops::sendto(o->socket_, bufs.buffers(), bufs.count(),
- o->flags_, o->destination_.data(), o->destination_.size(), ec);
-
- // Retry operation if interrupted by signal.
- if (ec == asio::error::interrupted)
- continue;
-
- // Check if we need to run the operation again.
- if (ec == asio::error::would_block
- || ec == asio::error::try_again)
- return false;
-
- o->ec_ = ec;
- o->bytes_transferred_ = (bytes < 0 ? 0 : bytes);
- return true;
- }
- }
-
- private:
- socket_type socket_;
- ConstBufferSequence buffers_;
- endpoint_type destination_;
- socket_base::message_flags flags_;
- };
-
- template <typename ConstBufferSequence, typename Handler>
- class send_to_op : public send_to_op_base<ConstBufferSequence>
- {
- public:
- send_to_op(socket_type socket, const ConstBufferSequence& buffers,
- const endpoint_type& endpoint, socket_base::message_flags flags,
- Handler handler)
- : send_to_op_base<ConstBufferSequence>(socket,
- buffers, endpoint, flags, &send_to_op::do_complete),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
- {
- // Take ownership of the handler object.
- send_to_op* o(static_cast<send_to_op*>(base));
- typedef handler_alloc_traits<Handler, send_to_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder2<Handler, asio::error_code, std::size_t>
- handler(o->handler_, o->ec_, o->bytes_transferred_);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- Handler handler_;
- };
-
// Start an asynchronous send. The data being sent must be valid for the
// lifetime of the asynchronous operation.
template <typename ConstBufferSequence, typename Handler>
@@ -863,235 +196,31 @@ public:
Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef send_to_op<ConstBufferSequence, Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, impl.socket_,
- buffers, destination, flags, handler);
+ typedef reactive_socket_sendto_op<ConstBufferSequence,
+ endpoint_type, Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl.socket_, buffers, destination, flags, handler);
- start_op(impl, reactor::write_op, ptr.get(), true, false);
- ptr.release();
+ start_op(impl, reactor::write_op, p.p, true, false);
+ p.v = p.p = 0;
}
// Start an asynchronous wait until data can be sent without blocking.
template <typename Handler>
void async_send_to(implementation_type& impl, const null_buffers&,
- socket_base::message_flags, const endpoint_type&, Handler handler)
+ const endpoint_type&, socket_base::message_flags, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef null_buffers_op<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, handler);
-
- start_op(impl, reactor::write_op, ptr.get(), false, false);
- ptr.release();
- }
-
- // Receive some data from the peer. Returns the number of bytes received.
- template <typename MutableBufferSequence>
- size_t receive(implementation_type& impl,
- const MutableBufferSequence& buffers,
- socket_base::message_flags flags, asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
- buffer_sequence_adapter<asio::mutable_buffer,
- MutableBufferSequence> bufs(buffers);
-
- // A request to receive 0 bytes on a stream socket is a no-op.
- if (impl.protocol_.type() == SOCK_STREAM && bufs.all_empty())
- {
- ec = asio::error_code();
- return 0;
- }
-
- // Receive some data.
- for (;;)
- {
- // Try to complete the operation without blocking.
- int bytes_recvd = socket_ops::recv(impl.socket_,
- bufs.buffers(), bufs.count(), flags, ec);
-
- // Check if operation succeeded.
- if (bytes_recvd > 0)
- return bytes_recvd;
-
- // Check for EOF.
- if (bytes_recvd == 0 && impl.protocol_.type() == SOCK_STREAM)
- {
- ec = asio::error::eof;
- return 0;
- }
-
- // Operation failed.
- if ((impl.flags_ & implementation_type::user_set_non_blocking)
- || (ec != asio::error::would_block
- && ec != asio::error::try_again))
- return 0;
-
- // Wait for socket to become ready.
- if (socket_ops::poll_read(impl.socket_, ec) < 0)
- return 0;
- }
- }
-
- // Wait until data can be received without blocking.
- size_t receive(implementation_type& impl, const null_buffers&,
- socket_base::message_flags, asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
- // Wait for socket to become ready.
- socket_ops::poll_read(impl.socket_, ec);
+ typedef reactive_null_buffers_op<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(handler);
- return 0;
- }
-
- template <typename MutableBufferSequence>
- class receive_op_base : public reactor_op
- {
- public:
- receive_op_base(socket_type socket, int protocol_type,
- const MutableBufferSequence& buffers,
- socket_base::message_flags flags, func_type complete_func)
- : reactor_op(&receive_op_base::do_perform, complete_func),
- socket_(socket),
- protocol_type_(protocol_type),
- buffers_(buffers),
- flags_(flags)
- {
- }
-
- static bool do_perform(reactor_op* base)
- {
- receive_op_base* o(static_cast<receive_op_base*>(base));
-
- buffer_sequence_adapter<asio::mutable_buffer,
- MutableBufferSequence> bufs(o->buffers_);
-
- for (;;)
- {
- // Receive some data.
- asio::error_code ec;
- int bytes = socket_ops::recv(o->socket_,
- bufs.buffers(), bufs.count(), o->flags_, ec);
- if (bytes == 0 && o->protocol_type_ == SOCK_STREAM)
- ec = asio::error::eof;
-
- // Retry operation if interrupted by signal.
- if (ec == asio::error::interrupted)
- continue;
-
- // Check if we need to run the operation again.
- if (ec == asio::error::would_block
- || ec == asio::error::try_again)
- return false;
-
- o->ec_ = ec;
- o->bytes_transferred_ = (bytes < 0 ? 0 : bytes);
- return true;
- }
- }
-
- private:
- socket_type socket_;
- int protocol_type_;
- MutableBufferSequence buffers_;
- socket_base::message_flags flags_;
- };
-
- template <typename MutableBufferSequence, typename Handler>
- class receive_op : public receive_op_base<MutableBufferSequence>
- {
- public:
- receive_op(socket_type socket, int protocol_type,
- const MutableBufferSequence& buffers,
- socket_base::message_flags flags, Handler handler)
- : receive_op_base<MutableBufferSequence>(socket,
- protocol_type, buffers, flags, &receive_op::do_complete),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
- {
- // Take ownership of the handler object.
- receive_op* o(static_cast<receive_op*>(base));
- typedef handler_alloc_traits<Handler, receive_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder2<Handler, asio::error_code, std::size_t>
- handler(o->handler_, o->ec_, o->bytes_transferred_);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- Handler handler_;
- };
-
- // Start an asynchronous receive. The buffer for the data being received
- // must be valid for the lifetime of the asynchronous operation.
- template <typename MutableBufferSequence, typename Handler>
- void async_receive(implementation_type& impl,
- const MutableBufferSequence& buffers,
- socket_base::message_flags flags, Handler handler)
- {
- // Allocate and construct an operation to wrap the handler.
- typedef receive_op<MutableBufferSequence, Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- int protocol_type = impl.protocol_.type();
- handler_ptr<alloc_traits> ptr(raw_ptr, impl.socket_,
- protocol_type, buffers, flags, handler);
-
- start_op(impl,
- (flags & socket_base::message_out_of_band)
- ? reactor::except_op : reactor::read_op,
- ptr.get(), (flags & socket_base::message_out_of_band) == 0,
- (impl.protocol_.type() == SOCK_STREAM
- && buffer_sequence_adapter<asio::mutable_buffer,
- MutableBufferSequence>::all_empty(buffers)));
- ptr.release();
- }
-
- // Wait until data can be received without blocking.
- template <typename Handler>
- void async_receive(implementation_type& impl, const null_buffers&,
- socket_base::message_flags flags, Handler handler)
- {
- // Allocate and construct an operation to wrap the handler.
- typedef null_buffers_op<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, handler);
-
- start_op(impl,
- (flags & socket_base::message_out_of_band)
- ? reactor::except_op : reactor::read_op,
- ptr.get(), false, false);
- ptr.release();
+ start_op(impl, reactor::write_op, p.p, false, false);
+ p.v = p.p = 0;
}
// Receive a datagram with the endpoint of the sender. Returns the number of
@@ -1102,47 +231,18 @@ public:
endpoint_type& sender_endpoint, socket_base::message_flags flags,
asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
buffer_sequence_adapter<asio::mutable_buffer,
MutableBufferSequence> bufs(buffers);
- // Receive some data.
- for (;;)
- {
- // Try to complete the operation without blocking.
- std::size_t addr_len = sender_endpoint.capacity();
- int bytes_recvd = socket_ops::recvfrom(impl.socket_, bufs.buffers(),
- bufs.count(), flags, sender_endpoint.data(), &addr_len, ec);
+ std::size_t addr_len = sender_endpoint.capacity();
+ std::size_t bytes_recvd = socket_ops::sync_recvfrom(
+ impl.socket_, impl.state_, bufs.buffers(), bufs.count(),
+ flags, sender_endpoint.data(), &addr_len, ec);
- // Check if operation succeeded.
- if (bytes_recvd > 0)
- {
- sender_endpoint.resize(addr_len);
- return bytes_recvd;
- }
+ if (!ec)
+ sender_endpoint.resize(addr_len);
- // Check for EOF.
- if (bytes_recvd == 0 && impl.protocol_.type() == SOCK_STREAM)
- {
- ec = asio::error::eof;
- return 0;
- }
-
- // Operation failed.
- if ((impl.flags_ & implementation_type::user_set_non_blocking)
- || (ec != asio::error::would_block
- && ec != asio::error::try_again))
- return 0;
-
- // Wait for socket to become ready.
- if (socket_ops::poll_read(impl.socket_, ec) < 0)
- return 0;
- }
+ return bytes_recvd;
}
// Wait until data can be received without blocking.
@@ -1150,12 +250,6 @@ public:
endpoint_type& sender_endpoint, socket_base::message_flags,
asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
// Wait for socket to become ready.
socket_ops::poll_read(impl.socket_, ec);
@@ -1165,105 +259,6 @@ public:
return 0;
}
- template <typename MutableBufferSequence>
- class receive_from_op_base : public reactor_op
- {
- public:
- receive_from_op_base(socket_type socket, int protocol_type,
- const MutableBufferSequence& buffers, endpoint_type& endpoint,
- socket_base::message_flags flags, func_type complete_func)
- : reactor_op(&receive_from_op_base::do_perform, complete_func),
- socket_(socket),
- protocol_type_(protocol_type),
- buffers_(buffers),
- sender_endpoint_(endpoint),
- flags_(flags)
- {
- }
-
- static bool do_perform(reactor_op* base)
- {
- receive_from_op_base* o(static_cast<receive_from_op_base*>(base));
-
- buffer_sequence_adapter<asio::mutable_buffer,
- MutableBufferSequence> bufs(o->buffers_);
-
- for (;;)
- {
- // Receive some data.
- asio::error_code ec;
- std::size_t addr_len = o->sender_endpoint_.capacity();
- int bytes = socket_ops::recvfrom(o->socket_, bufs.buffers(),
- bufs.count(), o->flags_, o->sender_endpoint_.data(), &addr_len, ec);
- if (bytes == 0 && o->protocol_type_ == SOCK_STREAM)
- ec = asio::error::eof;
-
- // Retry operation if interrupted by signal.
- if (ec == asio::error::interrupted)
- continue;
-
- // Check if we need to run the operation again.
- if (ec == asio::error::would_block
- || ec == asio::error::try_again)
- return false;
-
- o->sender_endpoint_.resize(addr_len);
- o->ec_ = ec;
- o->bytes_transferred_ = (bytes < 0 ? 0 : bytes);
- return true;
- }
- }
-
- private:
- socket_type socket_;
- int protocol_type_;
- MutableBufferSequence buffers_;
- endpoint_type& sender_endpoint_;
- socket_base::message_flags flags_;
- };
-
- template <typename MutableBufferSequence, typename Handler>
- class receive_from_op : public receive_from_op_base<MutableBufferSequence>
- {
- public:
- receive_from_op(socket_type socket, int protocol_type,
- const MutableBufferSequence& buffers, endpoint_type& endpoint,
- socket_base::message_flags flags, Handler handler)
- : receive_from_op_base<MutableBufferSequence>(socket, protocol_type,
- buffers, endpoint, flags, &receive_from_op::do_complete),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
- {
- // Take ownership of the handler object.
- receive_from_op* o(static_cast<receive_from_op*>(base));
- typedef handler_alloc_traits<Handler, receive_from_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder2<Handler, asio::error_code, std::size_t>
- handler(o->handler_, o->ec_, o->bytes_transferred_);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- Handler handler_;
- };
-
// Start an asynchronous receive. The buffer for the data being received and
// the sender_endpoint object must both be valid for the lifetime of the
// asynchronous operation.
@@ -1273,18 +268,20 @@ public:
socket_base::message_flags flags, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef receive_from_op<MutableBufferSequence, Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
+ typedef reactive_socket_recvfrom_op<MutableBufferSequence,
+ endpoint_type, Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
int protocol_type = impl.protocol_.type();
- handler_ptr<alloc_traits> ptr(raw_ptr, impl.socket_,
- protocol_type, buffers, sender_endpoint, flags, handler);
+ p.p = new (p.v) op(impl.socket_, protocol_type,
+ buffers, sender_endpoint, flags, handler);
start_op(impl,
(flags & socket_base::message_out_of_band)
? reactor::except_op : reactor::read_op,
- ptr.get(), true, false);
- ptr.release();
+ p.p, true, false);
+ p.v = p.p = 0;
}
// Wait until data can be received without blocking.
@@ -1294,10 +291,11 @@ public:
socket_base::message_flags flags, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef null_buffers_op<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, handler);
+ typedef reactive_null_buffers_op<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(handler);
// Reset endpoint since it can be given no sensible value at this time.
sender_endpoint = endpoint_type();
@@ -1305,8 +303,8 @@ public:
start_op(impl,
(flags & socket_base::message_out_of_band)
? reactor::except_op : reactor::read_op,
- ptr.get(), false, false);
- ptr.release();
+ p.p, false, false);
+ p.v = p.p = 0;
}
// Accept a new connection.
@@ -1314,12 +312,6 @@ public:
asio::error_code accept(implementation_type& impl,
Socket& peer, endpoint_type* peer_endpoint, asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
// We cannot accept a socket that is already open.
if (peer.is_open())
{
@@ -1327,181 +319,22 @@ public:
return ec;
}
- // Accept a socket.
- for (;;)
- {
- // Try to complete the operation without blocking.
- socket_holder new_socket;
- std::size_t addr_len = 0;
- if (peer_endpoint)
- {
- addr_len = peer_endpoint->capacity();
- new_socket.reset(socket_ops::accept(impl.socket_,
- peer_endpoint->data(), &addr_len, ec));
- }
- else
- {
- new_socket.reset(socket_ops::accept(impl.socket_, 0, 0, ec));
- }
-
- // Check if operation succeeded.
- if (new_socket.get() >= 0)
- {
- if (peer_endpoint)
- peer_endpoint->resize(addr_len);
- peer.assign(impl.protocol_, new_socket.get(), ec);
- if (!ec)
- new_socket.release();
- return ec;
- }
+ std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0;
+ socket_holder new_socket(socket_ops::sync_accept(impl.socket_,
+ impl.state_, peer_endpoint ? peer_endpoint->data() : 0,
+ peer_endpoint ? &addr_len : 0, ec));
- // Operation failed.
- if (ec == asio::error::would_block
- || ec == asio::error::try_again)
- {
- if (impl.flags_ & implementation_type::user_set_non_blocking)
- return ec;
- // Fall through to retry operation.
- }
- else if (ec == asio::error::connection_aborted)
- {
- if (impl.flags_ & implementation_type::enable_connection_aborted)
- return ec;
- // Fall through to retry operation.
- }
-#if defined(EPROTO)
- else if (ec.value() == EPROTO)
- {
- if (impl.flags_ & implementation_type::enable_connection_aborted)
- return ec;
- // Fall through to retry operation.
- }
-#endif // defined(EPROTO)
- else
- return ec;
-
- // Wait for socket to become ready.
- if (socket_ops::poll_read(impl.socket_, ec) < 0)
- return ec;
- }
- }
-
- template <typename Socket>
- class accept_op_base : public reactor_op
- {
- public:
- accept_op_base(socket_type socket, Socket& peer,
- const protocol_type& protocol, endpoint_type* peer_endpoint,
- bool enable_connection_aborted, func_type complete_func)
- : reactor_op(&accept_op_base::do_perform, complete_func),
- socket_(socket),
- peer_(peer),
- protocol_(protocol),
- peer_endpoint_(peer_endpoint),
- enable_connection_aborted_(enable_connection_aborted)
- {
- }
-
- static bool do_perform(reactor_op* base)
+ // On success, assign new connection to peer socket object.
+ if (new_socket.get() != invalid_socket)
{
- accept_op_base* o(static_cast<accept_op_base*>(base));
-
- for (;;)
- {
- // Accept the waiting connection.
- asio::error_code ec;
- socket_holder new_socket;
- std::size_t addr_len = 0;
- std::size_t* addr_len_p = 0;
- socket_addr_type* addr = 0;
- if (o->peer_endpoint_)
- {
- addr_len = o->peer_endpoint_->capacity();
- addr_len_p = &addr_len;
- addr = o->peer_endpoint_->data();
- }
- new_socket.reset(socket_ops::accept(o->socket_, addr, addr_len_p, ec));
-
- // Retry operation if interrupted by signal.
- if (ec == asio::error::interrupted)
- continue;
-
- // Check if we need to run the operation again.
- if (ec == asio::error::would_block
- || ec == asio::error::try_again)
- return false;
- if (ec == asio::error::connection_aborted
- && !o->enable_connection_aborted_)
- return false;
-#if defined(EPROTO)
- if (ec.value() == EPROTO && !o->enable_connection_aborted_)
- return false;
-#endif // defined(EPROTO)
-
- // Transfer ownership of the new socket to the peer object.
- if (!ec)
- {
- if (o->peer_endpoint_)
- o->peer_endpoint_->resize(addr_len);
- o->peer_.assign(o->protocol_, new_socket.get(), ec);
- if (!ec)
- new_socket.release();
- }
-
- o->ec_ = ec;
- return true;
- }
- }
-
- private:
- socket_type socket_;
- Socket& peer_;
- protocol_type protocol_;
- endpoint_type* peer_endpoint_;
- bool enable_connection_aborted_;
- };
-
- template <typename Socket, typename Handler>
- class accept_op : public accept_op_base<Socket>
- {
- public:
- accept_op(socket_type socket, Socket& peer, const protocol_type& protocol,
- endpoint_type* peer_endpoint, bool enable_connection_aborted,
- Handler handler)
- : accept_op_base<Socket>(socket, peer, protocol, peer_endpoint,
- enable_connection_aborted, &accept_op::do_complete),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
- {
- // Take ownership of the handler object.
- accept_op* o(static_cast<accept_op*>(base));
- typedef handler_alloc_traits<Handler, accept_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder1<Handler, asio::error_code>
- handler(o->handler_, o->ec_);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
+ if (peer_endpoint)
+ peer_endpoint->resize(addr_len);
+ if (!peer.assign(impl.protocol_, new_socket.get(), ec))
+ new_socket.release();
}
- private:
- Handler handler_;
- };
+ return ec;
+ }
// Start an asynchronous accept. The peer and peer_endpoint objects
// must be valid until the accept's handler is invoked.
@@ -1510,230 +343,41 @@ public:
endpoint_type* peer_endpoint, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef accept_op<Socket, Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- bool enable_connection_aborted =
- (impl.flags_ & implementation_type::enable_connection_aborted) != 0;
- handler_ptr<alloc_traits> ptr(raw_ptr, impl.socket_, peer,
- impl.protocol_, peer_endpoint, enable_connection_aborted, handler);
+ typedef reactive_socket_accept_op<Socket, Protocol, Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl.socket_, impl.state_, peer,
+ impl.protocol_, peer_endpoint, handler);
- start_accept_op(impl, ptr.get(), peer.is_open());
- ptr.release();
+ start_accept_op(impl, p.p, peer.is_open());
+ p.v = p.p = 0;
}
// Connect the socket to the specified endpoint.
asio::error_code connect(implementation_type& impl,
const endpoint_type& peer_endpoint, asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
- // Perform the connect operation.
- socket_ops::connect(impl.socket_,
+ socket_ops::sync_connect(impl.socket_,
peer_endpoint.data(), peer_endpoint.size(), ec);
- if (ec != asio::error::in_progress
- && ec != asio::error::would_block)
- {
- // The connect operation finished immediately.
- return ec;
- }
-
- // Wait for socket to become ready.
- if (socket_ops::poll_connect(impl.socket_, ec) < 0)
- return ec;
-
- // Get the error code from the connect operation.
- int connect_error = 0;
- size_t connect_error_len = sizeof(connect_error);
- if (socket_ops::getsockopt(impl.socket_, SOL_SOCKET, SO_ERROR,
- &connect_error, &connect_error_len, ec) == socket_error_retval)
- return ec;
-
- // Return the result of the connect operation.
- ec = asio::error_code(connect_error,
- asio::error::get_system_category());
return ec;
}
- class connect_op_base : public reactor_op
- {
- public:
- connect_op_base(socket_type socket, func_type complete_func)
- : reactor_op(&connect_op_base::do_perform, complete_func),
- socket_(socket)
- {
- }
-
- static bool do_perform(reactor_op* base)
- {
- connect_op_base* o(static_cast<connect_op_base*>(base));
-
- // Get the error code from the connect operation.
- int connect_error = 0;
- size_t connect_error_len = sizeof(connect_error);
- if (socket_ops::getsockopt(o->socket_, SOL_SOCKET, SO_ERROR,
- &connect_error, &connect_error_len, o->ec_) == socket_error_retval)
- return true;
-
- // The connection failed so the handler will be posted with an error code.
- if (connect_error)
- {
- o->ec_ = asio::error_code(connect_error,
- asio::error::get_system_category());
- }
-
- return true;
- }
-
- private:
- socket_type socket_;
- };
-
- template <typename Handler>
- class connect_op : public connect_op_base
- {
- public:
- connect_op(socket_type socket, Handler handler)
- : connect_op_base(socket, &connect_op::do_complete),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
- {
- // Take ownership of the handler object.
- connect_op* o(static_cast<connect_op*>(base));
- typedef handler_alloc_traits<Handler, connect_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder1<Handler, asio::error_code>
- handler(o->handler_, o->ec_);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- Handler handler_;
- };
-
// Start an asynchronous connect.
template <typename Handler>
void async_connect(implementation_type& impl,
const endpoint_type& peer_endpoint, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef connect_op<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, impl.socket_, handler);
-
- start_connect_op(impl, ptr.get(), peer_endpoint);
- ptr.release();
- }
-
-private:
- // Start the asynchronous read or write operation.
- void start_op(implementation_type& impl, int op_type,
- reactor_op* op, bool non_blocking, bool noop)
- {
- if (!noop)
- {
- if (is_open(impl))
- {
- if (!non_blocking || is_non_blocking(impl)
- || set_non_blocking(impl, op->ec_))
- {
- reactor_.start_op(op_type, impl.socket_,
- impl.reactor_data_, op, non_blocking);
- return;
- }
- }
- else
- op->ec_ = asio::error::bad_descriptor;
- }
+ typedef reactive_socket_connect_op<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl.socket_, handler);
- io_service_impl_.post_immediate_completion(op);
+ start_connect_op(impl, p.p, peer_endpoint.data(), peer_endpoint.size());
+ p.v = p.p = 0;
}
-
- // Start the asynchronous accept operation.
- void start_accept_op(implementation_type& impl,
- reactor_op* op, bool peer_is_open)
- {
- if (!peer_is_open)
- start_op(impl, reactor::read_op, op, true, false);
- else
- {
- op->ec_ = asio::error::already_open;
- io_service_impl_.post_immediate_completion(op);
- }
- }
-
- // Start the asynchronous connect operation.
- void start_connect_op(implementation_type& impl,
- reactor_op* op, const endpoint_type& peer_endpoint)
- {
- if (is_open(impl))
- {
- if (is_non_blocking(impl) || set_non_blocking(impl, op->ec_))
- {
- if (socket_ops::connect(impl.socket_, peer_endpoint.data(),
- peer_endpoint.size(), op->ec_) != 0)
- {
- if (op->ec_ == asio::error::in_progress
- || op->ec_ == asio::error::would_block)
- {
- op->ec_ = asio::error_code();
- reactor_.start_op(reactor::connect_op,
- impl.socket_, impl.reactor_data_, op, false);
- return;
- }
- }
- }
- }
- else
- op->ec_ = asio::error::bad_descriptor;
-
- io_service_impl_.post_immediate_completion(op);
- }
-
- // Determine whether the socket has been set non-blocking.
- bool is_non_blocking(implementation_type& impl) const
- {
- return (impl.flags_ & implementation_type::non_blocking);
- }
-
- // Set the internal non-blocking flag.
- bool set_non_blocking(implementation_type& impl,
- asio::error_code& ec)
- {
- ioctl_arg_type non_blocking = 1;
- if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec))
- return false;
- impl.flags_ |= implementation_type::internal_non_blocking;
- return true;
- }
-
- // The io_service implementation used to post completions.
- io_service_impl& io_service_impl_;
-
- // The selector that performs event demultiplexing for the service.
- reactor& reactor_;
};
} // namespace detail
@@ -1741,4 +385,6 @@ private:
#include "asio/detail/pop_options.hpp"
+#endif // !defined(ASIO_HAS_IOCP)
+
#endif // ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP
diff --git a/ext/asio/asio/detail/reactive_socket_service_base.hpp b/ext/asio/asio/detail/reactive_socket_service_base.hpp
new file mode 100644
index 0000000..b26d73e
--- /dev/null
+++ b/ext/asio/asio/detail/reactive_socket_service_base.hpp
@@ -0,0 +1,298 @@
+//
+// detail/reactive_socket_service_base.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP
+#define ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if !defined(ASIO_HAS_IOCP)
+
+#include <boost/utility/addressof.hpp>
+#include "asio/buffer.hpp"
+#include "asio/error.hpp"
+#include "asio/io_service.hpp"
+#include "asio/socket_base.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/reactive_null_buffers_op.hpp"
+#include "asio/detail/reactive_socket_recv_op.hpp"
+#include "asio/detail/reactive_socket_send_op.hpp"
+#include "asio/detail/reactor.hpp"
+#include "asio/detail/reactor_op.hpp"
+#include "asio/detail/socket_holder.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/socket_types.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+class reactive_socket_service_base
+{
+public:
+ // The native type of a socket.
+ typedef socket_type native_type;
+
+ // The implementation type of the socket.
+ struct base_implementation_type
+ {
+ // The native socket representation.
+ socket_type socket_;
+
+ // The current state of the socket.
+ socket_ops::state_type state_;
+
+ // Per-descriptor data used by the reactor.
+ reactor::per_descriptor_data reactor_data_;
+ };
+
+ // Constructor.
+ ASIO_DECL reactive_socket_service_base(
+ asio::io_service& io_service);
+
+ // Destroy all user-defined handler objects owned by the service.
+ ASIO_DECL void shutdown_service();
+
+ // Construct a new socket implementation.
+ ASIO_DECL void construct(base_implementation_type& impl);
+
+ // Destroy a socket implementation.
+ ASIO_DECL void destroy(base_implementation_type& impl);
+
+ // Determine whether the socket is open.
+ bool is_open(const base_implementation_type& impl) const
+ {
+ return impl.socket_ != invalid_socket;
+ }
+
+ // Destroy a socket implementation.
+ ASIO_DECL asio::error_code close(
+ base_implementation_type& impl, asio::error_code& ec);
+
+ // Get the native socket representation.
+ native_type native(base_implementation_type& impl)
+ {
+ return impl.socket_;
+ }
+
+ // Cancel all operations associated with the socket.
+ ASIO_DECL asio::error_code cancel(
+ base_implementation_type& impl, asio::error_code& ec);
+
+ // Determine whether the socket is at the out-of-band data mark.
+ bool at_mark(const base_implementation_type& impl,
+ asio::error_code& ec) const
+ {
+ return socket_ops::sockatmark(impl.socket_, ec);
+ }
+
+ // Determine the number of bytes available for reading.
+ std::size_t available(const base_implementation_type& impl,
+ asio::error_code& ec) const
+ {
+ return socket_ops::available(impl.socket_, ec);
+ }
+
+ // Place the socket into the state where it will listen for new connections.
+ asio::error_code listen(base_implementation_type& impl,
+ int backlog, asio::error_code& ec)
+ {
+ socket_ops::listen(impl.socket_, backlog, ec);
+ return ec;
+ }
+
+ // Perform an IO control command on the socket.
+ template <typename IO_Control_Command>
+ asio::error_code io_control(base_implementation_type& impl,
+ IO_Control_Command& command, asio::error_code& ec)
+ {
+ socket_ops::ioctl(impl.socket_, impl.state_, command.name(),
+ static_cast<ioctl_arg_type*>(command.data()), ec);
+ return ec;
+ }
+
+ /// Disable sends or receives on the socket.
+ asio::error_code shutdown(base_implementation_type& impl,
+ socket_base::shutdown_type what, asio::error_code& ec)
+ {
+ socket_ops::shutdown(impl.socket_, what, ec);
+ return ec;
+ }
+
+ // Send the given data to the peer.
+ template <typename ConstBufferSequence>
+ size_t send(base_implementation_type& impl,
+ const ConstBufferSequence& buffers,
+ socket_base::message_flags flags, asio::error_code& ec)
+ {
+ buffer_sequence_adapter<asio::const_buffer,
+ ConstBufferSequence> bufs(buffers);
+
+ return socket_ops::sync_send(impl.socket_, impl.state_,
+ bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec);
+ }
+
+ // Wait until data can be sent without blocking.
+ size_t send(base_implementation_type& impl, const null_buffers&,
+ socket_base::message_flags, asio::error_code& ec)
+ {
+ // Wait for socket to become ready.
+ socket_ops::poll_write(impl.socket_, ec);
+
+ return 0;
+ }
+
+ // Start an asynchronous send. The data being sent must be valid for the
+ // lifetime of the asynchronous operation.
+ template <typename ConstBufferSequence, typename Handler>
+ void async_send(base_implementation_type& impl,
+ const ConstBufferSequence& buffers,
+ socket_base::message_flags flags, Handler handler)
+ {
+ // Allocate and construct an operation to wrap the handler.
+ typedef reactive_socket_send_op<ConstBufferSequence, Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl.socket_, buffers, flags, handler);
+
+ start_op(impl, reactor::write_op, p.p, true,
+ ((impl.state_ & socket_ops::stream_oriented)
+ && buffer_sequence_adapter<asio::const_buffer,
+ ConstBufferSequence>::all_empty(buffers)));
+ p.v = p.p = 0;
+ }
+
+ // Start an asynchronous wait until data can be sent without blocking.
+ template <typename Handler>
+ void async_send(base_implementation_type& impl, const null_buffers&,
+ socket_base::message_flags, Handler handler)
+ {
+ // Allocate and construct an operation to wrap the handler.
+ typedef reactive_null_buffers_op<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(handler);
+
+ start_op(impl, reactor::write_op, p.p, false, false);
+ p.v = p.p = 0;
+ }
+
+ // Receive some data from the peer. Returns the number of bytes received.
+ template <typename MutableBufferSequence>
+ size_t receive(base_implementation_type& impl,
+ const MutableBufferSequence& buffers,
+ socket_base::message_flags flags, asio::error_code& ec)
+ {
+ buffer_sequence_adapter<asio::mutable_buffer,
+ MutableBufferSequence> bufs(buffers);
+
+ return socket_ops::sync_recv(impl.socket_, impl.state_,
+ bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec);
+ }
+
+ // Wait until data can be received without blocking.
+ size_t receive(base_implementation_type& impl, const null_buffers&,
+ socket_base::message_flags, asio::error_code& ec)
+ {
+ // Wait for socket to become ready.
+ socket_ops::poll_read(impl.socket_, ec);
+
+ return 0;
+ }
+
+ // Start an asynchronous receive. The buffer for the data being received
+ // must be valid for the lifetime of the asynchronous operation.
+ template <typename MutableBufferSequence, typename Handler>
+ void async_receive(base_implementation_type& impl,
+ const MutableBufferSequence& buffers,
+ socket_base::message_flags flags, Handler handler)
+ {
+ // Allocate and construct an operation to wrap the handler.
+ typedef reactive_socket_recv_op<MutableBufferSequence, Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl.socket_, impl.state_, buffers, flags, handler);
+
+ start_op(impl,
+ (flags & socket_base::message_out_of_band)
+ ? reactor::except_op : reactor::read_op,
+ p.p, (flags & socket_base::message_out_of_band) == 0,
+ ((impl.state_ & socket_ops::stream_oriented)
+ && buffer_sequence_adapter<asio::mutable_buffer,
+ MutableBufferSequence>::all_empty(buffers)));
+ p.v = p.p = 0;
+ }
+
+ // Wait until data can be received without blocking.
+ template <typename Handler>
+ void async_receive(base_implementation_type& impl, const null_buffers&,
+ socket_base::message_flags flags, Handler handler)
+ {
+ // Allocate and construct an operation to wrap the handler.
+ typedef reactive_null_buffers_op<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(handler);
+
+ start_op(impl,
+ (flags & socket_base::message_out_of_band)
+ ? reactor::except_op : reactor::read_op,
+ p.p, false, false);
+ p.v = p.p = 0;
+ }
+
+protected:
+ // Open a new socket implementation.
+ ASIO_DECL asio::error_code do_open(
+ base_implementation_type& impl, int af,
+ int type, int protocol, asio::error_code& ec);
+
+ // Assign a native socket to a socket implementation.
+ ASIO_DECL asio::error_code do_assign(
+ base_implementation_type& impl, int type,
+ const native_type& native_socket, asio::error_code& ec);
+
+ // Start the asynchronous read or write operation.
+ ASIO_DECL void start_op(base_implementation_type& impl, int op_type,
+ reactor_op* op, bool non_blocking, bool noop);
+
+ // Start the asynchronous accept operation.
+ ASIO_DECL void start_accept_op(base_implementation_type& impl,
+ reactor_op* op, bool peer_is_open);
+
+ // Start the asynchronous connect operation.
+ ASIO_DECL void start_connect_op(base_implementation_type& impl,
+ reactor_op* op, const socket_addr_type* addr, size_t addrlen);
+
+ // The selector that performs event demultiplexing for the service.
+ reactor& reactor_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/reactive_socket_service_base.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // !defined(ASIO_HAS_IOCP)
+
+#endif // ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP
diff --git a/ext/asio/asio/detail/reactor.hpp b/ext/asio/asio/detail/reactor.hpp
index 98ac360..0e938c9 100644
--- a/ext/asio/asio/detail/reactor.hpp
+++ b/ext/asio/asio/detail/reactor.hpp
@@ -1,8 +1,8 @@
//
-// reactor.hpp
-// ~~~~~~~~~~~
+// detail/reactor.hpp
+// ~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,8 +15,6 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
#include "asio/detail/reactor_fwd.hpp"
#if defined(ASIO_HAS_EPOLL)
@@ -29,6 +27,4 @@
# include "asio/detail/select_reactor.hpp"
#endif
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_REACTOR_HPP
diff --git a/ext/asio/asio/detail/reactor_fwd.hpp b/ext/asio/asio/detail/reactor_fwd.hpp
index 9c33be5..d45d768 100644
--- a/ext/asio/asio/detail/reactor_fwd.hpp
+++ b/ext/asio/asio/detail/reactor_fwd.hpp
@@ -1,8 +1,8 @@
//
-// reactor_fwd.hpp
-// ~~~~~~~~~~~~~~~
+// detail/reactor_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,19 +15,25 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/dev_poll_reactor_fwd.hpp"
-#include "asio/detail/epoll_reactor_fwd.hpp"
-#include "asio/detail/kqueue_reactor_fwd.hpp"
-#include "asio/detail/select_reactor_fwd.hpp"
-#include "asio/detail/win_iocp_io_service_fwd.hpp"
+#if defined(ASIO_HAS_IOCP)
+# include "asio/detail/select_reactor_fwd.hpp"
+#elif defined(ASIO_HAS_EPOLL)
+# include "asio/detail/epoll_reactor_fwd.hpp"
+#elif defined(ASIO_HAS_KQUEUE)
+# include "asio/detail/kqueue_reactor_fwd.hpp"
+#elif defined(ASIO_HAS_DEV_POLL)
+# include "asio/detail/dev_poll_reactor_fwd.hpp"
+#else
+# include "asio/detail/select_reactor_fwd.hpp"
+#endif
namespace asio {
namespace detail {
#if defined(ASIO_HAS_IOCP)
-typedef select_reactor<true> reactor;
+typedef select_reactor reactor;
#elif defined(ASIO_HAS_EPOLL)
typedef epoll_reactor reactor;
#elif defined(ASIO_HAS_KQUEUE)
@@ -35,12 +41,10 @@ typedef kqueue_reactor reactor;
#elif defined(ASIO_HAS_DEV_POLL)
typedef dev_poll_reactor reactor;
#else
-typedef select_reactor<false> reactor;
+typedef select_reactor reactor;
#endif
} // namespace detail
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_REACTOR_FWD_HPP
diff --git a/ext/asio/asio/detail/reactor_op.hpp b/ext/asio/asio/detail/reactor_op.hpp
index cd557fa..3009cf9 100644
--- a/ext/asio/asio/detail/reactor_op.hpp
+++ b/ext/asio/asio/detail/reactor_op.hpp
@@ -1,8 +1,8 @@
//
-// reactor_op.hpp
-// ~~~~~~~~~~~~
+// detail/reactor_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,10 +15,11 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/detail/operation.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
diff --git a/ext/asio/asio/detail/reactor_op_queue.hpp b/ext/asio/asio/detail/reactor_op_queue.hpp
index 233c0ae..6eed398 100644
--- a/ext/asio/asio/detail/reactor_op_queue.hpp
+++ b/ext/asio/asio/detail/reactor_op_queue.hpp
@@ -1,8 +1,8 @@
//
-// reactor_op_queue.hpp
-// ~~~~~~~~~~~~~~~~~~~~
+// detail/reactor_op_queue.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,13 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/error.hpp"
+#include "asio/detail/config.hpp"
#include "asio/detail/hash_map.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/op_queue.hpp"
#include "asio/detail/reactor_op.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
diff --git a/ext/asio/asio/detail/regex_fwd.hpp b/ext/asio/asio/detail/regex_fwd.hpp
new file mode 100644
index 0000000..9da295b
--- /dev/null
+++ b/ext/asio/asio/detail/regex_fwd.hpp
@@ -0,0 +1,31 @@
+//
+// detail/regex_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_REGEX_FWD_HPP
+#define ASIO_DETAIL_REGEX_FWD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include <boost/regex_fwd.hpp>
+#include <boost/regex/v4/match_flags.hpp>
+
+namespace boost {
+
+template <class BidiIterator>
+struct sub_match;
+
+template <class BidiIterator, class Allocator>
+class match_results;
+
+} // namespace boost
+
+#endif // ASIO_DETAIL_REGEX_FWD_HPP
diff --git a/ext/asio/asio/detail/resolve_endpoint_op.hpp b/ext/asio/asio/detail/resolve_endpoint_op.hpp
new file mode 100644
index 0000000..0931fd0
--- /dev/null
+++ b/ext/asio/asio/detail/resolve_endpoint_op.hpp
@@ -0,0 +1,116 @@
+//
+// detail/resolve_endpoint_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_RESOLVER_ENDPOINT_OP_HPP
+#define ASIO_DETAIL_RESOLVER_ENDPOINT_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <boost/utility/addressof.hpp>
+#include "asio/error.hpp"
+#include "asio/io_service.hpp"
+#include "asio/ip/basic_resolver_iterator.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/operation.hpp"
+#include "asio/detail/socket_ops.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Protocol, typename Handler>
+class resolve_endpoint_op : public operation
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(resolve_endpoint_op);
+
+ typedef typename Protocol::endpoint endpoint_type;
+ typedef asio::ip::basic_resolver_iterator<Protocol> iterator_type;
+
+ resolve_endpoint_op(socket_ops::weak_cancel_token_type cancel_token,
+ const endpoint_type& endpoint, io_service_impl& ios, Handler handler)
+ : operation(&resolve_endpoint_op::do_complete),
+ cancel_token_(cancel_token),
+ endpoint_(endpoint),
+ io_service_impl_(ios),
+ handler_(handler)
+ {
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
+ {
+ // Take ownership of the operation object.
+ resolve_endpoint_op* o(static_cast<resolve_endpoint_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+ if (owner && owner != &o->io_service_impl_)
+ {
+ // The operation is being run on the worker io_service. Time to perform
+ // the resolver operation.
+
+ // Perform the blocking endpoint resolution operation.
+ char host_name[NI_MAXHOST];
+ char service_name[NI_MAXSERV];
+ socket_ops::background_getnameinfo(o->cancel_token_, o->endpoint_.data(),
+ o->endpoint_.size(), host_name, NI_MAXHOST, service_name, NI_MAXSERV,
+ o->endpoint_.protocol().type(), o->ec_);
+ o->iter_ = iterator_type::create(o->endpoint_, host_name, service_name);
+
+ // Pass operation back to main io_service for completion.
+ o->io_service_impl_.post_deferred_completion(o);
+ p.v = p.p = 0;
+ }
+ else
+ {
+ // The operation has been returned to the main io_service. The completion
+ // handler is ready to be delivered.
+
+ // Make a copy of the handler so that the memory can be deallocated
+ // before the upcall is made. Even if we're not about to make an upcall,
+ // a sub-object of the handler may be the true owner of the memory
+ // associated with the handler. Consequently, a local copy of the handler
+ // is required to ensure that any owning sub-object remains valid until
+ // after we have deallocated the memory here.
+ detail::binder2<Handler, asio::error_code, iterator_type>
+ handler(o->handler_, o->ec_, o->iter_);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+ }
+
+private:
+ socket_ops::weak_cancel_token_type cancel_token_;
+ endpoint_type endpoint_;
+ io_service_impl& io_service_impl_;
+ Handler handler_;
+ asio::error_code ec_;
+ iterator_type iter_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_RESOLVER_ENDPOINT_OP_HPP
diff --git a/ext/asio/asio/detail/resolve_op.hpp b/ext/asio/asio/detail/resolve_op.hpp
new file mode 100644
index 0000000..a04fbc3
--- /dev/null
+++ b/ext/asio/asio/detail/resolve_op.hpp
@@ -0,0 +1,126 @@
+//
+// detail/resolve_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_RESOLVE_OP_HPP
+#define ASIO_DETAIL_RESOLVE_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <boost/utility/addressof.hpp>
+#include "asio/error.hpp"
+#include "asio/io_service.hpp"
+#include "asio/ip/basic_resolver_iterator.hpp"
+#include "asio/ip/basic_resolver_query.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/operation.hpp"
+#include "asio/detail/socket_ops.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Protocol, typename Handler>
+class resolve_op : public operation
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(resolve_op);
+
+ typedef asio::ip::basic_resolver_query<Protocol> query_type;
+ typedef asio::ip::basic_resolver_iterator<Protocol> iterator_type;
+
+ resolve_op(socket_ops::weak_cancel_token_type cancel_token,
+ const query_type& query, io_service_impl& ios, Handler handler)
+ : operation(&resolve_op::do_complete),
+ cancel_token_(cancel_token),
+ query_(query),
+ io_service_impl_(ios),
+ handler_(handler),
+ addrinfo_(0)
+ {
+ }
+
+ ~resolve_op()
+ {
+ if (addrinfo_)
+ socket_ops::freeaddrinfo(addrinfo_);
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
+ {
+ // Take ownership of the operation object.
+ resolve_op* o(static_cast<resolve_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+ if (owner && owner != &o->io_service_impl_)
+ {
+ // The operation is being run on the worker io_service. Time to perform
+ // the resolver operation.
+
+ // Perform the blocking host resolution operation.
+ socket_ops::background_getaddrinfo(o->cancel_token_,
+ o->query_.host_name().c_str(), o->query_.service_name().c_str(),
+ o->query_.hints(), &o->addrinfo_, o->ec_);
+
+ // Pass operation back to main io_service for completion.
+ o->io_service_impl_.post_deferred_completion(o);
+ p.v = p.p = 0;
+ }
+ else
+ {
+ // The operation has been returned to the main io_service. The completion
+ // handler is ready to be delivered.
+
+ // Make a copy of the handler so that the memory can be deallocated
+ // before the upcall is made. Even if we're not about to make an upcall,
+ // a sub-object of the handler may be the true owner of the memory
+ // associated with the handler. Consequently, a local copy of the handler
+ // is required to ensure that any owning sub-object remains valid until
+ // after we have deallocated the memory here.
+ detail::binder2<Handler, asio::error_code, iterator_type>
+ handler(o->handler_, o->ec_, iterator_type());
+ p.h = boost::addressof(handler.handler_);
+ if (o->addrinfo_)
+ {
+ handler.arg2_ = iterator_type::create(o->addrinfo_,
+ o->query_.host_name(), o->query_.service_name());
+ }
+ p.reset();
+
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+ }
+
+private:
+ socket_ops::weak_cancel_token_type cancel_token_;
+ query_type query_;
+ io_service_impl& io_service_impl_;
+ Handler handler_;
+ asio::error_code ec_;
+ asio::detail::addrinfo_type* addrinfo_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_RESOLVE_OP_HPP
diff --git a/ext/asio/asio/detail/resolver_service.hpp b/ext/asio/asio/detail/resolver_service.hpp
index 562dc10..c2c3b0c 100644
--- a/ext/asio/asio/detail/resolver_service.hpp
+++ b/ext/asio/asio/detail/resolver_service.hpp
@@ -1,8 +1,8 @@
//
-// resolver_service.hpp
-// ~~~~~~~~~~~~~~~~~~~~
+// detail/resolver_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,68 +15,25 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <cstring>
-#include <boost/scoped_ptr.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error.hpp"
-#include "asio/io_service.hpp"
+#include "asio/detail/config.hpp"
#include "asio/ip/basic_resolver_iterator.hpp"
#include "asio/ip/basic_resolver_query.hpp"
-#include "asio/detail/bind_handler.hpp"
-#include "asio/detail/fenced_block.hpp"
-#include "asio/detail/mutex.hpp"
-#include "asio/detail/noncopyable.hpp"
-#include "asio/detail/operation.hpp"
-#include "asio/detail/service_base.hpp"
-#include "asio/detail/socket_ops.hpp"
-#include "asio/detail/socket_types.hpp"
-#include "asio/detail/thread.hpp"
+#include "asio/detail/resolve_endpoint_op.hpp"
+#include "asio/detail/resolve_op.hpp"
+#include "asio/detail/resolver_service_base.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
template <typename Protocol>
-class resolver_service
- : public asio::detail::service_base<resolver_service<Protocol> >
+class resolver_service : public resolver_service_base
{
-private:
- // Helper class to perform exception-safe cleanup of addrinfo objects.
- class auto_addrinfo
- : private asio::detail::noncopyable
- {
- public:
- explicit auto_addrinfo(asio::detail::addrinfo_type* ai)
- : ai_(ai)
- {
- }
-
- ~auto_addrinfo()
- {
- if (ai_)
- socket_ops::freeaddrinfo(ai_);
- }
-
- operator asio::detail::addrinfo_type*()
- {
- return ai_;
- }
-
- private:
- asio::detail::addrinfo_type* ai_;
- };
-
public:
- // The implementation type of the resolver. The shared pointer is used as a
- // cancellation token to indicate to the background thread that the operation
- // has been cancelled.
- typedef boost::shared_ptr<void> implementation_type;
- struct noop_deleter { void operator()(void*) {} };
+ // The implementation type of the resolver. A cancellation token is used to
+ // indicate to the background thread that the operation has been cancelled.
+ typedef socket_ops::shared_cancel_token_type implementation_type;
// The endpoint type.
typedef typename Protocol::endpoint endpoint_type;
@@ -89,55 +46,8 @@ public:
// Constructor.
resolver_service(asio::io_service& io_service)
- : asio::detail::service_base<
- resolver_service<Protocol> >(io_service),
- mutex_(),
- io_service_impl_(asio::use_service<io_service_impl>(io_service)),
- work_io_service_(new asio::io_service),
- work_io_service_impl_(asio::use_service<
- io_service_impl>(*work_io_service_)),
- work_(new asio::io_service::work(*work_io_service_)),
- work_thread_(0)
- {
- }
-
- // Destructor.
- ~resolver_service()
+ : resolver_service_base(io_service)
{
- shutdown_service();
- }
-
- // Destroy all user-defined handler objects owned by the service.
- void shutdown_service()
- {
- work_.reset();
- if (work_io_service_)
- {
- work_io_service_->stop();
- if (work_thread_)
- {
- work_thread_->join();
- work_thread_.reset();
- }
- work_io_service_.reset();
- }
- }
-
- // Construct a new resolver implementation.
- void construct(implementation_type& impl)
- {
- impl.reset(static_cast<void*>(0), noop_deleter());
- }
-
- // Destroy a resolver implementation.
- void destroy(implementation_type&)
- {
- }
-
- // Cancel pending asynchronous operations.
- void cancel(implementation_type& impl)
- {
- impl.reset(static_cast<void*>(0), noop_deleter());
}
// Resolve a query to a list of entries.
@@ -145,293 +55,60 @@ public:
asio::error_code& ec)
{
asio::detail::addrinfo_type* address_info = 0;
- std::string host_name = query.host_name();
- std::string service_name = query.service_name();
- asio::detail::addrinfo_type hints = query.hints();
- socket_ops::getaddrinfo(!host_name.empty() ? host_name.c_str() : 0,
- service_name.c_str(), &hints, &address_info, ec);
+ socket_ops::getaddrinfo(query.host_name().c_str(),
+ query.service_name().c_str(), query.hints(), &address_info, ec);
auto_addrinfo auto_address_info(address_info);
- if (ec)
- return iterator_type();
-
- return iterator_type::create(address_info, host_name, service_name);
+ return ec ? iterator_type() : iterator_type::create(
+ address_info, query.host_name(), query.service_name());
}
- template <typename Handler>
- class resolve_op
- : public operation
- {
- public:
- resolve_op(implementation_type impl, const query_type& query,
- io_service_impl& io_service_impl, Handler handler)
- : operation(&resolve_op::do_complete),
- impl_(impl),
- query_(query),
- io_service_impl_(io_service_impl),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
- {
- // Take ownership of the operation object.
- resolve_op* o(static_cast<resolve_op*>(base));
- typedef handler_alloc_traits<Handler, resolve_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- if (owner)
- {
- if (owner != &o->io_service_impl_)
- {
- // The operation is being run on the worker io_service. Time to
- // perform the resolver operation.
-
- if (o->impl_.expired())
- {
- // THe operation has been cancelled.
- o->ec_ = asio::error::operation_aborted;
- }
- else
- {
- // Perform the blocking host resolution operation.
- asio::detail::addrinfo_type* address_info = 0;
- std::string host_name = o->query_.host_name();
- std::string service_name = o->query_.service_name();
- asio::detail::addrinfo_type hints = o->query_.hints();
- socket_ops::getaddrinfo(!host_name.empty() ? host_name.c_str() : 0,
- service_name.c_str(), &hints, &address_info, o->ec_);
- auto_addrinfo auto_address_info(address_info);
- o->iter_ = iterator_type::create(
- address_info, host_name, service_name);
- }
-
- o->io_service_impl_.post_deferred_completion(o);
- ptr.release();
- }
- else
- {
- // The operation has been returned to the main io_serice. The
- // completion handler is ready to be delivered.
-
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object
- // remains valid until after we have deallocated the memory here.
- detail::binder2<Handler, asio::error_code, iterator_type>
- handler(o->handler_, o->ec_, o->iter_);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
- }
-
- private:
- boost::weak_ptr<void> impl_;
- query_type query_;
- io_service_impl& io_service_impl_;
- Handler handler_;
- asio::error_code ec_;
- iterator_type iter_;
- };
-
// Asynchronously resolve a query to a list of entries.
template <typename Handler>
void async_resolve(implementation_type& impl, const query_type& query,
Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef resolve_op<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr,
- impl, query, io_service_impl_, handler);
-
- if (work_io_service_)
- {
- start_work_thread();
- io_service_impl_.work_started();
- work_io_service_impl_.post_immediate_completion(ptr.get());
- ptr.release();
- }
+ typedef resolve_op<Protocol, Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl, query, io_service_impl_, handler);
+
+ start_resolve_op(p.p);
+ p.v = p.p = 0;
}
// Resolve an endpoint to a list of entries.
iterator_type resolve(implementation_type&,
const endpoint_type& endpoint, asio::error_code& ec)
{
- // First try resolving with the service name. If that fails try resolving
- // but allow the service to be returned as a number.
char host_name[NI_MAXHOST];
char service_name[NI_MAXSERV];
- int flags = endpoint.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0;
- socket_ops::getnameinfo(endpoint.data(), endpoint.size(),
- host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec);
- if (ec)
- {
- flags |= NI_NUMERICSERV;
- socket_ops::getnameinfo(endpoint.data(), endpoint.size(),
- host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec);
- }
+ socket_ops::sync_getnameinfo(endpoint.data(), endpoint.size(),
+ host_name, NI_MAXHOST, service_name, NI_MAXSERV,
+ endpoint.protocol().type(), ec);
- if (ec)
- return iterator_type();
-
- return iterator_type::create(endpoint, host_name, service_name);
+ return ec ? iterator_type() : iterator_type::create(
+ endpoint, host_name, service_name);
}
- template <typename Handler>
- class resolve_endpoint_op
- : public operation
- {
- public:
- resolve_endpoint_op(implementation_type impl, const endpoint_type& ep,
- io_service_impl& io_service_impl, Handler handler)
- : operation(&resolve_endpoint_op::do_complete),
- impl_(impl),
- ep_(ep),
- io_service_impl_(io_service_impl),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
- {
- // Take ownership of the operation object.
- resolve_endpoint_op* o(static_cast<resolve_endpoint_op*>(base));
- typedef handler_alloc_traits<Handler, resolve_endpoint_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- if (owner)
- {
- if (owner != &o->io_service_impl_)
- {
- // The operation is being run on the worker io_service. Time to
- // perform the resolver operation.
-
- if (o->impl_.expired())
- {
- // THe operation has been cancelled.
- o->ec_ = asio::error::operation_aborted;
- }
- else
- {
- // Perform the blocking endoint resolution operation.
- char host_name[NI_MAXHOST];
- char service_name[NI_MAXSERV];
- int flags = o->ep_.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0;
- socket_ops::getnameinfo(o->ep_.data(), o->ep_.size(),
- host_name, NI_MAXHOST, service_name,
- NI_MAXSERV, flags, o->ec_);
- if (o->ec_)
- {
- flags |= NI_NUMERICSERV;
- socket_ops::getnameinfo(o->ep_.data(), o->ep_.size(),
- host_name, NI_MAXHOST, service_name,
- NI_MAXSERV, flags, o->ec_);
- }
- o->iter_ = iterator_type::create(o->ep_, host_name, service_name);
- }
-
- o->io_service_impl_.post_deferred_completion(o);
- ptr.release();
- }
- else
- {
- // The operation has been returned to the main io_serice. The
- // completion handler is ready to be delivered.
-
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object
- // remains valid until after we have deallocated the memory here.
- detail::binder2<Handler, asio::error_code, iterator_type>
- handler(o->handler_, o->ec_, o->iter_);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
- }
-
- private:
- boost::weak_ptr<void> impl_;
- endpoint_type ep_;
- io_service_impl& io_service_impl_;
- Handler handler_;
- asio::error_code ec_;
- iterator_type iter_;
- };
-
// Asynchronously resolve an endpoint to a list of entries.
template <typename Handler>
void async_resolve(implementation_type& impl, const endpoint_type& endpoint,
Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef resolve_endpoint_op<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr,
- impl, endpoint, io_service_impl_, handler);
-
- if (work_io_service_)
- {
- start_work_thread();
- io_service_impl_.work_started();
- work_io_service_impl_.post_immediate_completion(ptr.get());
- ptr.release();
- }
- }
-
-private:
- // Helper class to run the work io_service in a thread.
- class work_io_service_runner
- {
- public:
- work_io_service_runner(asio::io_service& io_service)
- : io_service_(io_service) {}
- void operator()() { io_service_.run(); }
- private:
- asio::io_service& io_service_;
- };
-
- // Start the work thread if it's not already running.
- void start_work_thread()
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- if (!work_thread_)
- {
- work_thread_.reset(new asio::detail::thread(
- work_io_service_runner(*work_io_service_)));
- }
+ typedef resolve_endpoint_op<Protocol, Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl, endpoint, io_service_impl_, handler);
+
+ start_resolve_op(p.p);
+ p.v = p.p = 0;
}
-
- // Mutex to protect access to internal data.
- asio::detail::mutex mutex_;
-
- // The io_service implementation used to post completions.
- io_service_impl& io_service_impl_;
-
- // Private io_service used for performing asynchronous host resolution.
- boost::scoped_ptr<asio::io_service> work_io_service_;
-
- // The work io_service implementation used to post completions.
- io_service_impl& work_io_service_impl_;
-
- // Work for the private io_service to perform.
- boost::scoped_ptr<asio::io_service::work> work_;
-
- // Thread used for running the work io_service's run loop.
- boost::scoped_ptr<asio::detail::thread> work_thread_;
};
} // namespace detail
diff --git a/ext/asio/asio/detail/resolver_service_base.hpp b/ext/asio/asio/detail/resolver_service_base.hpp
new file mode 100644
index 0000000..d6f20bd
--- /dev/null
+++ b/ext/asio/asio/detail/resolver_service_base.hpp
@@ -0,0 +1,123 @@
+//
+// detail/resolver_service_base.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_RESOLVER_SERVICE_BASE_HPP
+#define ASIO_DETAIL_RESOLVER_SERVICE_BASE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <boost/scoped_ptr.hpp>
+#include "asio/error.hpp"
+#include "asio/io_service.hpp"
+#include "asio/detail/mutex.hpp"
+#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/operation.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/socket_types.hpp"
+#include "asio/detail/thread.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+class resolver_service_base
+{
+public:
+ // The implementation type of the resolver. A cancellation token is used to
+ // indicate to the background thread that the operation has been cancelled.
+ typedef socket_ops::shared_cancel_token_type implementation_type;
+
+ // Constructor.
+ ASIO_DECL resolver_service_base(asio::io_service& io_service);
+
+ // Destructor.
+ ASIO_DECL ~resolver_service_base();
+
+ // Destroy all user-defined handler objects owned by the service.
+ ASIO_DECL void shutdown_service();
+
+ // Construct a new resolver implementation.
+ ASIO_DECL void construct(implementation_type& impl);
+
+ // Destroy a resolver implementation.
+ ASIO_DECL void destroy(implementation_type&);
+
+ // Cancel pending asynchronous operations.
+ ASIO_DECL void cancel(implementation_type& impl);
+
+protected:
+ // Helper function to start an asynchronous resolve operation.
+ ASIO_DECL void start_resolve_op(operation* op);
+
+ // Helper class to perform exception-safe cleanup of addrinfo objects.
+ class auto_addrinfo
+ : private asio::detail::noncopyable
+ {
+ public:
+ explicit auto_addrinfo(asio::detail::addrinfo_type* ai)
+ : ai_(ai)
+ {
+ }
+
+ ~auto_addrinfo()
+ {
+ if (ai_)
+ socket_ops::freeaddrinfo(ai_);
+ }
+
+ operator asio::detail::addrinfo_type*()
+ {
+ return ai_;
+ }
+
+ private:
+ asio::detail::addrinfo_type* ai_;
+ };
+
+ // Helper class to run the work io_service in a thread.
+ class work_io_service_runner;
+
+ // Start the work thread if it's not already running.
+ ASIO_DECL void start_work_thread();
+
+ // The io_service implementation used to post completions.
+ io_service_impl& io_service_impl_;
+
+private:
+ // Mutex to protect access to internal data.
+ asio::detail::mutex mutex_;
+
+ // Private io_service used for performing asynchronous host resolution.
+ boost::scoped_ptr<asio::io_service> work_io_service_;
+
+ // The work io_service implementation used to post completions.
+ io_service_impl& work_io_service_impl_;
+
+ // Work for the private io_service to perform.
+ boost::scoped_ptr<asio::io_service::work> work_;
+
+ // Thread used for running the work io_service's run loop.
+ boost::scoped_ptr<asio::detail::thread> work_thread_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/resolver_service_base.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // ASIO_DETAIL_RESOLVER_SERVICE_BASE_HPP
diff --git a/ext/asio/asio/detail/scoped_lock.hpp b/ext/asio/asio/detail/scoped_lock.hpp
index e6f6ba5..69b5725 100644
--- a/ext/asio/asio/detail/scoped_lock.hpp
+++ b/ext/asio/asio/detail/scoped_lock.hpp
@@ -1,8 +1,8 @@
//
-// scoped_lock.hpp
-// ~~~~~~~~~~~~~~~
+// detail/scoped_lock.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,10 +15,10 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
diff --git a/ext/asio/asio/detail/select_interrupter.hpp b/ext/asio/asio/detail/select_interrupter.hpp
index ff5505b..e975ddc 100644
--- a/ext/asio/asio/detail/select_interrupter.hpp
+++ b/ext/asio/asio/detail/select_interrupter.hpp
@@ -1,8 +1,8 @@
//
-// select_interrupter.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~
+// detail/select_interrupter.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,23 +15,20 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) || defined(__SYMBIAN32__)
# include "asio/detail/socket_select_interrupter.hpp"
-#else
+#elif defined(ASIO_HAS_EVENTFD)
# include "asio/detail/eventfd_select_interrupter.hpp"
+#else
# include "asio/detail/pipe_select_interrupter.hpp"
#endif
namespace asio {
namespace detail {
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) || defined(__SYMBIAN32__)
typedef socket_select_interrupter select_interrupter;
#elif defined(ASIO_HAS_EVENTFD)
typedef eventfd_select_interrupter select_interrupter;
@@ -42,6 +39,4 @@ typedef pipe_select_interrupter select_interrupter;
} // namespace detail
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_SELECT_INTERRUPTER_HPP
diff --git a/ext/asio/asio/detail/select_reactor.hpp b/ext/asio/asio/detail/select_reactor.hpp
index 798611b..161305f 100644
--- a/ext/asio/asio/detail/select_reactor.hpp
+++ b/ext/asio/asio/detail/select_reactor.hpp
@@ -1,8 +1,8 @@
//
-// select_reactor.hpp
-// ~~~~~~~~~~~~~~~~~~
+// detail/select_reactor.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,41 +15,38 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/socket_types.hpp" // Must come before posix_time.
+#if defined(ASIO_HAS_IOCP) \
+ || (!defined(ASIO_HAS_DEV_POLL) \
+ && !defined(ASIO_HAS_EPOLL) \
+ && !defined(ASIO_HAS_KQUEUE))
-#include "asio/detail/push_options.hpp"
#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/io_service.hpp"
-#include "asio/detail/bind_handler.hpp"
-#include "asio/detail/fd_set_adapter.hpp"
#include "asio/detail/mutex.hpp"
-#include "asio/detail/noncopyable.hpp"
#include "asio/detail/op_queue.hpp"
#include "asio/detail/reactor_op.hpp"
#include "asio/detail/reactor_op_queue.hpp"
#include "asio/detail/select_interrupter.hpp"
#include "asio/detail/select_reactor_fwd.hpp"
-#include "asio/detail/service_base.hpp"
-#include "asio/detail/signal_blocker.hpp"
-#include "asio/detail/socket_ops.hpp"
#include "asio/detail/socket_types.hpp"
-#include "asio/detail/thread.hpp"
#include "asio/detail/timer_op.hpp"
#include "asio/detail/timer_queue_base.hpp"
#include "asio/detail/timer_queue_fwd.hpp"
#include "asio/detail/timer_queue_set.hpp"
+#include "asio/io_service.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+# include "asio/detail/thread.hpp"
+#endif // defined(ASIO_HAS_IOCP)
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
-template <bool Own_Thread>
class select_reactor
- : public asio::detail::service_base<select_reactor<Own_Thread> >
+ : public asio::detail::service_base<select_reactor>
{
public:
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
@@ -66,280 +63,91 @@ public:
};
// Constructor.
- select_reactor(asio::io_service& io_service)
- : asio::detail::service_base<
- select_reactor<Own_Thread> >(io_service),
- io_service_(use_service<io_service_impl>(io_service)),
- mutex_(),
- interrupter_(),
- stop_thread_(false),
- thread_(0),
- shutdown_(false)
- {
- if (Own_Thread)
- {
- asio::detail::signal_blocker sb;
- thread_ = new asio::detail::thread(
- bind_handler(&select_reactor::call_run_thread, this));
- }
- }
+ ASIO_DECL select_reactor(asio::io_service& io_service);
// Destructor.
- ~select_reactor()
- {
- shutdown_service();
- }
+ ASIO_DECL ~select_reactor();
// Destroy all user-defined handler objects owned by the service.
- void shutdown_service()
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- shutdown_ = true;
- stop_thread_ = true;
- lock.unlock();
-
- if (Own_Thread)
- {
- if (thread_)
- {
- interrupter_.interrupt();
- thread_->join();
- delete thread_;
- thread_ = 0;
- }
- }
-
- op_queue<operation> ops;
-
- for (int i = 0; i < max_ops; ++i)
- op_queue_[i].get_all_operations(ops);
-
- timer_queues_.get_all_timers(ops);
- }
+ ASIO_DECL void shutdown_service();
// Initialise the task, but only if the reactor is not in its own thread.
- void init_task()
- {
- io_service_.init_task();
- }
+ ASIO_DECL void init_task();
// Register a socket with the reactor. Returns 0 on success, system error
// code on failure.
- int register_descriptor(socket_type, per_descriptor_data&)
+ ASIO_DECL int register_descriptor(socket_type, per_descriptor_data&);
+
+ // Post a reactor operation for immediate completion.
+ void post_immediate_completion(reactor_op* op)
{
- return 0;
+ io_service_.post_immediate_completion(op);
}
// Start a new operation. The reactor operation will be performed when the
// given descriptor is flagged as ready, or an error has occurred.
- void start_op(int op_type, socket_type descriptor,
- per_descriptor_data&, reactor_op* op, bool)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- if (!shutdown_)
- {
- bool first = op_queue_[op_type].enqueue_operation(descriptor, op);
- io_service_.work_started();
- if (first)
- interrupter_.interrupt();
- }
- }
+ ASIO_DECL void start_op(int op_type, socket_type descriptor,
+ per_descriptor_data&, reactor_op* op, bool);
// Cancel all operations associated with the given descriptor. The
// handlers associated with the descriptor will be invoked with the
// operation_aborted error.
- void cancel_ops(socket_type descriptor, per_descriptor_data&)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- cancel_ops_unlocked(descriptor, asio::error::operation_aborted);
- }
+ ASIO_DECL void cancel_ops(socket_type descriptor, per_descriptor_data&);
// Cancel any operations that are running against the descriptor and remove
// its registration from the reactor.
- void close_descriptor(socket_type descriptor, per_descriptor_data&)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- cancel_ops_unlocked(descriptor, asio::error::operation_aborted);
- }
+ ASIO_DECL void close_descriptor(socket_type descriptor,
+ per_descriptor_data&);
// Add a new timer queue to the reactor.
template <typename Time_Traits>
- void add_timer_queue(timer_queue<Time_Traits>& timer_queue)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- timer_queues_.insert(&timer_queue);
- }
+ void add_timer_queue(timer_queue<Time_Traits>& queue);
// Remove a timer queue from the reactor.
template <typename Time_Traits>
- void remove_timer_queue(timer_queue<Time_Traits>& timer_queue)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- timer_queues_.erase(&timer_queue);
- }
+ void remove_timer_queue(timer_queue<Time_Traits>& queue);
// Schedule a new operation in the given timer queue to expire at the
// specified absolute time.
template <typename Time_Traits>
- void schedule_timer(timer_queue<Time_Traits>& timer_queue,
- const typename Time_Traits::time_type& time, timer_op* op, void* token)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- if (!shutdown_)
- {
- bool earliest = timer_queue.enqueue_timer(time, op, token);
- io_service_.work_started();
- if (earliest)
- interrupter_.interrupt();
- }
- }
+ void schedule_timer(timer_queue<Time_Traits>& queue,
+ const typename Time_Traits::time_type& time,
+ typename timer_queue<Time_Traits>::per_timer_data& timer, timer_op* op);
// Cancel the timer operations associated with the given token. Returns the
// number of operations that have been posted or dispatched.
template <typename Time_Traits>
- std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- op_queue<operation> ops;
- std::size_t n = timer_queue.cancel_timer(token, ops);
- lock.unlock();
- io_service_.post_deferred_completions(ops);
- return n;
- }
+ std::size_t cancel_timer(timer_queue<Time_Traits>& queue,
+ typename timer_queue<Time_Traits>::per_timer_data& timer);
// Run select once until interrupted or events are ready to be dispatched.
- void run(bool block, op_queue<operation>& ops)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
-
- // Check if the thread is supposed to stop.
- if (Own_Thread)
- if (stop_thread_)
- return;
-
- // Set up the descriptor sets.
- fd_set_adapter fds[max_select_ops];
- fds[read_op].set(interrupter_.read_descriptor());
- socket_type max_fd = 0;
- bool have_work_to_do = !timer_queues_.all_empty();
- for (int i = 0; i < max_select_ops; ++i)
- {
- have_work_to_do = have_work_to_do || !op_queue_[i].empty();
- op_queue_[i].get_descriptors(fds[i], ops);
- if (fds[i].max_descriptor() > max_fd)
- max_fd = fds[i].max_descriptor();
- }
-
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- // Connection operations on Windows use both except and write fd_sets.
- have_work_to_do = have_work_to_do || !op_queue_[connect_op].empty();
- op_queue_[connect_op].get_descriptors(fds[write_op], ops);
- if (fds[write_op].max_descriptor() > max_fd)
- max_fd = fds[write_op].max_descriptor();
- op_queue_[connect_op].get_descriptors(fds[except_op], ops);
- if (fds[except_op].max_descriptor() > max_fd)
- max_fd = fds[except_op].max_descriptor();
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-
- // We can return immediately if there's no work to do and the reactor is
- // not supposed to block.
- if (!block && !have_work_to_do)
- return;
-
- // Determine how long to block while waiting for events.
- timeval tv_buf = { 0, 0 };
- timeval* tv = block ? get_timeout(tv_buf) : &tv_buf;
-
- lock.unlock();
-
- // Block on the select call until descriptors become ready.
- asio::error_code ec;
- int retval = socket_ops::select(static_cast<int>(max_fd + 1),
- fds[read_op], fds[write_op], fds[except_op], tv, ec);
-
- // Reset the interrupter.
- if (retval > 0 && fds[read_op].is_set(interrupter_.read_descriptor()))
- interrupter_.reset();
-
- lock.lock();
-
- // Dispatch all ready operations.
- if (retval > 0)
- {
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- // Connection operations on Windows use both except and write fd_sets.
- op_queue_[connect_op].perform_operations_for_descriptors(
- fds[except_op], ops);
- op_queue_[connect_op].perform_operations_for_descriptors(
- fds[write_op], ops);
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-
- // Exception operations must be processed first to ensure that any
- // out-of-band data is read before normal data.
- for (int i = max_select_ops - 1; i >= 0; --i)
- op_queue_[i].perform_operations_for_descriptors(fds[i], ops);
- }
- timer_queues_.get_ready_timers(ops);
- }
+ ASIO_DECL void run(bool block, op_queue<operation>& ops);
// Interrupt the select loop.
- void interrupt()
- {
- interrupter_.interrupt();
- }
+ ASIO_DECL void interrupt();
private:
+#if defined(ASIO_HAS_IOCP)
// Run the select loop in the thread.
- void run_thread()
- {
- if (Own_Thread)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- while (!stop_thread_)
- {
- lock.unlock();
- op_queue<operation> ops;
- run(true, ops);
- io_service_.post_deferred_completions(ops);
- lock.lock();
- }
- }
- }
+ ASIO_DECL void run_thread();
// Entry point for the select loop thread.
- static void call_run_thread(select_reactor* reactor)
- {
- if (Own_Thread)
- {
- reactor->run_thread();
- }
- }
+ ASIO_DECL static void call_run_thread(select_reactor* reactor);
+#endif // defined(ASIO_HAS_IOCP)
+
+ // Helper function to add a new timer queue.
+ ASIO_DECL void do_add_timer_queue(timer_queue_base& queue);
+
+ // Helper function to remove a timer queue.
+ ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue);
// Get the timeout value for the select call.
- timeval* get_timeout(timeval& tv)
- {
- // By default we will wait no longer than 5 minutes. This will ensure that
- // any changes to the system clock are detected after no longer than this.
- long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000);
- tv.tv_sec = usec / 1000000;
- tv.tv_usec = usec % 1000000;
- return &tv;
- }
+ ASIO_DECL timeval* get_timeout(timeval& tv);
// Cancel all operations associated with the given descriptor. This function
// does not acquire the select_reactor's mutex.
- void cancel_ops_unlocked(socket_type descriptor,
- const asio::error_code& ec)
- {
- bool need_interrupt = false;
- op_queue<operation> ops;
- for (int i = 0; i < max_ops; ++i)
- need_interrupt = op_queue_[i].cancel_operations(
- descriptor, ops, ec) || need_interrupt;
- io_service_.post_deferred_completions(ops);
- if (need_interrupt)
- interrupter_.interrupt();
- }
+ ASIO_DECL void cancel_ops_unlocked(socket_type descriptor,
+ const asio::error_code& ec);
// The io_service implementation used to post completions.
io_service_impl& io_service_;
@@ -356,11 +164,13 @@ private:
// The timer queues.
timer_queue_set timer_queues_;
+#if defined(ASIO_HAS_IOCP)
// Does the reactor loop thread need to stop.
bool stop_thread_;
// The thread that is running the reactor loop.
asio::detail::thread* thread_;
+#endif // defined(ASIO_HAS_IOCP)
// Whether the service has been shut down.
bool shutdown_;
@@ -371,4 +181,14 @@ private:
#include "asio/detail/pop_options.hpp"
+#include "asio/detail/impl/select_reactor.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/select_reactor.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(ASIO_HAS_IOCP)
+ // || (!defined(ASIO_HAS_DEV_POLL)
+ // && !defined(ASIO_HAS_EPOLL)
+ // && !defined(ASIO_HAS_KQUEUE))
+
#endif // ASIO_DETAIL_SELECT_REACTOR_HPP
diff --git a/ext/asio/asio/detail/select_reactor_fwd.hpp b/ext/asio/asio/detail/select_reactor_fwd.hpp
index 0b72e7e..04e5ecd 100644
--- a/ext/asio/asio/detail/select_reactor_fwd.hpp
+++ b/ext/asio/asio/detail/select_reactor_fwd.hpp
@@ -1,8 +1,8 @@
//
-// select_reactor_fwd.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~
+// detail/select_reactor_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
namespace asio {
namespace detail {
-template <bool Own_Thread>
class select_reactor;
} // namespace detail
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_SELECT_REACTOR_FWD_HPP
diff --git a/ext/asio/asio/detail/service_registry.hpp b/ext/asio/asio/detail/service_registry.hpp
index f80b4b6..34b946d 100644
--- a/ext/asio/asio/detail/service_registry.hpp
+++ b/ext/asio/asio/detail/service_registry.hpp
@@ -1,8 +1,8 @@
//
-// service_registry.hpp
-// ~~~~~~~~~~~~~~~~~~~~
+// detail/service_registry.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,11 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <typeinfo>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/io_service.hpp"
#include "asio/detail/mutex.hpp"
#include "asio/detail/noncopyable.hpp"
-#include "asio/detail/service_id.hpp"
+#include "asio/io_service.hpp"
#if defined(BOOST_NO_TYPEID)
# if !defined(ASIO_NO_TYPEID)
@@ -32,6 +27,8 @@
# endif // !defined(ASIO_NO_TYPEID)
#endif // defined(BOOST_NO_TYPEID)
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -55,98 +52,43 @@ class service_registry
{
public:
// Constructor.
- service_registry(asio::io_service& o)
- : owner_(o),
- first_service_(0)
- {
- }
+ ASIO_DECL service_registry(asio::io_service& o);
// Destructor.
- ~service_registry()
- {
- // Shutdown all services. This must be done in a separate loop before the
- // services are destroyed since the destructors of user-defined handler
- // objects may try to access other service objects.
- asio::io_service::service* service = first_service_;
- while (service)
- {
- service->shutdown_service();
- service = service->next_;
- }
-
- // Destroy all services.
- while (first_service_)
- {
- asio::io_service::service* next_service = first_service_->next_;
- destroy(first_service_);
- first_service_ = next_service;
- }
- }
+ ASIO_DECL ~service_registry();
// Get the service object corresponding to the specified service type. Will
// create a new service object automatically if no such object already
// exists. Ownership of the service object is not transferred to the caller.
template <typename Service>
- Service& use_service()
- {
- asio::io_service::service::key key;
- init_key(key, Service::id);
- factory_type factory = &service_registry::create<Service>;
- return *static_cast<Service*>(do_use_service(key, factory));
- }
+ Service& use_service();
- // Add a service object. Returns false on error, in which case ownership of
- // the object is retained by the caller.
+ // Add a service object. Throws on error, in which case ownership of the
+ // object is retained by the caller.
template <typename Service>
- bool add_service(Service* new_service)
- {
- asio::io_service::service::key key;
- init_key(key, Service::id);
- return do_add_service(key, new_service);
- }
+ void add_service(Service* new_service);
// Check whether a service object of the specified type already exists.
template <typename Service>
- bool has_service() const
- {
- asio::io_service::service::key key;
- init_key(key, Service::id);
- return do_has_service(key);
- }
+ bool has_service() const;
private:
// Initialise a service's key based on its id.
- void init_key(asio::io_service::service::key& key,
- const asio::io_service::id& id)
- {
- key.type_info_ = 0;
- key.id_ = &id;
- }
+ ASIO_DECL static void init_key(
+ asio::io_service::service::key& key,
+ const asio::io_service::id& id);
#if !defined(ASIO_NO_TYPEID)
// Initialise a service's key based on its id.
template <typename Service>
- void init_key(asio::io_service::service::key& key,
- const asio::detail::service_id<Service>& /*id*/)
- {
- key.type_info_ = &typeid(typeid_wrapper<Service>);
- key.id_ = 0;
- }
+ static void init_key(asio::io_service::service::key& key,
+ const asio::detail::service_id<Service>& /*id*/);
#endif // !defined(ASIO_NO_TYPEID)
// Check if a service matches the given id.
- static bool keys_match(
+ ASIO_DECL static bool keys_match(
const asio::io_service::service::key& key1,
- const asio::io_service::service::key& key2)
- {
- if (key1.id_ && key2.id_)
- if (key1.id_ == key2.id_)
- return true;
- if (key1.type_info_ && key2.type_info_)
- if (*key1.type_info_ == *key2.type_info_)
- return true;
- return false;
- }
+ const asio::io_service::service::key& key2);
// The type of a factory function used for creating a service instance.
typedef asio::io_service::service*
@@ -155,18 +97,15 @@ private:
// Factory function for creating a service instance.
template <typename Service>
static asio::io_service::service* create(
- asio::io_service& owner)
- {
- return new Service(owner);
- }
+ asio::io_service& owner);
// Destroy a service instance.
- static void destroy(asio::io_service::service* service)
- {
- delete service;
- }
+ ASIO_DECL static void destroy(
+ asio::io_service::service* service);
// Helper class to manage service pointers.
+ struct auto_service_ptr;
+ friend struct auto_service_ptr;
struct auto_service_ptr
{
asio::io_service::service* ptr_;
@@ -176,86 +115,19 @@ private:
// Get the service object corresponding to the specified service key. Will
// create a new service object automatically if no such object already
// exists. Ownership of the service object is not transferred to the caller.
- asio::io_service::service* do_use_service(
+ ASIO_DECL asio::io_service::service* do_use_service(
const asio::io_service::service::key& key,
- factory_type factory)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
-
- // First see if there is an existing service object with the given key.
- asio::io_service::service* service = first_service_;
- while (service)
- {
- if (keys_match(service->key_, key))
- return service;
- service = service->next_;
- }
-
- // Create a new service object. The service registry's mutex is not locked
- // at this time to allow for nested calls into this function from the new
- // service's constructor.
- lock.unlock();
- auto_service_ptr new_service = { factory(owner_) };
- new_service.ptr_->key_ = key;
- lock.lock();
-
- // Check that nobody else created another service object of the same type
- // while the lock was released.
- service = first_service_;
- while (service)
- {
- if (keys_match(service->key_, key))
- return service;
- service = service->next_;
- }
-
- // Service was successfully initialised, pass ownership to registry.
- new_service.ptr_->next_ = first_service_;
- first_service_ = new_service.ptr_;
- new_service.ptr_ = 0;
- return first_service_;
- }
+ factory_type factory);
// Add a service object. Returns false on error, in which case ownership of
// the object is retained by the caller.
- bool do_add_service(
+ ASIO_DECL void do_add_service(
const asio::io_service::service::key& key,
- asio::io_service::service* new_service)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
-
- // Check if there is an existing service object with the given key.
- asio::io_service::service* service = first_service_;
- while (service)
- {
- if (keys_match(service->key_, key))
- return false;
- service = service->next_;
- }
-
- // Take ownership of the service object.
- new_service->key_ = key;
- new_service->next_ = first_service_;
- first_service_ = new_service;
-
- return true;
- }
+ asio::io_service::service* new_service);
// Check whether a service object with the specified key already exists.
- bool do_has_service(const asio::io_service::service::key& key) const
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
-
- asio::io_service::service* service = first_service_;
- while (service)
- {
- if (keys_match(service->key_, key))
- return true;
- service = service->next_;
- }
-
- return false;
- }
+ ASIO_DECL bool do_has_service(
+ const asio::io_service::service::key& key) const;
// Mutex to protect access to internal data.
mutable asio::detail::mutex mutex_;
@@ -272,4 +144,9 @@ private:
#include "asio/detail/pop_options.hpp"
+#include "asio/detail/impl/service_registry.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/service_registry.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
#endif // ASIO_DETAIL_SERVICE_REGISTRY_HPP
diff --git a/ext/asio/asio/detail/service_registry_fwd.hpp b/ext/asio/asio/detail/service_registry_fwd.hpp
index 423bb4b..4108c1a 100644
--- a/ext/asio/asio/detail/service_registry_fwd.hpp
+++ b/ext/asio/asio/detail/service_registry_fwd.hpp
@@ -1,8 +1,8 @@
//
-// service_registry_fwd.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/service_registry_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,8 +15,6 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
namespace asio {
namespace detail {
@@ -25,6 +23,4 @@ class service_registry;
} // namespace detail
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_SERVICE_REGISTRY_FWD_HPP
diff --git a/ext/asio/asio/detail/shared_ptr.hpp b/ext/asio/asio/detail/shared_ptr.hpp
new file mode 100644
index 0000000..cd496f5
--- /dev/null
+++ b/ext/asio/asio/detail/shared_ptr.hpp
@@ -0,0 +1,38 @@
+//
+// detail/shared_ptr.hpp
+// ~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_SHARED_PTR_HPP
+#define ASIO_DETAIL_SHARED_PTR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1600)
+# include <memory>
+#else
+# include <boost/shared_ptr.hpp>
+#endif
+
+namespace asio {
+namespace detail {
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1600)
+using std::shared_ptr;
+#else
+using boost::shared_ptr;
+#endif
+
+} // namespace detail
+} // namespace asio
+
+#endif // ASIO_DETAIL_SHARED_PTR_HPP
diff --git a/ext/asio/asio/detail/signal_blocker.hpp b/ext/asio/asio/detail/signal_blocker.hpp
index 52f70c8..89f45ca 100644
--- a/ext/asio/asio/detail/signal_blocker.hpp
+++ b/ext/asio/asio/detail/signal_blocker.hpp
@@ -1,8 +1,8 @@
//
-// signal_blocker.hpp
-// ~~~~~~~~~~~~~~~~~~
+// detail/signal_blocker.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,11 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
+#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) \
+ || defined(BOOST_WINDOWS) || defined(__CYGWIN__) || defined(__SYMBIAN32__)
# include "asio/detail/null_signal_blocker.hpp"
-#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-# include "asio/detail/win_signal_blocker.hpp"
#elif defined(BOOST_HAS_PTHREADS)
# include "asio/detail/posix_signal_blocker.hpp"
#else
@@ -34,10 +29,9 @@
namespace asio {
namespace detail {
-#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
+#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) \
+ || defined(BOOST_WINDOWS) || defined(__CYGWIN__) || defined(__SYMBIAN32__)
typedef null_signal_blocker signal_blocker;
-#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-typedef win_signal_blocker signal_blocker;
#elif defined(BOOST_HAS_PTHREADS)
typedef posix_signal_blocker signal_blocker;
#endif
@@ -45,6 +39,4 @@ typedef posix_signal_blocker signal_blocker;
} // namespace detail
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_SIGNAL_BLOCKER_HPP
diff --git a/ext/asio/asio/detail/signal_init.hpp b/ext/asio/asio/detail/signal_init.hpp
index 12f17d3..80925f6 100644
--- a/ext/asio/asio/detail/signal_init.hpp
+++ b/ext/asio/asio/detail/signal_init.hpp
@@ -1,8 +1,8 @@
//
-// signal_init.hpp
-// ~~~~~~~~~~~~~~~
+// detail/signal_init.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
-#include "asio/detail/push_options.hpp"
#include <csignal>
-#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
@@ -44,8 +40,8 @@ public:
} // namespace detail
} // namespace asio
-#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
-
#include "asio/detail/pop_options.hpp"
+#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+
#endif // ASIO_DETAIL_SIGNAL_INIT_HPP
diff --git a/ext/asio/asio/detail/socket_holder.hpp b/ext/asio/asio/detail/socket_holder.hpp
index 82a3884..cb40175 100644
--- a/ext/asio/asio/detail/socket_holder.hpp
+++ b/ext/asio/asio/detail/socket_holder.hpp
@@ -1,8 +1,8 @@
//
-// socket_holder.hpp
-// ~~~~~~~~~~~~~~~~~
+// detail/socket_holder.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,11 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -46,7 +47,8 @@ public:
if (socket_ != invalid_socket)
{
asio::error_code ec;
- socket_ops::close(socket_, ec);
+ socket_ops::state_type state = 0;
+ socket_ops::close(socket_, state, true, ec);
}
}
@@ -62,7 +64,8 @@ public:
if (socket_ != invalid_socket)
{
asio::error_code ec;
- socket_ops::close(socket_, ec);
+ socket_ops::state_type state = 0;
+ socket_ops::close(socket_, state, true, ec);
socket_ = invalid_socket;
}
}
diff --git a/ext/asio/asio/detail/socket_ops.hpp b/ext/asio/asio/detail/socket_ops.hpp
index 1a863a9..8ad464c 100644
--- a/ext/asio/asio/detail/socket_ops.hpp
+++ b/ext/asio/asio/detail/socket_ops.hpp
@@ -1,8 +1,8 @@
//
-// socket_ops.hpp
-// ~~~~~~~~~~~~~~
+// detail/socket_ops.hpp
+// ~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,193 +15,102 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include <boost/assert.hpp>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <cerrno>
-#include <boost/detail/workaround.hpp>
-#include <new>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/error.hpp"
+#include "asio/error_code.hpp"
+#include "asio/detail/shared_ptr.hpp"
#include "asio/detail/socket_types.hpp"
+#include "asio/detail/weak_ptr.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
namespace socket_ops {
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-struct msghdr { int msg_namelen; };
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+// Socket state bits.
+enum
+{
+ // The user wants a non-blocking socket.
+ user_set_non_blocking = 1,
-#if defined(__hpux)
-// HP-UX doesn't declare these functions extern "C", so they are declared again
-// here to avoid linker errors about undefined symbols.
-extern "C" char* if_indextoname(unsigned int, char*);
-extern "C" unsigned int if_nametoindex(const char*);
-#endif // defined(__hpux)
+ // The socket has been set non-blocking.
+ internal_non_blocking = 2,
-inline void clear_error(asio::error_code& ec)
-{
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- WSASetLastError(0);
-#else
- errno = 0;
-#endif
- ec = asio::error_code();
-}
-
-template <typename ReturnType>
-inline ReturnType error_wrapper(ReturnType return_value,
- asio::error_code& ec)
-{
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- ec = asio::error_code(WSAGetLastError(),
- asio::error::get_system_category());
-#else
- ec = asio::error_code(errno,
- asio::error::get_system_category());
-#endif
- return return_value;
-}
-
-template <typename SockLenType>
-inline socket_type call_accept(SockLenType msghdr::*,
- socket_type s, socket_addr_type* addr, std::size_t* addrlen)
-{
- SockLenType tmp_addrlen = addrlen ? (SockLenType)*addrlen : 0;
- socket_type result = ::accept(s, addr, addrlen ? &tmp_addrlen : 0);
- if (addrlen)
- *addrlen = (std::size_t)tmp_addrlen;
- return result;
-}
-
-inline socket_type accept(socket_type s, socket_addr_type* addr,
- std::size_t* addrlen, asio::error_code& ec)
-{
- clear_error(ec);
-
- socket_type new_s = error_wrapper(call_accept(
- &msghdr::msg_namelen, s, addr, addrlen), ec);
- if (new_s == invalid_socket)
- return new_s;
-
-#if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__)
- int optval = 1;
- int result = error_wrapper(::setsockopt(new_s,
- SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)), ec);
- if (result != 0)
- {
- ::close(new_s);
- return invalid_socket;
- }
-#endif
-
- clear_error(ec);
- return new_s;
-}
-
-template <typename SockLenType>
-inline int call_bind(SockLenType msghdr::*,
- socket_type s, const socket_addr_type* addr, std::size_t addrlen)
-{
- return ::bind(s, addr, (SockLenType)addrlen);
-}
+ // Helper "state" used to determine whether the socket is non-blocking.
+ non_blocking = user_set_non_blocking | internal_non_blocking,
-inline int bind(socket_type s, const socket_addr_type* addr,
- std::size_t addrlen, asio::error_code& ec)
-{
- clear_error(ec);
- int result = error_wrapper(call_bind(
- &msghdr::msg_namelen, s, addr, addrlen), ec);
- if (result == 0)
- clear_error(ec);
- return result;
-}
-
-inline int close(socket_type s, asio::error_code& ec)
-{
- clear_error(ec);
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- int result = error_wrapper(::closesocket(s), ec);
-#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- int result = error_wrapper(::close(s), ec);
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- if (result == 0)
- clear_error(ec);
- return result;
-}
+ // User wants connection_aborted errors, which are disabled by default.
+ enable_connection_aborted = 4,
-inline int shutdown(socket_type s, int what, asio::error_code& ec)
-{
- clear_error(ec);
- int result = error_wrapper(::shutdown(s, what), ec);
- if (result == 0)
- clear_error(ec);
- return result;
-}
-
-template <typename SockLenType>
-inline int call_connect(SockLenType msghdr::*,
- socket_type s, const socket_addr_type* addr, std::size_t addrlen)
-{
- return ::connect(s, addr, (SockLenType)addrlen);
-}
+ // The user set the linger option. Needs to be checked when closing.
+ user_set_linger = 8,
-inline int connect(socket_type s, const socket_addr_type* addr,
- std::size_t addrlen, asio::error_code& ec)
-{
- clear_error(ec);
- int result = error_wrapper(call_connect(
- &msghdr::msg_namelen, s, addr, addrlen), ec);
- if (result == 0)
- clear_error(ec);
- return result;
-}
-
-inline int socketpair(int af, int type, int protocol,
- socket_type sv[2], asio::error_code& ec)
-{
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- (void)(af);
- (void)(type);
- (void)(protocol);
- (void)(sv);
- ec = asio::error::operation_not_supported;
- return -1;
-#else
- clear_error(ec);
- int result = error_wrapper(::socketpair(af, type, protocol, sv), ec);
- if (result == 0)
- clear_error(ec);
- return result;
-#endif
-}
-
-inline int listen(socket_type s, int backlog, asio::error_code& ec)
-{
- clear_error(ec);
- int result = error_wrapper(::listen(s, backlog), ec);
- if (result == 0)
- clear_error(ec);
- return result;
-}
-
-inline void init_buf_iov_base(void*& base, void* addr)
-{
- base = addr;
-}
+ // The socket is stream-oriented.
+ stream_oriented = 16,
-template <typename T>
-inline void init_buf_iov_base(T& base, void* addr)
-{
- base = static_cast<T>(addr);
-}
+ // The socket is datagram-oriented.
+ datagram_oriented = 32
+};
+
+typedef unsigned char state_type;
+
+struct noop_deleter { void operator()(void*) {} };
+typedef shared_ptr<void> shared_cancel_token_type;
+typedef weak_ptr<void> weak_cancel_token_type;
+
+ASIO_DECL socket_type accept(socket_type s, socket_addr_type* addr,
+ std::size_t* addrlen, asio::error_code& ec);
+
+ASIO_DECL socket_type sync_accept(socket_type s,
+ state_type state, socket_addr_type* addr,
+ std::size_t* addrlen, asio::error_code& ec);
+
+#if defined(ASIO_HAS_IOCP)
+
+ASIO_DECL void complete_iocp_accept(socket_type s,
+ void* output_buffer, DWORD address_length,
+ socket_addr_type* addr, std::size_t* addrlen,
+ socket_type new_socket, asio::error_code& ec);
+
+#else // defined(ASIO_HAS_IOCP)
+
+ASIO_DECL bool non_blocking_accept(socket_type s,
+ state_type state, socket_addr_type* addr, std::size_t* addrlen,
+ asio::error_code& ec, socket_type& new_socket);
+
+#endif // defined(ASIO_HAS_IOCP)
+
+ASIO_DECL int bind(socket_type s, const socket_addr_type* addr,
+ std::size_t addrlen, asio::error_code& ec);
+
+ASIO_DECL int close(socket_type s, state_type& state,
+ bool destruction, asio::error_code& ec);
+
+ASIO_DECL bool set_internal_non_blocking(socket_type s,
+ state_type& state, asio::error_code& ec);
+
+ASIO_DECL int shutdown(socket_type s,
+ int what, asio::error_code& ec);
+
+ASIO_DECL int connect(socket_type s, const socket_addr_type* addr,
+ std::size_t addrlen, asio::error_code& ec);
+
+ASIO_DECL void sync_connect(socket_type s, const socket_addr_type* addr,
+ std::size_t addrlen, asio::error_code& ec);
+
+ASIO_DECL bool non_blocking_connect(
+ socket_type s, asio::error_code& ec);
+
+ASIO_DECL int socketpair(int af, int type, int protocol,
+ socket_type sv[2], asio::error_code& ec);
+
+ASIO_DECL bool sockatmark(socket_type s, asio::error_code& ec);
+
+ASIO_DECL size_t available(socket_type s, asio::error_code& ec);
+
+ASIO_DECL int listen(socket_type s,
+ int backlog, asio::error_code& ec);
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
typedef WSABUF buf;
@@ -209,1699 +118,163 @@ typedef WSABUF buf;
typedef iovec buf;
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-inline void init_buf(buf& b, void* data, size_t size)
-{
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- b.buf = static_cast<char*>(data);
- b.len = static_cast<u_long>(size);
-#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- init_buf_iov_base(b.iov_base, data);
- b.iov_len = size;
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-}
+ASIO_DECL void init_buf(buf& b, void* data, size_t size);
-inline void init_buf(buf& b, const void* data, size_t size)
-{
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- b.buf = static_cast<char*>(const_cast<void*>(data));
- b.len = static_cast<u_long>(size);
-#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- init_buf_iov_base(b.iov_base, const_cast<void*>(data));
- b.iov_len = size;
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-}
+ASIO_DECL void init_buf(buf& b, const void* data, size_t size);
-inline void init_msghdr_msg_name(void*& name, socket_addr_type* addr)
-{
- name = addr;
-}
+ASIO_DECL int recv(socket_type s, buf* bufs, size_t count, int flags,
+ asio::error_code& ec);
-inline void init_msghdr_msg_name(void*& name, const socket_addr_type* addr)
-{
- name = const_cast<socket_addr_type*>(addr);
-}
+ASIO_DECL size_t sync_recv(socket_type s, state_type state, buf* bufs,
+ size_t count, int flags, bool all_empty, asio::error_code& ec);
-template <typename T>
-inline void init_msghdr_msg_name(T& name, socket_addr_type* addr)
-{
- name = reinterpret_cast<T>(addr);
-}
+#if defined(ASIO_HAS_IOCP)
-template <typename T>
-inline void init_msghdr_msg_name(T& name, const socket_addr_type* addr)
-{
- name = reinterpret_cast<T>(const_cast<socket_addr_type*>(addr));
-}
+ASIO_DECL void complete_iocp_recv(state_type state,
+ const weak_cancel_token_type& cancel_token, bool all_empty,
+ asio::error_code& ec, size_t bytes_transferred);
-inline int recv(socket_type s, buf* bufs, size_t count, int flags,
- asio::error_code& ec)
-{
- clear_error(ec);
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- // Receive some data.
- DWORD recv_buf_count = static_cast<DWORD>(count);
- DWORD bytes_transferred = 0;
- DWORD recv_flags = flags;
- int result = error_wrapper(::WSARecv(s, bufs,
- recv_buf_count, &bytes_transferred, &recv_flags, 0, 0), ec);
- if (result != 0)
- return -1;
- clear_error(ec);
- return bytes_transferred;
-#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- msghdr msg = msghdr();
- msg.msg_iov = bufs;
- msg.msg_iovlen = count;
- int result = error_wrapper(::recvmsg(s, &msg, flags), ec);
- if (result >= 0)
- clear_error(ec);
- return result;
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-}
+#else // defined(ASIO_HAS_IOCP)
+
+ASIO_DECL bool non_blocking_recv(socket_type s,
+ buf* bufs, size_t count, int flags, bool is_stream,
+ asio::error_code& ec, size_t& bytes_transferred);
+
+#endif // defined(ASIO_HAS_IOCP)
-inline int recvfrom(socket_type s, buf* bufs, size_t count, int flags,
+ASIO_DECL int recvfrom(socket_type s, buf* bufs, size_t count, int flags,
socket_addr_type* addr, std::size_t* addrlen,
- asio::error_code& ec)
-{
- clear_error(ec);
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- // Receive some data.
- DWORD recv_buf_count = static_cast<DWORD>(count);
- DWORD bytes_transferred = 0;
- DWORD recv_flags = flags;
- int tmp_addrlen = (int)*addrlen;
- int result = error_wrapper(::WSARecvFrom(s, bufs, recv_buf_count,
- &bytes_transferred, &recv_flags, addr, &tmp_addrlen, 0, 0), ec);
- *addrlen = (std::size_t)tmp_addrlen;
- if (result != 0)
- return -1;
- clear_error(ec);
- return bytes_transferred;
-#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- msghdr msg = msghdr();
- init_msghdr_msg_name(msg.msg_name, addr);
- msg.msg_namelen = *addrlen;
- msg.msg_iov = bufs;
- msg.msg_iovlen = count;
- int result = error_wrapper(::recvmsg(s, &msg, flags), ec);
- *addrlen = msg.msg_namelen;
- if (result >= 0)
- clear_error(ec);
- return result;
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-}
+ asio::error_code& ec);
-inline int send(socket_type s, const buf* bufs, size_t count, int flags,
- asio::error_code& ec)
-{
- clear_error(ec);
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- // Send the data.
- DWORD send_buf_count = static_cast<DWORD>(count);
- DWORD bytes_transferred = 0;
- DWORD send_flags = flags;
- int result = error_wrapper(::WSASend(s, const_cast<buf*>(bufs),
- send_buf_count, &bytes_transferred, send_flags, 0, 0), ec);
- if (result != 0)
- return -1;
- clear_error(ec);
- return bytes_transferred;
-#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- msghdr msg = msghdr();
- msg.msg_iov = const_cast<buf*>(bufs);
- msg.msg_iovlen = count;
-#if defined(__linux__)
- flags |= MSG_NOSIGNAL;
-#endif // defined(__linux__)
- int result = error_wrapper(::sendmsg(s, &msg, flags), ec);
- if (result >= 0)
- clear_error(ec);
- return result;
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-}
+ASIO_DECL size_t sync_recvfrom(socket_type s, state_type state,
+ buf* bufs, size_t count, int flags, socket_addr_type* addr,
+ std::size_t* addrlen, asio::error_code& ec);
+
+#if defined(ASIO_HAS_IOCP)
+
+ASIO_DECL void complete_iocp_recvfrom(
+ const weak_cancel_token_type& cancel_token,
+ asio::error_code& ec);
+
+#else // defined(ASIO_HAS_IOCP)
+
+ASIO_DECL bool non_blocking_recvfrom(socket_type s,
+ buf* bufs, size_t count, int flags,
+ socket_addr_type* addr, std::size_t* addrlen,
+ asio::error_code& ec, size_t& bytes_transferred);
+
+#endif // defined(ASIO_HAS_IOCP)
+
+ASIO_DECL int send(socket_type s, const buf* bufs,
+ size_t count, int flags, asio::error_code& ec);
+
+ASIO_DECL size_t sync_send(socket_type s, state_type state,
+ const buf* bufs, size_t count, int flags,
+ bool all_empty, asio::error_code& ec);
+
+#if defined(ASIO_HAS_IOCP)
+
+ASIO_DECL void complete_iocp_send(
+ const weak_cancel_token_type& cancel_token,
+ asio::error_code& ec);
+
+#else // defined(ASIO_HAS_IOCP)
-inline int sendto(socket_type s, const buf* bufs, size_t count, int flags,
+ASIO_DECL bool non_blocking_send(socket_type s,
+ const buf* bufs, size_t count, int flags,
+ asio::error_code& ec, size_t& bytes_transferred);
+
+#endif // defined(ASIO_HAS_IOCP)
+
+ASIO_DECL int sendto(socket_type s, const buf* bufs, size_t count,
+ int flags, const socket_addr_type* addr, std::size_t addrlen,
+ asio::error_code& ec);
+
+ASIO_DECL size_t sync_sendto(socket_type s, state_type state,
+ const buf* bufs, size_t count, int flags, const socket_addr_type* addr,
+ std::size_t addrlen, asio::error_code& ec);
+
+#if !defined(ASIO_HAS_IOCP)
+
+ASIO_DECL bool non_blocking_sendto(socket_type s,
+ const buf* bufs, size_t count, int flags,
const socket_addr_type* addr, std::size_t addrlen,
- asio::error_code& ec)
-{
- clear_error(ec);
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- // Send the data.
- DWORD send_buf_count = static_cast<DWORD>(count);
- DWORD bytes_transferred = 0;
- int result = error_wrapper(::WSASendTo(s, const_cast<buf*>(bufs),
- send_buf_count, &bytes_transferred, flags, addr,
- static_cast<int>(addrlen), 0, 0), ec);
- if (result != 0)
- return -1;
- clear_error(ec);
- return bytes_transferred;
-#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- msghdr msg = msghdr();
- init_msghdr_msg_name(msg.msg_name, addr);
- msg.msg_namelen = addrlen;
- msg.msg_iov = const_cast<buf*>(bufs);
- msg.msg_iovlen = count;
-#if defined(__linux__)
- flags |= MSG_NOSIGNAL;
-#endif // defined(__linux__)
- int result = error_wrapper(::sendmsg(s, &msg, flags), ec);
- if (result >= 0)
- clear_error(ec);
- return result;
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-}
+ asio::error_code& ec, size_t& bytes_transferred);
-inline socket_type socket(int af, int type, int protocol,
- asio::error_code& ec)
-{
- clear_error(ec);
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- socket_type s = error_wrapper(::WSASocket(af, type, protocol, 0, 0,
- WSA_FLAG_OVERLAPPED), ec);
- if (s == invalid_socket)
- return s;
-
- if (af == AF_INET6)
- {
- // Try to enable the POSIX default behaviour of having IPV6_V6ONLY set to
- // false. This will only succeed on Windows Vista and later versions of
- // Windows, where a dual-stack IPv4/v6 implementation is available.
- DWORD optval = 0;
- ::setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
- reinterpret_cast<const char*>(&optval), sizeof(optval));
- }
-
- clear_error(ec);
-
- return s;
-#elif defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__)
- socket_type s = error_wrapper(::socket(af, type, protocol), ec);
- if (s == invalid_socket)
- return s;
-
- int optval = 1;
- int result = error_wrapper(::setsockopt(s,
- SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)), ec);
- if (result != 0)
- {
- ::close(s);
- return invalid_socket;
- }
-
- return s;
-#else
- int s = error_wrapper(::socket(af, type, protocol), ec);
- if (s >= 0)
- clear_error(ec);
- return s;
-#endif
-}
-
-template <typename SockLenType>
-inline int call_setsockopt(SockLenType msghdr::*,
- socket_type s, int level, int optname,
- const void* optval, std::size_t optlen)
-{
- return ::setsockopt(s, level, optname,
- (const char*)optval, (SockLenType)optlen);
-}
+#endif // !defined(ASIO_HAS_IOCP)
-inline int setsockopt(socket_type s, int level, int optname,
- const void* optval, std::size_t optlen, asio::error_code& ec)
-{
- if (level == custom_socket_option_level && optname == always_fail_option)
- {
- ec = asio::error::invalid_argument;
- return -1;
- }
-
-#if defined(__BORLANDC__)
- // Mysteriously, using the getsockopt and setsockopt functions directly with
- // Borland C++ results in incorrect values being set and read. The bug can be
- // worked around by using function addresses resolved with GetProcAddress.
- if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32"))
- {
- typedef int (WSAAPI *sso_t)(SOCKET, int, int, const char*, int);
- if (sso_t sso = (sso_t)::GetProcAddress(winsock_module, "setsockopt"))
- {
- clear_error(ec);
- return error_wrapper(sso(s, level, optname,
- reinterpret_cast<const char*>(optval),
- static_cast<int>(optlen)), ec);
- }
- }
- ec = asio::error::fault;
- return -1;
-#else // defined(__BORLANDC__)
- clear_error(ec);
- int result = error_wrapper(call_setsockopt(&msghdr::msg_namelen,
- s, level, optname, optval, optlen), ec);
- if (result == 0)
- clear_error(ec);
- return result;
-#endif // defined(__BORLANDC__)
-}
-
-template <typename SockLenType>
-inline int call_getsockopt(SockLenType msghdr::*,
- socket_type s, int level, int optname,
- void* optval, std::size_t* optlen)
-{
- SockLenType tmp_optlen = (SockLenType)*optlen;
- int result = ::getsockopt(s, level, optname, (char*)optval, &tmp_optlen);
- *optlen = (std::size_t)tmp_optlen;
- return result;
-}
-
-inline int getsockopt(socket_type s, int level, int optname, void* optval,
- size_t* optlen, asio::error_code& ec)
-{
- if (level == custom_socket_option_level && optname == always_fail_option)
- {
- ec = asio::error::invalid_argument;
- return -1;
- }
-
-#if defined(__BORLANDC__)
- // Mysteriously, using the getsockopt and setsockopt functions directly with
- // Borland C++ results in incorrect values being set and read. The bug can be
- // worked around by using function addresses resolved with GetProcAddress.
- if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32"))
- {
- typedef int (WSAAPI *gso_t)(SOCKET, int, int, char*, int*);
- if (gso_t gso = (gso_t)::GetProcAddress(winsock_module, "getsockopt"))
- {
- clear_error(ec);
- int tmp_optlen = static_cast<int>(*optlen);
- int result = error_wrapper(gso(s, level, optname,
- reinterpret_cast<char*>(optval), &tmp_optlen), ec);
- *optlen = static_cast<size_t>(tmp_optlen);
- if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY
- && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD))
- {
- // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are
- // only supported on Windows Vista and later. To simplify program logic
- // we will fake success of getting this option and specify that the
- // value is non-zero (i.e. true). This corresponds to the behavior of
- // IPv6 sockets on Windows platforms pre-Vista.
- *static_cast<DWORD*>(optval) = 1;
- clear_error(ec);
- }
- return result;
- }
- }
- ec = asio::error::fault;
- return -1;
-#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- clear_error(ec);
- int result = error_wrapper(call_getsockopt(&msghdr::msg_namelen,
- s, level, optname, optval, optlen), ec);
- if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY
- && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD))
- {
- // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are only
- // supported on Windows Vista and later. To simplify program logic we will
- // fake success of getting this option and specify that the value is
- // non-zero (i.e. true). This corresponds to the behavior of IPv6 sockets
- // on Windows platforms pre-Vista.
- *static_cast<DWORD*>(optval) = 1;
- clear_error(ec);
- }
- if (result == 0)
- clear_error(ec);
- return result;
-#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- clear_error(ec);
- int result = error_wrapper(call_getsockopt(&msghdr::msg_namelen,
- s, level, optname, optval, optlen), ec);
-#if defined(__linux__)
- if (result == 0 && level == SOL_SOCKET && *optlen == sizeof(int)
- && (optname == SO_SNDBUF || optname == SO_RCVBUF))
- {
- // On Linux, setting SO_SNDBUF or SO_RCVBUF to N actually causes the kernel
- // to set the buffer size to N*2. Linux puts additional stuff into the
- // buffers so that only about half is actually available to the application.
- // The retrieved value is divided by 2 here to make it appear as though the
- // correct value has been set.
- *static_cast<int*>(optval) /= 2;
- }
-#endif // defined(__linux__)
- if (result == 0)
- clear_error(ec);
- return result;
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-}
+ASIO_DECL socket_type socket(int af, int type, int protocol,
+ asio::error_code& ec);
-template <typename SockLenType>
-inline int call_getpeername(SockLenType msghdr::*,
- socket_type s, socket_addr_type* addr, std::size_t* addrlen)
-{
- SockLenType tmp_addrlen = (SockLenType)*addrlen;
- int result = ::getpeername(s, addr, &tmp_addrlen);
- *addrlen = (std::size_t)tmp_addrlen;
- return result;
-}
-
-inline int getpeername(socket_type s, socket_addr_type* addr,
- std::size_t* addrlen, asio::error_code& ec)
-{
- clear_error(ec);
- int result = error_wrapper(call_getpeername(
- &msghdr::msg_namelen, s, addr, addrlen), ec);
- if (result == 0)
- clear_error(ec);
- return result;
-}
-
-template <typename SockLenType>
-inline int call_getsockname(SockLenType msghdr::*,
- socket_type s, socket_addr_type* addr, std::size_t* addrlen)
-{
- SockLenType tmp_addrlen = (SockLenType)*addrlen;
- int result = ::getsockname(s, addr, &tmp_addrlen);
- *addrlen = (std::size_t)tmp_addrlen;
- return result;
-}
-
-inline int getsockname(socket_type s, socket_addr_type* addr,
- std::size_t* addrlen, asio::error_code& ec)
-{
- clear_error(ec);
- int result = error_wrapper(call_getsockname(
- &msghdr::msg_namelen, s, addr, addrlen), ec);
- if (result == 0)
- clear_error(ec);
- return result;
-}
-
-inline int ioctl(socket_type s, long cmd, ioctl_arg_type* arg,
- asio::error_code& ec)
-{
- clear_error(ec);
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- int result = error_wrapper(::ioctlsocket(s, cmd, arg), ec);
-#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- int result = error_wrapper(::ioctl(s, cmd, arg), ec);
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- if (result >= 0)
- clear_error(ec);
- return result;
-}
+ASIO_DECL int setsockopt(socket_type s, state_type& state,
+ int level, int optname, const void* optval,
+ std::size_t optlen, asio::error_code& ec);
-inline int select(int nfds, fd_set* readfds, fd_set* writefds,
- fd_set* exceptfds, timeval* timeout, asio::error_code& ec)
-{
- clear_error(ec);
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- if (!readfds && !writefds && !exceptfds && timeout)
- {
- DWORD milliseconds = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
- if (milliseconds == 0)
- milliseconds = 1; // Force context switch.
- ::Sleep(milliseconds);
- ec = asio::error_code();
- return 0;
- }
-
- // The select() call allows timeout values measured in microseconds, but the
- // system clock (as wrapped by boost::posix_time::microsec_clock) typically
- // has a resolution of 10 milliseconds. This can lead to a spinning select
- // reactor, meaning increased CPU usage, when waiting for the earliest
- // scheduled timeout if it's less than 10 milliseconds away. To avoid a tight
- // spin we'll use a minimum timeout of 1 millisecond.
- if (timeout && timeout->tv_sec == 0
- && timeout->tv_usec > 0 && timeout->tv_usec < 1000)
- timeout->tv_usec = 1000;
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ASIO_DECL int getsockopt(socket_type s, state_type state,
+ int level, int optname, void* optval,
+ size_t* optlen, asio::error_code& ec);
-#if defined(__hpux) && defined(__HP_aCC)
- timespec ts;
- ts.tv_sec = timeout ? timeout->tv_sec : 0;
- ts.tv_nsec = timeout ? timeout->tv_usec * 1000 : 0;
- return error_wrapper(::pselect(nfds, readfds,
- writefds, exceptfds, timeout ? &ts : 0, 0), ec);
-#else
- int result = error_wrapper(::select(nfds, readfds,
- writefds, exceptfds, timeout), ec);
- if (result >= 0)
- clear_error(ec);
- return result;
-#endif
-}
-
-inline int poll_read(socket_type s, asio::error_code& ec)
-{
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- FD_SET fds;
- FD_ZERO(&fds);
- FD_SET(s, &fds);
- clear_error(ec);
- int result = error_wrapper(::select(s, &fds, 0, 0, 0), ec);
- if (result >= 0)
- clear_error(ec);
- return result;
-#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- pollfd fds;
- fds.fd = s;
- fds.events = POLLIN;
- fds.revents = 0;
- clear_error(ec);
- int result = error_wrapper(::poll(&fds, 1, -1), ec);
- if (result >= 0)
- clear_error(ec);
- return result;
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-}
+ASIO_DECL int getpeername(socket_type s, socket_addr_type* addr,
+ std::size_t* addrlen, bool cached, asio::error_code& ec);
-inline int poll_write(socket_type s, asio::error_code& ec)
-{
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- FD_SET fds;
- FD_ZERO(&fds);
- FD_SET(s, &fds);
- clear_error(ec);
- int result = error_wrapper(::select(s, 0, &fds, 0, 0), ec);
- if (result >= 0)
- clear_error(ec);
- return result;
-#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- pollfd fds;
- fds.fd = s;
- fds.events = POLLOUT;
- fds.revents = 0;
- clear_error(ec);
- int result = error_wrapper(::poll(&fds, 1, -1), ec);
- if (result >= 0)
- clear_error(ec);
- return result;
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-}
+ASIO_DECL int getsockname(socket_type s, socket_addr_type* addr,
+ std::size_t* addrlen, asio::error_code& ec);
-inline int poll_connect(socket_type s, asio::error_code& ec)
-{
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- FD_SET write_fds;
- FD_ZERO(&write_fds);
- FD_SET(s, &write_fds);
- FD_SET except_fds;
- FD_ZERO(&except_fds);
- FD_SET(s, &except_fds);
- clear_error(ec);
- int result = error_wrapper(::select(s, 0, &write_fds, &except_fds, 0), ec);
- if (result >= 0)
- clear_error(ec);
- return result;
-#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- pollfd fds;
- fds.fd = s;
- fds.events = POLLOUT;
- fds.revents = 0;
- clear_error(ec);
- int result = error_wrapper(::poll(&fds, 1, -1), ec);
- if (result >= 0)
- clear_error(ec);
- return result;
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-}
+ASIO_DECL int ioctl(socket_type s, state_type& state,
+ int cmd, ioctl_arg_type* arg, asio::error_code& ec);
-inline const char* inet_ntop(int af, const void* src, char* dest, size_t length,
- unsigned long scope_id, asio::error_code& ec)
-{
- clear_error(ec);
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- using namespace std; // For memcpy.
-
- if (af != AF_INET && af != AF_INET6)
- {
- ec = asio::error::address_family_not_supported;
- return 0;
- }
-
- union
- {
- socket_addr_type base;
- sockaddr_storage_type storage;
- sockaddr_in4_type v4;
- sockaddr_in6_type v6;
- } address;
- DWORD address_length;
- if (af == AF_INET)
- {
- address_length = sizeof(sockaddr_in4_type);
- address.v4.sin_family = AF_INET;
- address.v4.sin_port = 0;
- memcpy(&address.v4.sin_addr, src, sizeof(in4_addr_type));
- }
- else // AF_INET6
- {
- address_length = sizeof(sockaddr_in6_type);
- address.v6.sin6_family = AF_INET6;
- address.v6.sin6_port = 0;
- address.v6.sin6_flowinfo = 0;
- address.v6.sin6_scope_id = scope_id;
- memcpy(&address.v6.sin6_addr, src, sizeof(in6_addr_type));
- }
-
- DWORD string_length = static_cast<DWORD>(length);
-#if defined(BOOST_NO_ANSI_APIS)
- LPWSTR string_buffer = (LPWSTR)_alloca(length * sizeof(WCHAR));
- int result = error_wrapper(::WSAAddressToStringW(&address.base,
- address_length, 0, string_buffer, &string_length), ec);
- ::WideCharToMultiByte(CP_ACP, 0, string_buffer, -1, dest, length, 0, 0);
-#else
- int result = error_wrapper(::WSAAddressToStringA(
- &address.base, address_length, 0, dest, &string_length), ec);
-#endif
-
- // Windows may set error code on success.
- if (result != socket_error_retval)
- clear_error(ec);
-
- // Windows may not set an error code on failure.
- else if (result == socket_error_retval && !ec)
- ec = asio::error::invalid_argument;
-
- return result == socket_error_retval ? 0 : dest;
-#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- const char* result = error_wrapper(::inet_ntop(af, src, dest, length), ec);
- if (result == 0 && !ec)
- ec = asio::error::invalid_argument;
- if (result != 0 && af == AF_INET6 && scope_id != 0)
- {
- using namespace std; // For strcat and sprintf.
- char if_name[IF_NAMESIZE + 1] = "%";
- const in6_addr_type* ipv6_address = static_cast<const in6_addr_type*>(src);
- bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address);
- if (!is_link_local || if_indextoname(scope_id, if_name + 1) == 0)
- sprintf(if_name + 1, "%lu", scope_id);
- strcat(dest, if_name);
- }
- return result;
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-}
+ASIO_DECL int select(int nfds, fd_set* readfds, fd_set* writefds,
+ fd_set* exceptfds, timeval* timeout, asio::error_code& ec);
-inline int inet_pton(int af, const char* src, void* dest,
- unsigned long* scope_id, asio::error_code& ec)
-{
- clear_error(ec);
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- using namespace std; // For memcpy and strcmp.
-
- if (af != AF_INET && af != AF_INET6)
- {
- ec = asio::error::address_family_not_supported;
- return -1;
- }
-
- union
- {
- socket_addr_type base;
- sockaddr_storage_type storage;
- sockaddr_in4_type v4;
- sockaddr_in6_type v6;
- } address;
- int address_length = sizeof(sockaddr_storage_type);
-#if defined(BOOST_NO_ANSI_APIS)
- int num_wide_chars = strlen(src) + 1;
- LPWSTR wide_buffer = (LPWSTR)_alloca(num_wide_chars * sizeof(WCHAR));
- ::MultiByteToWideChar(CP_ACP, 0, src, -1, wide_buffer, num_wide_chars);
- int result = error_wrapper(::WSAStringToAddressW(
- wide_buffer, af, 0, &address.base, &address_length), ec);
-#else
- int result = error_wrapper(::WSAStringToAddressA(
- const_cast<char*>(src), af, 0, &address.base, &address_length), ec);
-#endif
-
- if (af == AF_INET)
- {
- if (result != socket_error_retval)
- {
- memcpy(dest, &address.v4.sin_addr, sizeof(in4_addr_type));
- clear_error(ec);
- }
- else if (strcmp(src, "255.255.255.255") == 0)
- {
- static_cast<in4_addr_type*>(dest)->s_addr = INADDR_NONE;
- clear_error(ec);
- }
- }
- else // AF_INET6
- {
- if (result != socket_error_retval)
- {
- memcpy(dest, &address.v6.sin6_addr, sizeof(in6_addr_type));
- if (scope_id)
- *scope_id = address.v6.sin6_scope_id;
- clear_error(ec);
- }
- }
-
- // Windows may not set an error code on failure.
- if (result == socket_error_retval && !ec)
- ec = asio::error::invalid_argument;
-
- if (result != socket_error_retval)
- clear_error(ec);
-
- return result == socket_error_retval ? -1 : 1;
-#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- int result = error_wrapper(::inet_pton(af, src, dest), ec);
- if (result <= 0 && !ec)
- ec = asio::error::invalid_argument;
- if (result > 0 && af == AF_INET6 && scope_id)
- {
- using namespace std; // For strchr and atoi.
- *scope_id = 0;
- if (const char* if_name = strchr(src, '%'))
- {
- in6_addr_type* ipv6_address = static_cast<in6_addr_type*>(dest);
- bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address);
- if (is_link_local)
- *scope_id = if_nametoindex(if_name + 1);
- if (*scope_id == 0)
- *scope_id = atoi(if_name + 1);
- }
- }
- return result;
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-}
+ASIO_DECL int poll_read(socket_type s, asio::error_code& ec);
-inline int gethostname(char* name, int namelen, asio::error_code& ec)
-{
- clear_error(ec);
- int result = error_wrapper(::gethostname(name, namelen), ec);
-#if defined(BOOST_WINDOWS)
- if (result == 0)
- clear_error(ec);
-#endif
- return result;
-}
-
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) \
- || defined(__MACH__) && defined(__APPLE__)
-
-// The following functions are only needed for emulation of getaddrinfo and
-// getnameinfo.
-
-inline asio::error_code translate_netdb_error(int error)
-{
- switch (error)
- {
- case 0:
- return asio::error_code();
- case HOST_NOT_FOUND:
- return asio::error::host_not_found;
- case TRY_AGAIN:
- return asio::error::host_not_found_try_again;
- case NO_RECOVERY:
- return asio::error::no_recovery;
- case NO_DATA:
- return asio::error::no_data;
- default:
- BOOST_ASSERT(false);
- return asio::error::invalid_argument;
- }
-}
-
-inline hostent* gethostbyaddr(const char* addr, int length, int af,
- hostent* result, char* buffer, int buflength, asio::error_code& ec)
-{
- clear_error(ec);
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- (void)(buffer);
- (void)(buflength);
- hostent* retval = error_wrapper(::gethostbyaddr(addr, length, af), ec);
- if (!retval)
- return 0;
- clear_error(ec);
- *result = *retval;
- return retval;
-#elif defined(__sun) || defined(__QNX__)
- int error = 0;
- hostent* retval = error_wrapper(::gethostbyaddr_r(addr, length, af, result,
- buffer, buflength, &error), ec);
- if (error)
- ec = translate_netdb_error(error);
- return retval;
-#elif defined(__MACH__) && defined(__APPLE__)
- (void)(buffer);
- (void)(buflength);
- int error = 0;
- hostent* retval = error_wrapper(::getipnodebyaddr(
- addr, length, af, &error), ec);
- if (error)
- ec = translate_netdb_error(error);
- if (!retval)
- return 0;
- *result = *retval;
- return retval;
-#else
- hostent* retval = 0;
- int error = 0;
- error_wrapper(::gethostbyaddr_r(addr, length, af, result, buffer,
- buflength, &retval, &error), ec);
- if (error)
- ec = translate_netdb_error(error);
- return retval;
-#endif
-}
-
-inline hostent* gethostbyname(const char* name, int af, struct hostent* result,
- char* buffer, int buflength, int ai_flags, asio::error_code& ec)
-{
- clear_error(ec);
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- (void)(buffer);
- (void)(buflength);
- (void)(ai_flags);
- if (af != AF_INET)
- {
- ec = asio::error::address_family_not_supported;
- return 0;
- }
- hostent* retval = error_wrapper(::gethostbyname(name), ec);
- if (!retval)
- return 0;
- clear_error(ec);
- *result = *retval;
- return result;
-#elif defined(__sun) || defined(__QNX__)
- (void)(ai_flags);
- if (af != AF_INET)
- {
- ec = asio::error::address_family_not_supported;
- return 0;
- }
- int error = 0;
- hostent* retval = error_wrapper(::gethostbyname_r(name, result, buffer,
- buflength, &error), ec);
- if (error)
- ec = translate_netdb_error(error);
- return retval;
-#elif defined(__MACH__) && defined(__APPLE__)
- (void)(buffer);
- (void)(buflength);
- int error = 0;
- hostent* retval = error_wrapper(::getipnodebyname(
- name, af, ai_flags, &error), ec);
- if (error)
- ec = translate_netdb_error(error);
- if (!retval)
- return 0;
- *result = *retval;
- return retval;
-#else
- (void)(ai_flags);
- if (af != AF_INET)
- {
- ec = asio::error::address_family_not_supported;
- return 0;
- }
- hostent* retval = 0;
- int error = 0;
- error_wrapper(::gethostbyname_r(name, result,
- buffer, buflength, &retval, &error), ec);
- if (error)
- ec = translate_netdb_error(error);
- return retval;
-#endif
-}
-
-inline void freehostent(hostent* h)
-{
-#if defined(__MACH__) && defined(__APPLE__)
- if (h)
- ::freehostent(h);
-#else
- (void)(h);
-#endif
-}
-
-// Emulation of getaddrinfo based on implementation in:
-// Stevens, W. R., UNIX Network Programming Vol. 1, 2nd Ed., Prentice-Hall 1998.
-
-struct gai_search
-{
- const char* host;
- int family;
-};
+ASIO_DECL int poll_write(socket_type s, asio::error_code& ec);
-inline int gai_nsearch(const char* host,
- const addrinfo_type* hints, gai_search (&search)[2])
-{
- int search_count = 0;
- if (host == 0 || host[0] == '\0')
- {
- if (hints->ai_flags & AI_PASSIVE)
- {
- // No host and AI_PASSIVE implies wildcard bind.
- switch (hints->ai_family)
- {
- case AF_INET:
- search[search_count].host = "0.0.0.0";
- search[search_count].family = AF_INET;
- ++search_count;
- break;
- case AF_INET6:
- search[search_count].host = "0::0";
- search[search_count].family = AF_INET6;
- ++search_count;
- break;
- case AF_UNSPEC:
- search[search_count].host = "0::0";
- search[search_count].family = AF_INET6;
- ++search_count;
- search[search_count].host = "0.0.0.0";
- search[search_count].family = AF_INET;
- ++search_count;
- break;
- default:
- break;
- }
- }
- else
- {
- // No host and not AI_PASSIVE means connect to local host.
- switch (hints->ai_family)
- {
- case AF_INET:
- search[search_count].host = "localhost";
- search[search_count].family = AF_INET;
- ++search_count;
- break;
- case AF_INET6:
- search[search_count].host = "localhost";
- search[search_count].family = AF_INET6;
- ++search_count;
- break;
- case AF_UNSPEC:
- search[search_count].host = "localhost";
- search[search_count].family = AF_INET6;
- ++search_count;
- search[search_count].host = "localhost";
- search[search_count].family = AF_INET;
- ++search_count;
- break;
- default:
- break;
- }
- }
- }
- else
- {
- // Host is specified.
- switch (hints->ai_family)
- {
- case AF_INET:
- search[search_count].host = host;
- search[search_count].family = AF_INET;
- ++search_count;
- break;
- case AF_INET6:
- search[search_count].host = host;
- search[search_count].family = AF_INET6;
- ++search_count;
- break;
- case AF_UNSPEC:
- search[search_count].host = host;
- search[search_count].family = AF_INET6;
- ++search_count;
- search[search_count].host = host;
- search[search_count].family = AF_INET;
- ++search_count;
- break;
- default:
- break;
- }
- }
- return search_count;
-}
-
-template <typename T>
-inline T* gai_alloc(std::size_t size = sizeof(T))
-{
- using namespace std;
- T* p = static_cast<T*>(::operator new(size, std::nothrow));
- if (p)
- memset(p, 0, size);
- return p;
-}
-
-inline void gai_free(void* p)
-{
- ::operator delete(p);
-}
+ASIO_DECL int poll_connect(socket_type s, asio::error_code& ec);
-inline void gai_strcpy(char* target, const char* source, std::size_t max_size)
-{
- using namespace std;
-#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE)
- strcpy_s(target, max_size, source);
-#else
- *target = 0;
- strncat(target, source, max_size);
-#endif
-}
-
-enum { gai_clone_flag = 1 << 30 };
-
-inline int gai_aistruct(addrinfo_type*** next, const addrinfo_type* hints,
- const void* addr, int family)
-{
- using namespace std;
-
- addrinfo_type* ai = gai_alloc<addrinfo_type>();
- if (ai == 0)
- return EAI_MEMORY;
-
- ai->ai_next = 0;
- **next = ai;
- *next = &ai->ai_next;
-
- ai->ai_canonname = 0;
- ai->ai_socktype = hints->ai_socktype;
- if (ai->ai_socktype == 0)
- ai->ai_flags |= gai_clone_flag;
- ai->ai_protocol = hints->ai_protocol;
- ai->ai_family = family;
-
- switch (ai->ai_family)
- {
- case AF_INET:
- {
- sockaddr_in4_type* sinptr = gai_alloc<sockaddr_in4_type>();
- if (sinptr == 0)
- return EAI_MEMORY;
- sinptr->sin_family = AF_INET;
- memcpy(&sinptr->sin_addr, addr, sizeof(in4_addr_type));
- ai->ai_addr = reinterpret_cast<sockaddr*>(sinptr);
- ai->ai_addrlen = sizeof(sockaddr_in4_type);
- break;
- }
- case AF_INET6:
- {
- sockaddr_in6_type* sin6ptr = gai_alloc<sockaddr_in6_type>();
- if (sin6ptr == 0)
- return EAI_MEMORY;
- sin6ptr->sin6_family = AF_INET6;
- memcpy(&sin6ptr->sin6_addr, addr, sizeof(in6_addr_type));
- ai->ai_addr = reinterpret_cast<sockaddr*>(sin6ptr);
- ai->ai_addrlen = sizeof(sockaddr_in6_type);
- break;
- }
- default:
- break;
- }
-
- return 0;
-}
-
-inline addrinfo_type* gai_clone(addrinfo_type* ai)
-{
- using namespace std;
+ASIO_DECL const char* inet_ntop(int af, const void* src, char* dest,
+ size_t length, unsigned long scope_id, asio::error_code& ec);
- addrinfo_type* new_ai = gai_alloc<addrinfo_type>();
- if (new_ai == 0)
- return new_ai;
+ASIO_DECL int inet_pton(int af, const char* src, void* dest,
+ unsigned long* scope_id, asio::error_code& ec);
- new_ai->ai_next = ai->ai_next;
- ai->ai_next = new_ai;
+ASIO_DECL int gethostname(char* name,
+ int namelen, asio::error_code& ec);
- new_ai->ai_flags = 0;
- new_ai->ai_family = ai->ai_family;
- new_ai->ai_socktype = ai->ai_socktype;
- new_ai->ai_protocol = ai->ai_protocol;
- new_ai->ai_canonname = 0;
- new_ai->ai_addrlen = ai->ai_addrlen;
- new_ai->ai_addr = gai_alloc<sockaddr>(ai->ai_addrlen);
- memcpy(new_ai->ai_addr, ai->ai_addr, ai->ai_addrlen);
+ASIO_DECL asio::error_code getaddrinfo(const char* host,
+ const char* service, const addrinfo_type& hints,
+ addrinfo_type** result, asio::error_code& ec);
- return new_ai;
-}
+ASIO_DECL asio::error_code background_getaddrinfo(
+ const weak_cancel_token_type& cancel_token, const char* host,
+ const char* service, const addrinfo_type& hints,
+ addrinfo_type** result, asio::error_code& ec);
-inline int gai_port(addrinfo_type* aihead, int port, int socktype)
-{
- int num_found = 0;
-
- for (addrinfo_type* ai = aihead; ai; ai = ai->ai_next)
- {
- if (ai->ai_flags & gai_clone_flag)
- {
- if (ai->ai_socktype != 0)
- {
- ai = gai_clone(ai);
- if (ai == 0)
- return -1;
- // ai now points to newly cloned entry.
- }
- }
- else if (ai->ai_socktype != socktype)
- {
- // Ignore if mismatch on socket type.
- continue;
- }
-
- ai->ai_socktype = socktype;
-
- switch (ai->ai_family)
- {
- case AF_INET:
- {
- sockaddr_in4_type* sinptr =
- reinterpret_cast<sockaddr_in4_type*>(ai->ai_addr);
- sinptr->sin_port = port;
- ++num_found;
- break;
- }
- case AF_INET6:
- {
- sockaddr_in6_type* sin6ptr =
- reinterpret_cast<sockaddr_in6_type*>(ai->ai_addr);
- sin6ptr->sin6_port = port;
- ++num_found;
- break;
- }
- default:
- break;
- }
- }
-
- return num_found;
-}
-
-inline int gai_serv(addrinfo_type* aihead,
- const addrinfo_type* hints, const char* serv)
-{
- using namespace std;
-
- int num_found = 0;
-
- if (
-#if defined(AI_NUMERICSERV)
- (hints->ai_flags & AI_NUMERICSERV) ||
-#endif
- isdigit(serv[0]))
- {
- int port = htons(atoi(serv));
- if (hints->ai_socktype)
- {
- // Caller specifies socket type.
- int rc = gai_port(aihead, port, hints->ai_socktype);
- if (rc < 0)
- return EAI_MEMORY;
- num_found += rc;
- }
- else
- {
- // Caller does not specify socket type.
- int rc = gai_port(aihead, port, SOCK_STREAM);
- if (rc < 0)
- return EAI_MEMORY;
- num_found += rc;
- rc = gai_port(aihead, port, SOCK_DGRAM);
- if (rc < 0)
- return EAI_MEMORY;
- num_found += rc;
- }
- }
- else
- {
- // Try service name with TCP first, then UDP.
- if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_STREAM)
- {
- servent* sptr = getservbyname(serv, "tcp");
- if (sptr != 0)
- {
- int rc = gai_port(aihead, sptr->s_port, SOCK_STREAM);
- if (rc < 0)
- return EAI_MEMORY;
- num_found += rc;
- }
- }
- if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_DGRAM)
- {
- servent* sptr = getservbyname(serv, "udp");
- if (sptr != 0)
- {
- int rc = gai_port(aihead, sptr->s_port, SOCK_DGRAM);
- if (rc < 0)
- return EAI_MEMORY;
- num_found += rc;
- }
- }
- }
-
- if (num_found == 0)
- {
- if (hints->ai_socktype == 0)
- {
- // All calls to getservbyname() failed.
- return EAI_NONAME;
- }
- else
- {
- // Service not supported for socket type.
- return EAI_SERVICE;
- }
- }
-
- return 0;
-}
-
-inline int gai_echeck(const char* host, const char* service,
- int flags, int family, int socktype, int protocol)
-{
- (void)(flags);
- (void)(protocol);
-
- // Host or service must be specified.
- if (host == 0 || host[0] == '\0')
- if (service == 0 || service[0] == '\0')
- return EAI_NONAME;
-
- // Check combination of family and socket type.
- switch (family)
- {
- case AF_UNSPEC:
- break;
- case AF_INET:
- case AF_INET6:
- if (service != 0 && service[0] != '\0')
- if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
- return EAI_SOCKTYPE;
- break;
- default:
- return EAI_FAMILY;
- }
-
- return 0;
-}
-
-inline void freeaddrinfo_emulation(addrinfo_type* aihead)
-{
- addrinfo_type* ai = aihead;
- while (ai)
- {
- gai_free(ai->ai_addr);
- gai_free(ai->ai_canonname);
- addrinfo_type* ainext = ai->ai_next;
- gai_free(ai);
- ai = ainext;
- }
-}
-
-inline int getaddrinfo_emulation(const char* host, const char* service,
- const addrinfo_type* hintsp, addrinfo_type** result)
-{
- // Set up linked list of addrinfo structures.
- addrinfo_type* aihead = 0;
- addrinfo_type** ainext = &aihead;
- char* canon = 0;
-
- // Supply default hints if not specified by caller.
- addrinfo_type hints = addrinfo_type();
- hints.ai_family = AF_UNSPEC;
- if (hintsp)
- hints = *hintsp;
-
- // If the resolution is not specifically for AF_INET6, remove the AI_V4MAPPED
- // and AI_ALL flags.
-#if defined(AI_V4MAPPED)
- if (hints.ai_family != AF_INET6)
- hints.ai_flags &= ~AI_V4MAPPED;
-#endif
-#if defined(AI_ALL)
- if (hints.ai_family != AF_INET6)
- hints.ai_flags &= ~AI_ALL;
-#endif
-
- // Basic error checking.
- int rc = gai_echeck(host, service, hints.ai_flags, hints.ai_family,
- hints.ai_socktype, hints.ai_protocol);
- if (rc != 0)
- {
- freeaddrinfo_emulation(aihead);
- return rc;
- }
-
- gai_search search[2];
- int search_count = gai_nsearch(host, &hints, search);
- for (gai_search* sptr = search; sptr < search + search_count; ++sptr)
- {
- // Check for IPv4 dotted decimal string.
- in4_addr_type inaddr;
- asio::error_code ec;
- if (socket_ops::inet_pton(AF_INET, sptr->host, &inaddr, 0, ec) == 1)
- {
- if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET)
- {
- freeaddrinfo_emulation(aihead);
- gai_free(canon);
- return EAI_FAMILY;
- }
- if (sptr->family == AF_INET)
- {
- rc = gai_aistruct(&ainext, &hints, &inaddr, AF_INET);
- if (rc != 0)
- {
- freeaddrinfo_emulation(aihead);
- gai_free(canon);
- return rc;
- }
- }
- continue;
- }
-
- // Check for IPv6 hex string.
- in6_addr_type in6addr;
- if (socket_ops::inet_pton(AF_INET6, sptr->host, &in6addr, 0, ec) == 1)
- {
- if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET6)
- {
- freeaddrinfo_emulation(aihead);
- gai_free(canon);
- return EAI_FAMILY;
- }
- if (sptr->family == AF_INET6)
- {
- rc = gai_aistruct(&ainext, &hints, &in6addr, AF_INET6);
- if (rc != 0)
- {
- freeaddrinfo_emulation(aihead);
- gai_free(canon);
- return rc;
- }
- }
- continue;
- }
-
- // Look up hostname.
- hostent hent;
- char hbuf[8192] = "";
- hostent* hptr = socket_ops::gethostbyname(sptr->host,
- sptr->family, &hent, hbuf, sizeof(hbuf), hints.ai_flags, ec);
- if (hptr == 0)
- {
- if (search_count == 2)
- {
- // Failure is OK if there are multiple searches.
- continue;
- }
- freeaddrinfo_emulation(aihead);
- gai_free(canon);
- if (ec == asio::error::host_not_found)
- return EAI_NONAME;
- if (ec == asio::error::host_not_found_try_again)
- return EAI_AGAIN;
- if (ec == asio::error::no_recovery)
- return EAI_FAIL;
- if (ec == asio::error::no_data)
- return EAI_NONAME;
- return EAI_NONAME;
- }
-
- // Check for address family mismatch if one was specified.
- if (hints.ai_family != AF_UNSPEC && hints.ai_family != hptr->h_addrtype)
- {
- freeaddrinfo_emulation(aihead);
- gai_free(canon);
- socket_ops::freehostent(hptr);
- return EAI_FAMILY;
- }
-
- // Save canonical name first time.
- if (host != 0 && host[0] != '\0' && hptr->h_name && hptr->h_name[0]
- && (hints.ai_flags & AI_CANONNAME) && canon == 0)
- {
- std::size_t canon_len = strlen(hptr->h_name) + 1;
- canon = gai_alloc<char>(canon_len);
- if (canon == 0)
- {
- freeaddrinfo_emulation(aihead);
- socket_ops::freehostent(hptr);
- return EAI_MEMORY;
- }
- gai_strcpy(canon, hptr->h_name, canon_len);
- }
-
- // Create an addrinfo structure for each returned address.
- for (char** ap = hptr->h_addr_list; *ap; ++ap)
- {
- rc = gai_aistruct(&ainext, &hints, *ap, hptr->h_addrtype);
- if (rc != 0)
- {
- freeaddrinfo_emulation(aihead);
- gai_free(canon);
- socket_ops::freehostent(hptr);
- return EAI_FAMILY;
- }
- }
-
- socket_ops::freehostent(hptr);
- }
-
- // Check if we found anything.
- if (aihead == 0)
- {
- gai_free(canon);
- return EAI_NONAME;
- }
-
- // Return canonical name in first entry.
- if (host != 0 && host[0] != '\0' && (hints.ai_flags & AI_CANONNAME))
- {
- if (canon)
- {
- aihead->ai_canonname = canon;
- canon = 0;
- }
- else
- {
- std::size_t canonname_len = strlen(search[0].host) + 1;
- aihead->ai_canonname = gai_alloc<char>(canonname_len);
- if (aihead->ai_canonname == 0)
- {
- freeaddrinfo_emulation(aihead);
- return EAI_MEMORY;
- }
- gai_strcpy(aihead->ai_canonname, search[0].host, canonname_len);
- }
- }
- gai_free(canon);
-
- // Process the service name.
- if (service != 0 && service[0] != '\0')
- {
- rc = gai_serv(aihead, &hints, service);
- if (rc != 0)
- {
- freeaddrinfo_emulation(aihead);
- return rc;
- }
- }
-
- // Return result to caller.
- *result = aihead;
- return 0;
-}
-
-inline asio::error_code getnameinfo_emulation(
- const socket_addr_type* sa, std::size_t salen, char* host,
- std::size_t hostlen, char* serv, std::size_t servlen, int flags,
- asio::error_code& ec)
-{
- using namespace std;
-
- const char* addr;
- size_t addr_len;
- unsigned short port;
- switch (sa->sa_family)
- {
- case AF_INET:
- if (salen != sizeof(sockaddr_in4_type))
- {
- return ec = asio::error::invalid_argument;
- }
- addr = reinterpret_cast<const char*>(
- &reinterpret_cast<const sockaddr_in4_type*>(sa)->sin_addr);
- addr_len = sizeof(in4_addr_type);
- port = reinterpret_cast<const sockaddr_in4_type*>(sa)->sin_port;
- break;
- case AF_INET6:
- if (salen != sizeof(sockaddr_in6_type))
- {
- return ec = asio::error::invalid_argument;
- }
- addr = reinterpret_cast<const char*>(
- &reinterpret_cast<const sockaddr_in6_type*>(sa)->sin6_addr);
- addr_len = sizeof(in6_addr_type);
- port = reinterpret_cast<const sockaddr_in6_type*>(sa)->sin6_port;
- break;
- default:
- return ec = asio::error::address_family_not_supported;
- }
-
- if (host && hostlen > 0)
- {
- if (flags & NI_NUMERICHOST)
- {
- if (socket_ops::inet_ntop(sa->sa_family, addr, host, hostlen, 0, ec) == 0)
- {
- return ec;
- }
- }
- else
- {
- hostent hent;
- char hbuf[8192] = "";
- hostent* hptr = socket_ops::gethostbyaddr(addr,
- static_cast<int>(addr_len), sa->sa_family,
- &hent, hbuf, sizeof(hbuf), ec);
- if (hptr && hptr->h_name && hptr->h_name[0] != '\0')
- {
- if (flags & NI_NOFQDN)
- {
- char* dot = strchr(hptr->h_name, '.');
- if (dot)
- {
- *dot = 0;
- }
- }
- gai_strcpy(host, hptr->h_name, hostlen);
- socket_ops::freehostent(hptr);
- }
- else
- {
- socket_ops::freehostent(hptr);
- if (flags & NI_NAMEREQD)
- {
- return ec = asio::error::host_not_found;
- }
- if (socket_ops::inet_ntop(sa->sa_family,
- addr, host, hostlen, 0, ec) == 0)
- {
- return ec;
- }
- }
- }
- }
-
- if (serv && servlen > 0)
- {
- if (flags & NI_NUMERICSERV)
- {
- if (servlen < 6)
- {
- return ec = asio::error::no_buffer_space;
- }
-#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE)
- sprintf_s(serv, servlen, "%u", ntohs(port));
-#else
- sprintf(serv, "%u", ntohs(port));
-#endif
- }
- else
- {
-#if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS)
- static ::pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- ::pthread_mutex_lock(&mutex);
-#endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS)
- servent* sptr = ::getservbyport(port, (flags & NI_DGRAM) ? "udp" : 0);
- if (sptr && sptr->s_name && sptr->s_name[0] != '\0')
- {
- gai_strcpy(serv, sptr->s_name, servlen);
- }
- else
- {
- if (servlen < 6)
- {
- return ec = asio::error::no_buffer_space;
- }
-#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE)
- sprintf_s(serv, servlen, "%u", ntohs(port));
-#else
- sprintf(serv, "%u", ntohs(port));
-#endif
- }
-#if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS)
- ::pthread_mutex_unlock(&mutex);
-#endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS)
- }
- }
-
- clear_error(ec);
- return ec;
-}
+ASIO_DECL void freeaddrinfo(addrinfo_type* ai);
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- // || defined(__MACH__) && defined(__APPLE__)
+ASIO_DECL asio::error_code getnameinfo(
+ const socket_addr_type* addr, std::size_t addrlen,
+ char* host, std::size_t hostlen, char* serv,
+ std::size_t servlen, int flags, asio::error_code& ec);
-inline asio::error_code translate_addrinfo_error(int error)
-{
- switch (error)
- {
- case 0:
- return asio::error_code();
- case EAI_AGAIN:
- return asio::error::host_not_found_try_again;
- case EAI_BADFLAGS:
- return asio::error::invalid_argument;
- case EAI_FAIL:
- return asio::error::no_recovery;
- case EAI_FAMILY:
- return asio::error::address_family_not_supported;
- case EAI_MEMORY:
- return asio::error::no_memory;
- case EAI_NONAME:
-#if defined(EAI_ADDRFAMILY)
- case EAI_ADDRFAMILY:
-#endif
-#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
- case EAI_NODATA:
-#endif
- return asio::error::host_not_found;
- case EAI_SERVICE:
- return asio::error::service_not_found;
- case EAI_SOCKTYPE:
- return asio::error::socket_type_not_supported;
- default: // Possibly the non-portable EAI_SYSTEM.
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- return asio::error_code(
- WSAGetLastError(), asio::error::get_system_category());
-#else
- return asio::error_code(
- errno, asio::error::get_system_category());
-#endif
- }
-}
-
-inline asio::error_code getaddrinfo(const char* host,
- const char* service, const addrinfo_type* hints, addrinfo_type** result,
- asio::error_code& ec)
-{
- clear_error(ec);
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE)
- // Building for Windows XP, Windows Server 2003, or later.
- int error = ::getaddrinfo(host, service, hints, result);
- return ec = translate_addrinfo_error(error);
-# else
- // Building for Windows 2000 or earlier.
- typedef int (WSAAPI *gai_t)(const char*,
- const char*, const addrinfo_type*, addrinfo_type**);
- if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32"))
- {
- if (gai_t gai = (gai_t)::GetProcAddress(winsock_module, "getaddrinfo"))
- {
- int error = gai(host, service, hints, result);
- return ec = translate_addrinfo_error(error);
- }
- }
- int error = getaddrinfo_emulation(host, service, hints, result);
- return ec = translate_addrinfo_error(error);
-# endif
-#elif defined(__MACH__) && defined(__APPLE__)
- int error = getaddrinfo_emulation(host, service, hints, result);
- return ec = translate_addrinfo_error(error);
-#else
- int error = ::getaddrinfo(host, service, hints, result);
- return ec = translate_addrinfo_error(error);
-#endif
-}
-
-inline void freeaddrinfo(addrinfo_type* ai)
-{
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE)
- // Building for Windows XP, Windows Server 2003, or later.
- ::freeaddrinfo(ai);
-# else
- // Building for Windows 2000 or earlier.
- typedef int (WSAAPI *fai_t)(addrinfo_type*);
- if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32"))
- {
- if (fai_t fai = (fai_t)::GetProcAddress(winsock_module, "freeaddrinfo"))
- {
- fai(ai);
- return;
- }
- }
- freeaddrinfo_emulation(ai);
-# endif
-#elif defined(__MACH__) && defined(__APPLE__)
- freeaddrinfo_emulation(ai);
-#else
- ::freeaddrinfo(ai);
-#endif
-}
-
-inline asio::error_code getnameinfo(const socket_addr_type* addr,
- std::size_t addrlen, char* host, std::size_t hostlen,
- char* serv, std::size_t servlen, int flags, asio::error_code& ec)
-{
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE)
- // Building for Windows XP, Windows Server 2003, or later.
- clear_error(ec);
- int error = ::getnameinfo(addr, static_cast<socklen_t>(addrlen),
- host, static_cast<DWORD>(hostlen),
- serv, static_cast<DWORD>(servlen), flags);
- return ec = translate_addrinfo_error(error);
-# else
- // Building for Windows 2000 or earlier.
- typedef int (WSAAPI *gni_t)(const socket_addr_type*,
- int, char*, DWORD, char*, DWORD, int);
- if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32"))
- {
- if (gni_t gni = (gni_t)::GetProcAddress(winsock_module, "getnameinfo"))
- {
- clear_error(ec);
- int error = gni(addr, static_cast<int>(addrlen),
- host, static_cast<DWORD>(hostlen),
- serv, static_cast<DWORD>(servlen), flags);
- return ec = translate_addrinfo_error(error);
- }
- }
- clear_error(ec);
- return getnameinfo_emulation(addr, addrlen,
- host, hostlen, serv, servlen, flags, ec);
-# endif
-#elif defined(__MACH__) && defined(__APPLE__)
- using namespace std; // For memcpy.
- sockaddr_storage_type tmp_addr;
- memcpy(&tmp_addr, addr, addrlen);
- tmp_addr.ss_len = addrlen;
- addr = reinterpret_cast<socket_addr_type*>(&tmp_addr);
- clear_error(ec);
- return getnameinfo_emulation(addr, addrlen,
- host, hostlen, serv, servlen, flags, ec);
-#else
- clear_error(ec);
- int error = ::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags);
- return ec = translate_addrinfo_error(error);
-#endif
-}
-
-inline u_long_type network_to_host_long(u_long_type value)
-{
- return ntohl(value);
-}
+ASIO_DECL asio::error_code sync_getnameinfo(
+ const socket_addr_type* addr, std::size_t addrlen,
+ char* host, std::size_t hostlen, char* serv,
+ std::size_t servlen, int sock_type, asio::error_code& ec);
-inline u_long_type host_to_network_long(u_long_type value)
-{
- return htonl(value);
-}
+ASIO_DECL asio::error_code background_getnameinfo(
+ const weak_cancel_token_type& cancel_token,
+ const socket_addr_type* addr, std::size_t addrlen,
+ char* host, std::size_t hostlen, char* serv,
+ std::size_t servlen, int sock_type, asio::error_code& ec);
-inline u_short_type network_to_host_short(u_short_type value)
-{
- return ntohs(value);
-}
+ASIO_DECL u_long_type network_to_host_long(u_long_type value);
-inline u_short_type host_to_network_short(u_short_type value)
-{
- return htons(value);
-}
+ASIO_DECL u_long_type host_to_network_long(u_long_type value);
+
+ASIO_DECL u_short_type network_to_host_short(u_short_type value);
+
+ASIO_DECL u_short_type host_to_network_short(u_short_type value);
} // namespace socket_ops
} // namespace detail
@@ -1909,4 +282,8 @@ inline u_short_type host_to_network_short(u_short_type value)
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/socket_ops.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
#endif // ASIO_DETAIL_SOCKET_OPS_HPP
diff --git a/ext/asio/asio/detail/socket_option.hpp b/ext/asio/asio/detail/socket_option.hpp
index ac070b7..c9d7896 100644
--- a/ext/asio/asio/detail/socket_option.hpp
+++ b/ext/asio/asio/detail/socket_option.hpp
@@ -1,8 +1,8 @@
//
-// socket_option.hpp
-// ~~~~~~~~~~~~~~~~~
+// detail/socket_option.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
#include <stdexcept>
#include <boost/config.hpp>
#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/detail/socket_types.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
namespace socket_option {
diff --git a/ext/asio/asio/detail/socket_select_interrupter.hpp b/ext/asio/asio/detail/socket_select_interrupter.hpp
index 62e1063..b05a4ff 100644
--- a/ext/asio/asio/detail/socket_select_interrupter.hpp
+++ b/ext/asio/asio/detail/socket_select_interrupter.hpp
@@ -1,8 +1,8 @@
//
-// socket_select_interrupter.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/socket_select_interrupter.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,19 +15,16 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
-#include <cstdlib>
-#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
+#if defined(BOOST_WINDOWS) \
+ || defined(__CYGWIN__) \
+ || defined(__SYMBIAN32__)
-#include "asio/error.hpp"
-#include "asio/system_error.hpp"
-#include "asio/detail/socket_holder.hpp"
-#include "asio/detail/socket_ops.hpp"
#include "asio/detail/socket_types.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -35,135 +32,16 @@ class socket_select_interrupter
{
public:
// Constructor.
- socket_select_interrupter()
- {
- asio::error_code ec;
- socket_holder acceptor(socket_ops::socket(
- AF_INET, SOCK_STREAM, IPPROTO_TCP, ec));
- if (acceptor.get() == invalid_socket)
- {
- asio::system_error e(ec, "socket_select_interrupter");
- boost::throw_exception(e);
- }
-
- int opt = 1;
- socket_ops::setsockopt(acceptor.get(),
- SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt), ec);
-
- using namespace std; // For memset.
- sockaddr_in4_type addr;
- std::size_t addr_len = sizeof(addr);
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- addr.sin_port = 0;
- if (socket_ops::bind(acceptor.get(), (const socket_addr_type*)&addr,
- addr_len, ec) == socket_error_retval)
- {
- asio::system_error e(ec, "socket_select_interrupter");
- boost::throw_exception(e);
- }
-
- if (socket_ops::getsockname(acceptor.get(), (socket_addr_type*)&addr,
- &addr_len, ec) == socket_error_retval)
- {
- asio::system_error e(ec, "socket_select_interrupter");
- boost::throw_exception(e);
- }
-
- // Some broken firewalls on Windows will intermittently cause getsockname to
- // return 0.0.0.0 when the socket is actually bound to 127.0.0.1. We
- // explicitly specify the target address here to work around this problem.
- addr.sin_addr.s_addr = inet_addr("127.0.0.1");
-
- if (socket_ops::listen(acceptor.get(),
- SOMAXCONN, ec) == socket_error_retval)
- {
- asio::system_error e(ec, "socket_select_interrupter");
- boost::throw_exception(e);
- }
-
- socket_holder client(socket_ops::socket(
- AF_INET, SOCK_STREAM, IPPROTO_TCP, ec));
- if (client.get() == invalid_socket)
- {
- asio::system_error e(ec, "socket_select_interrupter");
- boost::throw_exception(e);
- }
-
- if (socket_ops::connect(client.get(), (const socket_addr_type*)&addr,
- addr_len, ec) == socket_error_retval)
- {
- asio::system_error e(ec, "socket_select_interrupter");
- boost::throw_exception(e);
- }
-
- socket_holder server(socket_ops::accept(acceptor.get(), 0, 0, ec));
- if (server.get() == invalid_socket)
- {
- asio::system_error e(ec, "socket_select_interrupter");
- boost::throw_exception(e);
- }
-
- ioctl_arg_type non_blocking = 1;
- if (socket_ops::ioctl(client.get(), FIONBIO, &non_blocking, ec))
- {
- asio::system_error e(ec, "socket_select_interrupter");
- boost::throw_exception(e);
- }
-
- opt = 1;
- socket_ops::setsockopt(client.get(),
- IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec);
-
- non_blocking = 1;
- if (socket_ops::ioctl(server.get(), FIONBIO, &non_blocking, ec))
- {
- asio::system_error e(ec, "socket_select_interrupter");
- boost::throw_exception(e);
- }
-
- opt = 1;
- socket_ops::setsockopt(server.get(),
- IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec);
-
- read_descriptor_ = server.release();
- write_descriptor_ = client.release();
- }
+ ASIO_DECL socket_select_interrupter();
// Destructor.
- ~socket_select_interrupter()
- {
- asio::error_code ec;
- if (read_descriptor_ != invalid_socket)
- socket_ops::close(read_descriptor_, ec);
- if (write_descriptor_ != invalid_socket)
- socket_ops::close(write_descriptor_, ec);
- }
+ ASIO_DECL ~socket_select_interrupter();
// Interrupt the select call.
- void interrupt()
- {
- char byte = 0;
- socket_ops::buf b;
- socket_ops::init_buf(b, &byte, 1);
- asio::error_code ec;
- socket_ops::send(write_descriptor_, &b, 1, 0, ec);
- }
+ ASIO_DECL void interrupt();
// Reset the select interrupt. Returns true if the call was interrupted.
- bool reset()
- {
- char data[1024];
- socket_ops::buf b;
- socket_ops::init_buf(b, data, sizeof(data));
- asio::error_code ec;
- int bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec);
- bool was_interrupted = (bytes_read > 0);
- while (bytes_read == sizeof(data))
- bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec);
- return was_interrupted;
- }
+ ASIO_DECL bool reset();
// Get the read descriptor to be passed to select.
socket_type read_descriptor() const
@@ -189,4 +67,12 @@ private:
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/socket_select_interrupter.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(BOOST_WINDOWS)
+ // || defined(__CYGWIN__)
+ // || defined(__SYMBIAN32__)
+
#endif // ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP
diff --git a/ext/asio/asio/detail/socket_types.hpp b/ext/asio/asio/detail/socket_types.hpp
index 34b3d3e..4e29e51 100644
--- a/ext/asio/asio/detail/socket_types.hpp
+++ b/ext/asio/asio/detail/socket_types.hpp
@@ -1,8 +1,8 @@
//
-// socket_types.hpp
-// ~~~~~~~~~~~~~~~~
+// detail/socket_types.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,69 +15,19 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
# if defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_)
# error WinSock.h has already been included
# endif // defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_)
-# if !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS)
-# if defined(_MSC_VER) || defined(__BORLANDC__)
-# pragma message( \
- "Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. For example:\n"\
- "- add -D_WIN32_WINNT=0x0501 to the compiler command line; or\n"\
- "- add _WIN32_WINNT=0x0501 to your project's Preprocessor Definitions.\n"\
- "Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target).")
-# else // defined(_MSC_VER) || defined(__BORLANDC__)
-# warning Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately.
-# warning For example, add -D_WIN32_WINNT=0x0501 to the compiler command line.
-# warning Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target).
-# endif // defined(_MSC_VER) || defined(__BORLANDC__)
-# define _WIN32_WINNT 0x0501
-# endif // !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS)
-# if defined(_MSC_VER)
-# if defined(_WIN32) && !defined(WIN32)
-# if !defined(_WINSOCK2API_)
-# define WIN32 // Needed for correct types in winsock2.h
-# else // !defined(_WINSOCK2API_)
-# error Please define the macro WIN32 in your compiler options
-# endif // !defined(_WINSOCK2API_)
-# endif // defined(_WIN32) && !defined(WIN32)
-# endif // defined(_MSC_VER)
# if defined(__BORLANDC__)
# include <stdlib.h> // Needed for __errno
-# if defined(__WIN32__) && !defined(WIN32)
-# if !defined(_WINSOCK2API_)
-# define WIN32 // Needed for correct types in winsock2.h
-# else // !defined(_WINSOCK2API_)
-# error Please define the macro WIN32 in your compiler options
-# endif // !defined(_WINSOCK2API_)
-# endif // defined(__WIN32__) && !defined(WIN32)
# if !defined(_WSPIAPI_H_)
# define _WSPIAPI_H_
# define ASIO_WSPIAPI_H_DEFINED
# endif // !defined(_WSPIAPI_H_)
# endif // defined(__BORLANDC__)
-# if !defined(ASIO_NO_WIN32_LEAN_AND_MEAN)
-# if !defined(WIN32_LEAN_AND_MEAN)
-# define WIN32_LEAN_AND_MEAN
-# endif // !defined(WIN32_LEAN_AND_MEAN)
-# endif // !defined(ASIO_NO_WIN32_LEAN_AND_MEAN)
-# if !defined(ASIO_NO_NOMINMAX)
-# if !defined(NOMINMAX)
-# define NOMINMAX 1
-# endif // !defined(NOMINMAX)
-# endif // !defined(ASIO_NO_NOMINMAX)
-# if defined(__CYGWIN__)
-# if !defined(__USE_W32_SOCKETS)
-# error You must add -D__USE_W32_SOCKETS to your compiler options.
-# endif // !defined(__USE_W32_SOCKETS)
-# endif // defined(__CYGWIN__)
# include <winsock2.h>
# include <ws2tcpip.h>
# include <mswsock.h>
@@ -96,18 +46,25 @@
# include "asio/detail/old_win_sdk_compat.hpp"
#else
# include <sys/ioctl.h>
-# include <sys/poll.h>
+# if !defined(__SYMBIAN32__)
+# include <sys/poll.h>
+# endif
# include <sys/types.h>
-# if defined(__hpux) && !defined(__HP_aCC)
+# include <sys/stat.h>
+# include <fcntl.h>
+# if defined(__hpux)
# include <sys/time.h>
-# else
+# endif
+# if !defined(__hpux) || defined(__SELECT)
# include <sys/select.h>
# endif
# include <sys/socket.h>
# include <sys/uio.h>
# include <sys/un.h>
# include <netinet/in.h>
-# include <netinet/tcp.h>
+# if !defined(__SYMBIAN32__)
+# include <netinet/tcp.h>
+# endif
# include <arpa/inet.h>
# include <netdb.h>
# include <net/if.h>
@@ -117,7 +74,8 @@
# include <sys/sockio.h>
# endif
#endif
-#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
diff --git a/ext/asio/asio/detail/solaris_fenced_block.hpp b/ext/asio/asio/detail/solaris_fenced_block.hpp
index d337f3b..411c824 100644
--- a/ext/asio/asio/detail/solaris_fenced_block.hpp
+++ b/ext/asio/asio/detail/solaris_fenced_block.hpp
@@ -1,8 +1,8 @@
//
-// solaris_fenced_block.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/solaris_fenced_block.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if defined(__sun)
-#include "asio/detail/push_options.hpp"
#include <atomic.h>
-#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
@@ -50,8 +46,8 @@ public:
} // namespace detail
} // namespace asio
-#endif // defined(__sun)
-
#include "asio/detail/pop_options.hpp"
+#endif // defined(__sun)
+
#endif // ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP
diff --git a/ext/asio/asio/detail/strand_service.hpp b/ext/asio/asio/detail/strand_service.hpp
index ea50f41..4885b93 100644
--- a/ext/asio/asio/detail/strand_service.hpp
+++ b/ext/asio/asio/detail/strand_service.hpp
@@ -1,8 +1,8 @@
//
-// strand_service.hpp
-// ~~~~~~~~~~~~~~~~~~
+// detail/strand_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,23 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/functional/hash.hpp>
+#include "asio/detail/config.hpp"
#include <boost/scoped_ptr.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/io_service.hpp"
-#include "asio/detail/call_stack.hpp"
-#include "asio/detail/completion_handler.hpp"
-#include "asio/detail/fenced_block.hpp"
-#include "asio/detail/handler_alloc_helpers.hpp"
-#include "asio/detail/handler_invoke_helpers.hpp"
#include "asio/detail/mutex.hpp"
#include "asio/detail/op_queue.hpp"
#include "asio/detail/operation.hpp"
-#include "asio/detail/service_base.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
@@ -41,7 +32,10 @@ class strand_service
: public asio::detail::service_base<strand_service>
{
private:
+ // Helper class to re-post the strand on exit.
struct on_do_complete_exit;
+
+ // Helper class to re-post the strand on exit.
struct on_dispatch_exit;
public:
@@ -51,11 +45,7 @@ public:
: public operation
{
public:
- strand_impl()
- : operation(&strand_service::do_complete),
- count_(0)
- {
- }
+ strand_impl();
private:
// Only this service will have access to the internal values.
@@ -76,180 +66,29 @@ public:
typedef strand_impl* implementation_type;
// Construct a new strand service for the specified io_service.
- explicit strand_service(asio::io_service& io_service)
- : asio::detail::service_base<strand_service>(io_service),
- io_service_(asio::use_service<io_service_impl>(io_service)),
- mutex_(),
- salt_(0)
- {
- }
+ ASIO_DECL explicit strand_service(asio::io_service& io_service);
// Destroy all user-defined handler objects owned by the service.
- void shutdown_service()
- {
- op_queue<operation> ops;
-
- asio::detail::mutex::scoped_lock lock(mutex_);
-
- for (std::size_t i = 0; i < num_implementations; ++i)
- if (strand_impl* impl = implementations_[i].get())
- ops.push(impl->queue_);
- }
+ ASIO_DECL void shutdown_service();
// Construct a new strand implementation.
- void construct(implementation_type& impl)
- {
- std::size_t index = boost::hash_value(&impl);
- boost::hash_combine(index, salt_++);
- index = index % num_implementations;
-
- asio::detail::mutex::scoped_lock lock(mutex_);
-
- if (!implementations_[index])
- implementations_[index].reset(new strand_impl);
- impl = implementations_[index].get();
- }
+ ASIO_DECL void construct(implementation_type& impl);
// Destroy a strand implementation.
- void destroy(implementation_type& impl)
- {
- impl = 0;
- }
+ void destroy(implementation_type& impl);
// Request the io_service to invoke the given handler.
template <typename Handler>
- void dispatch(implementation_type& impl, Handler handler)
- {
- // If we are already in the strand then the handler can run immediately.
- if (call_stack<strand_impl>::contains(impl))
- {
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- return;
- }
-
- // Allocate and construct an object to wrap the handler.
- typedef completion_handler<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, handler);
-
- // If we are running inside the io_service, and no other handler is queued
- // or running, then the handler can run immediately.
- bool can_dispatch = call_stack<io_service_impl>::contains(&io_service_);
- impl->mutex_.lock();
- bool first = (++impl->count_ == 1);
- if (can_dispatch && first)
- {
- // Immediate invocation is allowed.
- impl->mutex_.unlock();
-
- // Memory must be releaesed before any upcall is made.
- ptr.reset();
-
- // Indicate that this strand is executing on the current thread.
- call_stack<strand_impl>::context ctx(impl);
-
- // Ensure the next handler, if any, is scheduled on block exit.
- on_dispatch_exit on_exit = { &io_service_, impl };
- (void)on_exit;
-
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- return;
- }
-
- // Immediate invocation is not allowed, so enqueue for later.
- impl->queue_.push(ptr.get());
- impl->mutex_.unlock();
- ptr.release();
-
- // The first handler to be enqueued is responsible for scheduling the
- // strand.
- if (first)
- io_service_.post_immediate_completion(impl);
- }
+ void dispatch(implementation_type& impl, Handler handler);
// Request the io_service to invoke the given handler and return immediately.
template <typename Handler>
- void post(implementation_type& impl, Handler handler)
- {
- // Allocate and construct an object to wrap the handler.
- typedef completion_handler<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, handler);
-
- // Add the handler to the queue.
- impl->mutex_.lock();
- bool first = (++impl->count_ == 1);
- impl->queue_.push(ptr.get());
- impl->mutex_.unlock();
- ptr.release();
-
- // The first handler to be enqueue is responsible for scheduling the strand.
- if (first)
- io_service_.post_immediate_completion(impl);
- }
+ void post(implementation_type& impl, Handler handler);
private:
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
- {
- if (owner)
- {
- strand_impl* impl = static_cast<strand_impl*>(base);
-
- // Get the next handler to be executed.
- impl->mutex_.lock();
- operation* o = impl->queue_.front();
- impl->queue_.pop();
- impl->mutex_.unlock();
-
- // Indicate that this strand is executing on the current thread.
- call_stack<strand_impl>::context ctx(impl);
-
- // Ensure the next handler, if any, is scheduled on block exit.
- on_do_complete_exit on_exit = { owner, impl };
- (void)on_exit;
-
- o->complete(*owner);
- }
- }
-
- // Helper class to re-post the strand on exit.
- struct on_do_complete_exit
- {
- io_service_impl* owner_;
- strand_impl* impl_;
-
- ~on_do_complete_exit()
- {
- impl_->mutex_.lock();
- bool more_handlers = (--impl_->count_ > 0);
- impl_->mutex_.unlock();
-
- if (more_handlers)
- owner_->post_immediate_completion(impl_);
- }
- };
-
- // Helper class to re-post the strand on exit.
- struct on_dispatch_exit
- {
- io_service_impl* io_service_;
- strand_impl* impl_;
-
- ~on_dispatch_exit()
- {
- impl_->mutex_.lock();
- bool more_handlers = (--impl_->count_ > 0);
- impl_->mutex_.unlock();
-
- if (more_handlers)
- io_service_->post_immediate_completion(impl_);
- }
- };
+ ASIO_DECL static void do_complete(io_service_impl* owner,
+ operation* base, asio::error_code ec,
+ std::size_t bytes_transferred);
// The io_service implementation used to post completions.
io_service_impl& io_service_;
@@ -273,4 +112,9 @@ private:
#include "asio/detail/pop_options.hpp"
+#include "asio/detail/impl/strand_service.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/strand_service.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
#endif // ASIO_DETAIL_STRAND_SERVICE_HPP
diff --git a/ext/asio/asio/detail/task_io_service.hpp b/ext/asio/asio/detail/task_io_service.hpp
index eb77c1d..2d69513 100644
--- a/ext/asio/asio/detail/task_io_service.hpp
+++ b/ext/asio/asio/detail/task_io_service.hpp
@@ -1,8 +1,8 @@
//
-// task_io_service.hpp
-// ~~~~~~~~~~~~~~~~~~~
+// detail/task_io_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,180 +15,59 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+
+#if !defined(ASIO_HAS_IOCP)
+#include <boost/detail/atomic_count.hpp>
#include "asio/error_code.hpp"
#include "asio/io_service.hpp"
-#include "asio/detail/call_stack.hpp"
-#include "asio/detail/completion_handler.hpp"
-#include "asio/detail/event.hpp"
-#include "asio/detail/fenced_block.hpp"
-#include "asio/detail/handler_alloc_helpers.hpp"
-#include "asio/detail/handler_invoke_helpers.hpp"
#include "asio/detail/mutex.hpp"
#include "asio/detail/op_queue.hpp"
-#include "asio/detail/service_base.hpp"
+#include "asio/detail/reactor_fwd.hpp"
#include "asio/detail/task_io_service_fwd.hpp"
#include "asio/detail/task_io_service_operation.hpp"
#include "asio/detail/push_options.hpp"
-#include <boost/detail/atomic_count.hpp>
-#include "asio/detail/pop_options.hpp"
namespace asio {
namespace detail {
-template <typename Task>
class task_io_service
- : public asio::detail::service_base<task_io_service<Task> >
+ : public asio::detail::service_base<task_io_service>
{
public:
- typedef task_io_service_operation<Task> operation;
+ typedef task_io_service_operation operation;
// Constructor.
- task_io_service(asio::io_service& io_service)
- : asio::detail::service_base<task_io_service<Task> >(io_service),
- mutex_(),
- task_(0),
- task_interrupted_(true),
- outstanding_work_(0),
- stopped_(false),
- shutdown_(false),
- first_idle_thread_(0)
- {
- }
+ ASIO_DECL task_io_service(asio::io_service& io_service);
- void init(size_t /*concurrency_hint*/)
- {
- }
+ // How many concurrent threads are likely to run the io_service.
+ ASIO_DECL void init(std::size_t concurrency_hint);
// Destroy all user-defined handler objects owned by the service.
- void shutdown_service()
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- shutdown_ = true;
- lock.unlock();
-
- // Destroy handler objects.
- while (!op_queue_.empty())
- {
- operation* o = op_queue_.front();
- op_queue_.pop();
- if (o != &task_operation_)
- o->destroy();
- }
-
- // Reset to initial state.
- task_ = 0;
- }
+ ASIO_DECL void shutdown_service();
// Initialise the task, if required.
- void init_task()
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- if (!shutdown_ && !task_)
- {
- task_ = &use_service<Task>(this->get_io_service());
- op_queue_.push(&task_operation_);
- wake_one_thread_and_unlock(lock);
- }
- }
+ ASIO_DECL void init_task();
// Run the event loop until interrupted or no more work.
- size_t run(asio::error_code& ec)
- {
- ec = asio::error_code();
- if (outstanding_work_ == 0)
- {
- stop();
- return 0;
- }
-
- typename call_stack<task_io_service>::context ctx(this);
-
- idle_thread_info this_idle_thread;
- this_idle_thread.next = 0;
-
- asio::detail::mutex::scoped_lock lock(mutex_);
-
- size_t n = 0;
- for (; do_one(lock, &this_idle_thread); lock.lock())
- if (n != (std::numeric_limits<size_t>::max)())
- ++n;
- return n;
- }
+ ASIO_DECL std::size_t run(asio::error_code& ec);
// Run until interrupted or one operation is performed.
- size_t run_one(asio::error_code& ec)
- {
- ec = asio::error_code();
- if (outstanding_work_ == 0)
- {
- stop();
- return 0;
- }
-
- typename call_stack<task_io_service>::context ctx(this);
-
- idle_thread_info this_idle_thread;
- this_idle_thread.next = 0;
-
- asio::detail::mutex::scoped_lock lock(mutex_);
-
- return do_one(lock, &this_idle_thread);
- }
+ ASIO_DECL std::size_t run_one(asio::error_code& ec);
// Poll for operations without blocking.
- size_t poll(asio::error_code& ec)
- {
- if (outstanding_work_ == 0)
- {
- stop();
- ec = asio::error_code();
- return 0;
- }
-
- typename call_stack<task_io_service>::context ctx(this);
-
- asio::detail::mutex::scoped_lock lock(mutex_);
-
- size_t n = 0;
- for (; do_one(lock, 0); lock.lock())
- if (n != (std::numeric_limits<size_t>::max)())
- ++n;
- return n;
- }
+ ASIO_DECL std::size_t poll(asio::error_code& ec);
// Poll for one operation without blocking.
- size_t poll_one(asio::error_code& ec)
- {
- ec = asio::error_code();
- if (outstanding_work_ == 0)
- {
- stop();
- return 0;
- }
-
- typename call_stack<task_io_service>::context ctx(this);
-
- asio::detail::mutex::scoped_lock lock(mutex_);
-
- return do_one(lock, 0);
- }
+ ASIO_DECL std::size_t poll_one(asio::error_code& ec);
// Interrupt the event processing loop.
- void stop()
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- stop_all_threads(lock);
- }
+ ASIO_DECL void stop();
// Reset in preparation for a subsequent run invocation.
- void reset()
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- stopped_ = false;
- }
+ ASIO_DECL void reset();
// Notify that some work has started.
void work_started()
@@ -205,228 +84,60 @@ public:
// Request invocation of the given handler.
template <typename Handler>
- void dispatch(Handler handler)
- {
- if (call_stack<task_io_service>::contains(this))
- {
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- else
- post(handler);
- }
+ void dispatch(Handler handler);
// Request invocation of the given handler and return immediately.
template <typename Handler>
- void post(Handler handler)
- {
- // Allocate and construct an operation to wrap the handler.
- typedef completion_handler<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, handler);
-
- post_immediate_completion(ptr.get());
- ptr.release();
- }
+ void post(Handler handler);
// Request invocation of the given operation and return immediately. Assumes
// that work_started() has not yet been called for the operation.
- void post_immediate_completion(operation* op)
- {
- work_started();
- post_deferred_completion(op);
- }
+ ASIO_DECL void post_immediate_completion(operation* op);
// Request invocation of the given operation and return immediately. Assumes
// that work_started() was previously called for the operation.
- void post_deferred_completion(operation* op)
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- op_queue_.push(op);
- wake_one_thread_and_unlock(lock);
- }
+ ASIO_DECL void post_deferred_completion(operation* op);
// Request invocation of the given operations and return immediately. Assumes
// that work_started() was previously called for each operation.
- void post_deferred_completions(op_queue<operation>& ops)
- {
- if (!ops.empty())
- {
- asio::detail::mutex::scoped_lock lock(mutex_);
- op_queue_.push(ops);
- wake_one_thread_and_unlock(lock);
- }
- }
+ ASIO_DECL void post_deferred_completions(op_queue<operation>& ops);
private:
+ // Structure containing information about an idle thread.
struct idle_thread_info;
- size_t do_one(asio::detail::mutex::scoped_lock& lock,
- idle_thread_info* this_idle_thread)
- {
- bool polling = !this_idle_thread;
- bool task_has_run = false;
- while (!stopped_)
- {
- if (!op_queue_.empty())
- {
- // Prepare to execute first handler from queue.
- operation* o = op_queue_.front();
- op_queue_.pop();
- bool more_handlers = (!op_queue_.empty());
-
- if (o == &task_operation_)
- {
- task_interrupted_ = more_handlers || polling;
-
- // If the task has already run and we're polling then we're done.
- if (task_has_run && polling)
- {
- task_interrupted_ = true;
- op_queue_.push(&task_operation_);
- return 0;
- }
- task_has_run = true;
-
- if (!more_handlers || !wake_one_idle_thread_and_unlock(lock))
- lock.unlock();
-
- op_queue<operation> completed_ops;
- task_cleanup c = { this, &lock, &completed_ops };
- (void)c;
-
- // Run the task. May throw an exception. Only block if the operation
- // queue is empty and we're not polling, otherwise we want to return
- // as soon as possible.
- task_->run(!more_handlers && !polling, completed_ops);
- }
- else
- {
- if (more_handlers)
- wake_one_thread_and_unlock(lock);
- else
- lock.unlock();
-
- // Ensure the count of outstanding work is decremented on block exit.
- work_finished_on_block_exit on_exit = { this };
- (void)on_exit;
-
- // Complete the operation. May throw an exception.
- o->complete(*this); // deletes the operation object
-
- return 1;
- }
- }
- else if (this_idle_thread)
- {
- // Nothing to run right now, so just wait for work to do.
- this_idle_thread->next = first_idle_thread_;
- first_idle_thread_ = this_idle_thread;
- this_idle_thread->wakeup_event.clear(lock);
- this_idle_thread->wakeup_event.wait(lock);
- }
- else
- {
- return 0;
- }
- }
-
- return 0;
- }
+ // Run at most one operation. Blocks only if this_idle_thread is non-null.
+ ASIO_DECL std::size_t do_one(mutex::scoped_lock& lock,
+ idle_thread_info* this_idle_thread);
// Stop the task and all idle threads.
- void stop_all_threads(
- asio::detail::mutex::scoped_lock& lock)
- {
- stopped_ = true;
-
- while (first_idle_thread_)
- {
- idle_thread_info* idle_thread = first_idle_thread_;
- first_idle_thread_ = idle_thread->next;
- idle_thread->next = 0;
- idle_thread->wakeup_event.signal(lock);
- }
-
- if (!task_interrupted_ && task_)
- {
- task_interrupted_ = true;
- task_->interrupt();
- }
- }
+ ASIO_DECL void stop_all_threads(mutex::scoped_lock& lock);
// Wakes a single idle thread and unlocks the mutex. Returns true if an idle
// thread was found. If there is no idle thread, returns false and leaves the
// mutex locked.
- bool wake_one_idle_thread_and_unlock(
- asio::detail::mutex::scoped_lock& lock)
- {
- if (first_idle_thread_)
- {
- idle_thread_info* idle_thread = first_idle_thread_;
- first_idle_thread_ = idle_thread->next;
- idle_thread->next = 0;
- idle_thread->wakeup_event.signal_and_unlock(lock);
- return true;
- }
- return false;
- }
+ ASIO_DECL bool wake_one_idle_thread_and_unlock(
+ mutex::scoped_lock& lock);
// Wake a single idle thread, or the task, and always unlock the mutex.
- void wake_one_thread_and_unlock(
- asio::detail::mutex::scoped_lock& lock)
- {
- if (!wake_one_idle_thread_and_unlock(lock))
- {
- if (!task_interrupted_ && task_)
- {
- task_interrupted_ = true;
- task_->interrupt();
- }
- lock.unlock();
- }
- }
+ ASIO_DECL void wake_one_thread_and_unlock(
+ mutex::scoped_lock& lock);
// Helper class to perform task-related operations on block exit.
struct task_cleanup;
friend struct task_cleanup;
- struct task_cleanup
- {
- ~task_cleanup()
- {
- // Enqueue the completed operations and reinsert the task at the end of
- // the operation queue.
- lock_->lock();
- task_io_service_->task_interrupted_ = true;
- task_io_service_->op_queue_.push(*ops_);
- task_io_service_->op_queue_.push(&task_io_service_->task_operation_);
- }
-
- task_io_service* task_io_service_;
- asio::detail::mutex::scoped_lock* lock_;
- op_queue<operation>* ops_;
- };
// Helper class to call work_finished() on block exit.
- struct work_finished_on_block_exit
- {
- ~work_finished_on_block_exit()
- {
- task_io_service_->work_finished();
- }
-
- task_io_service* task_io_service_;
- };
+ struct work_finished_on_block_exit;
// Mutex to protect access to internal data.
- asio::detail::mutex mutex_;
+ mutex mutex_;
// The task to be run by this service.
- Task* task_;
+ reactor* task_;
// Operation object to represent the position of the task in the queue.
- struct task_operation : public operation
+ struct task_operation : operation
{
task_operation() : operation(0) {}
} task_operation_;
@@ -446,13 +157,6 @@ private:
// Flag to indicate that the dispatcher has been shut down.
bool shutdown_;
- // Structure containing information about an idle thread.
- struct idle_thread_info
- {
- event wakeup_event;
- idle_thread_info* next;
- };
-
// The threads that are currently idle.
idle_thread_info* first_idle_thread_;
};
@@ -462,4 +166,11 @@ private:
#include "asio/detail/pop_options.hpp"
+#include "asio/detail/impl/task_io_service.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/task_io_service.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // !defined(ASIO_HAS_IOCP)
+
#endif // ASIO_DETAIL_TASK_IO_SERVICE_HPP
diff --git a/ext/asio/asio/detail/task_io_service_fwd.hpp b/ext/asio/asio/detail/task_io_service_fwd.hpp
index 5b18d1d..b66cb07 100644
--- a/ext/asio/asio/detail/task_io_service_fwd.hpp
+++ b/ext/asio/asio/detail/task_io_service_fwd.hpp
@@ -1,8 +1,8 @@
//
-// task_io_service_fwd.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~
+// detail/task_io_service_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
namespace asio {
namespace detail {
-template <typename Task>
class task_io_service;
} // namespace detail
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP
diff --git a/ext/asio/asio/detail/task_io_service_operation.hpp b/ext/asio/asio/detail/task_io_service_operation.hpp
index 6776f69..dd4384a 100644
--- a/ext/asio/asio/detail/task_io_service_operation.hpp
+++ b/ext/asio/asio/detail/task_io_service_operation.hpp
@@ -1,8 +1,8 @@
//
-// task_io_service_operation.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/task_io_service_operation.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,22 +15,21 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
#include "asio/error_code.hpp"
#include "asio/detail/op_queue.hpp"
#include "asio/detail/task_io_service_fwd.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
// Base class for all operations. A function pointer is used instead of virtual
// functions to avoid the associated overhead.
-template <typename Task>
class task_io_service_operation
{
public:
- void complete(task_io_service<Task>& owner)
+ void complete(task_io_service& owner)
{
func_(&owner, this, asio::error_code(), 0);
}
@@ -41,8 +40,9 @@ public:
}
protected:
- typedef void (*func_type)(task_io_service<Task>*,
- task_io_service_operation*, asio::error_code, std::size_t);
+ typedef void (*func_type)(task_io_service*,
+ task_io_service_operation*,
+ asio::error_code, std::size_t);
task_io_service_operation(func_type func)
: next_(0),
diff --git a/ext/asio/asio/detail/thread.hpp b/ext/asio/asio/detail/thread.hpp
index 3c9280b..7c4103b 100644
--- a/ext/asio/asio/detail/thread.hpp
+++ b/ext/asio/asio/detail/thread.hpp
@@ -1,8 +1,8 @@
//
-// thread.hpp
-// ~~~~~~~~~~
+// detail/thread.hpp
+// ~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,11 +15,7 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
# include "asio/detail/null_thread.hpp"
@@ -53,6 +49,4 @@ typedef posix_thread thread;
} // namespace detail
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_THREAD_HPP
diff --git a/ext/asio/asio/detail/throw_error.hpp b/ext/asio/asio/detail/throw_error.hpp
index 51d6e48..d39aa92 100644
--- a/ext/asio/asio/detail/throw_error.hpp
+++ b/ext/asio/asio/detail/throw_error.hpp
@@ -1,8 +1,8 @@
//
-// throw_error.hpp
-// ~~~~~~~~~~~~~~~
+// detail/throw_error.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,25 +15,30 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+#include "asio/error_code.hpp"
#include "asio/detail/push_options.hpp"
-#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error_code.hpp"
-#include "asio/system_error.hpp"
namespace asio {
namespace detail {
+ASIO_DECL void do_throw_error(const asio::error_code& err);
+
+ASIO_DECL void do_throw_error(const asio::error_code& err,
+ const char* location);
+
inline void throw_error(const asio::error_code& err)
{
if (err)
- {
- asio::system_error e(err);
- boost::throw_exception(e);
- }
+ do_throw_error(err);
+}
+
+inline void throw_error(const asio::error_code& err,
+ const char* location)
+{
+ if (err)
+ do_throw_error(err, location);
}
} // namespace detail
@@ -41,4 +46,8 @@ inline void throw_error(const asio::error_code& err)
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/throw_error.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
#endif // ASIO_DETAIL_THROW_ERROR_HPP
diff --git a/ext/asio/asio/detail/timer_op.hpp b/ext/asio/asio/detail/timer_op.hpp
index bf3c3ae..62cd292 100644
--- a/ext/asio/asio/detail/timer_op.hpp
+++ b/ext/asio/asio/detail/timer_op.hpp
@@ -1,8 +1,8 @@
//
-// timer_op.hpp
-// ~~~~~~~~~~~~
+// detail/timer_op.hpp
+// ~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,10 +15,11 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/detail/operation.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
diff --git a/ext/asio/asio/detail/timer_queue.hpp b/ext/asio/asio/detail/timer_queue.hpp
index 2e4d2d5..514c434 100644
--- a/ext/asio/asio/detail/timer_queue.hpp
+++ b/ext/asio/asio/detail/timer_queue.hpp
@@ -1,8 +1,8 @@
//
-// timer_queue.hpp
-// ~~~~~~~~~~~~~~~
+// detail/timer_queue.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,21 +15,22 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <memory>
#include <vector>
#include <boost/config.hpp>
#include <boost/limits.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error.hpp"
-#include "asio/detail/hash_map.hpp"
#include "asio/detail/op_queue.hpp"
#include "asio/detail/timer_op.hpp"
#include "asio/detail/timer_queue_base.hpp"
+#include "asio/error.hpp"
+#include "asio/time_traits.hpp"
+
+#include "asio/detail/push_options.hpp"
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
@@ -45,6 +46,26 @@ public:
// The duration type.
typedef typename Time_Traits::duration_type duration_type;
+ // Per-timer data.
+ class per_timer_data
+ {
+ public:
+ per_timer_data() : next_(0), prev_(0) {}
+
+ private:
+ friend class timer_queue;
+
+ // The operations waiting on the timer.
+ op_queue<timer_op> op_queue_;
+
+ // The index of the timer in the heap.
+ std::size_t heap_index_;
+
+ // Pointers to adjacent timers in a linked list.
+ per_timer_data* next_;
+ per_timer_data* prev_;
+ };
+
// Constructor.
timer_queue()
: timers_(),
@@ -55,35 +76,45 @@ public:
// Add a new timer to the queue. Returns true if this is the timer that is
// earliest in the queue, in which case the reactor's event demultiplexing
// function call may need to be interrupted and restarted.
- bool enqueue_timer(const time_type& time, timer_op* op, void* token)
+ bool enqueue_timer(const time_type& time, per_timer_data& timer, timer_op* op)
{
- // Ensure that there is space for the timer in the heap. We reserve here so
- // that the push_back below will not throw due to a reallocation failure.
- heap_.reserve(heap_.size() + 1);
-
- // Insert the new timer into the hash.
- typedef typename hash_map<void*, timer>::iterator iterator;
- typedef typename hash_map<void*, timer>::value_type value_type;
- std::pair<iterator, bool> result =
- timers_.insert(value_type(token, timer()));
- result.first->second.op_queue_.push(op);
- if (result.second)
+ // Enqueue the timer object.
+ if (timer.prev_ == 0 && &timer != timers_)
{
- // Put the new timer at the correct position in the heap.
- result.first->second.time_ = time;
- result.first->second.heap_index_ = heap_.size();
- result.first->second.token_ = token;
- heap_.push_back(&result.first->second);
- up_heap(heap_.size() - 1);
+ if (this->is_positive_infinity(time))
+ {
+ // No heap entry is required for timers that never expire.
+ timer.heap_index_ = (std::numeric_limits<std::size_t>::max)();
+ }
+ else
+ {
+ // Put the new timer at the correct position in the heap. This is done
+ // first since push_back() can throw due to allocation failure.
+ timer.heap_index_ = heap_.size();
+ heap_entry entry = { time, &timer };
+ heap_.push_back(entry);
+ up_heap(heap_.size() - 1);
+ }
+
+ // Insert the new timer into the linked list of active timers.
+ timer.next_ = timers_;
+ timer.prev_ = 0;
+ if (timers_)
+ timers_->prev_ = &timer;
+ timers_ = &timer;
}
- return (heap_[0] == &result.first->second);
+ // Enqueue the individual timer operation.
+ timer.op_queue_.push(op);
+
+ // Interrupt reactor only if newly added timer is first to expire.
+ return timer.heap_index_ == 0 && timer.op_queue_.front() == op;
}
// Whether there are no timers in the queue.
virtual bool empty() const
{
- return heap_.empty();
+ return timers_ == 0;
}
// Get the time for the timer that is earliest in the queue.
@@ -93,12 +124,14 @@ public:
return max_duration;
boost::posix_time::time_duration duration = Time_Traits::to_posix_duration(
- Time_Traits::subtract(heap_[0]->time_, Time_Traits::now()));
+ Time_Traits::subtract(heap_[0].time_, Time_Traits::now()));
if (duration > boost::posix_time::milliseconds(max_duration))
duration = boost::posix_time::milliseconds(max_duration);
- else if (duration < boost::posix_time::milliseconds(0))
+ else if (duration <= boost::posix_time::milliseconds(0))
duration = boost::posix_time::milliseconds(0);
+ else if (duration < boost::posix_time::milliseconds(1))
+ duration = boost::posix_time::milliseconds(1);
return duration.total_milliseconds();
}
@@ -110,12 +143,14 @@ public:
return max_duration;
boost::posix_time::time_duration duration = Time_Traits::to_posix_duration(
- Time_Traits::subtract(heap_[0]->time_, Time_Traits::now()));
+ Time_Traits::subtract(heap_[0].time_, Time_Traits::now()));
if (duration > boost::posix_time::microseconds(max_duration))
duration = boost::posix_time::microseconds(max_duration);
- else if (duration < boost::posix_time::microseconds(0))
+ else if (duration <= boost::posix_time::microseconds(0))
duration = boost::posix_time::microseconds(0);
+ else if (duration < boost::posix_time::microseconds(1))
+ duration = boost::posix_time::microseconds(1);
return duration.total_microseconds();
}
@@ -124,77 +159,54 @@ public:
virtual void get_ready_timers(op_queue<operation>& ops)
{
const time_type now = Time_Traits::now();
- while (!heap_.empty() && !Time_Traits::less_than(now, heap_[0]->time_))
+ while (!heap_.empty() && !Time_Traits::less_than(now, heap_[0].time_))
{
- timer* t = heap_[0];
- ops.push(t->op_queue_);
- remove_timer(t);
+ per_timer_data* timer = heap_[0].timer_;
+ ops.push(timer->op_queue_);
+ remove_timer(*timer);
}
}
// Dequeue all timers.
virtual void get_all_timers(op_queue<operation>& ops)
{
- typename hash_map<void*, timer>::iterator i = timers_.begin();
- typename hash_map<void*, timer>::iterator end = timers_.end();
- while (i != end)
+ while (timers_)
{
- ops.push(i->second.op_queue_);
- typename hash_map<void*, timer>::iterator old_i = i++;
- timers_.erase(old_i);
+ per_timer_data* timer = timers_;
+ timers_ = timers_->next_;
+ ops.push(timer->op_queue_);
+ timer->next_ = 0;
+ timer->prev_ = 0;
}
heap_.clear();
- timers_.clear();
}
// Cancel and dequeue the timers with the given token.
- std::size_t cancel_timer(void* timer_token, op_queue<operation>& ops)
+ std::size_t cancel_timer(per_timer_data& timer, op_queue<operation>& ops)
{
std::size_t num_cancelled = 0;
- typedef typename hash_map<void*, timer>::iterator iterator;
- iterator it = timers_.find(timer_token);
- if (it != timers_.end())
+ if (timer.prev_ != 0 || &timer == timers_)
{
- while (timer_op* op = it->second.op_queue_.front())
+ while (timer_op* op = timer.op_queue_.front())
{
op->ec_ = asio::error::operation_aborted;
- it->second.op_queue_.pop();
+ timer.op_queue_.pop();
ops.push(op);
++num_cancelled;
}
- remove_timer(&it->second);
+ remove_timer(timer);
}
return num_cancelled;
}
private:
- // Structure representing a single outstanding timer.
- struct timer
- {
- timer() {}
- timer(const timer&) {}
- void operator=(const timer&) {}
-
- // The time when the timer should fire.
- time_type time_;
-
- // The operations waiting on the timer.
- op_queue<timer_op> op_queue_;
-
- // The index of the timer in the heap.
- size_t heap_index_;
-
- // The token associated with the timer.
- void* token_;
- };
-
// Move the item at the given index up the heap to its correct position.
- void up_heap(size_t index)
+ void up_heap(std::size_t index)
{
- size_t parent = (index - 1) / 2;
+ std::size_t parent = (index - 1) / 2;
while (index > 0
- && Time_Traits::less_than(heap_[index]->time_, heap_[parent]->time_))
+ && Time_Traits::less_than(heap_[index].time_, heap_[parent].time_))
{
swap_heap(index, parent);
index = parent;
@@ -203,16 +215,16 @@ private:
}
// Move the item at the given index down the heap to its correct position.
- void down_heap(size_t index)
+ void down_heap(std::size_t index)
{
- size_t child = index * 2 + 1;
+ std::size_t child = index * 2 + 1;
while (child < heap_.size())
{
- size_t min_child = (child + 1 == heap_.size()
+ std::size_t min_child = (child + 1 == heap_.size()
|| Time_Traits::less_than(
- heap_[child]->time_, heap_[child + 1]->time_))
+ heap_[child].time_, heap_[child + 1].time_))
? child : child + 1;
- if (Time_Traits::less_than(heap_[index]->time_, heap_[min_child]->time_))
+ if (Time_Traits::less_than(heap_[index].time_, heap_[min_child].time_))
break;
swap_heap(index, min_child);
index = min_child;
@@ -221,20 +233,20 @@ private:
}
// Swap two entries in the heap.
- void swap_heap(size_t index1, size_t index2)
+ void swap_heap(std::size_t index1, std::size_t index2)
{
- timer* tmp = heap_[index1];
+ heap_entry tmp = heap_[index1];
heap_[index1] = heap_[index2];
heap_[index2] = tmp;
- heap_[index1]->heap_index_ = index1;
- heap_[index2]->heap_index_ = index2;
+ heap_[index1].timer_->heap_index_ = index1;
+ heap_[index2].timer_->heap_index_ = index2;
}
// Remove a timer from the heap and list of timers.
- void remove_timer(timer* t)
+ void remove_timer(per_timer_data& timer)
{
// Remove the timer from the heap.
- size_t index = t->heap_index_;
+ std::size_t index = timer.heap_index_;
if (!heap_.empty() && index < heap_.size())
{
if (index == heap_.size() - 1)
@@ -245,29 +257,112 @@ private:
{
swap_heap(index, heap_.size() - 1);
heap_.pop_back();
- size_t parent = (index - 1) / 2;
+ std::size_t parent = (index - 1) / 2;
if (index > 0 && Time_Traits::less_than(
- heap_[index]->time_, heap_[parent]->time_))
+ heap_[index].time_, heap_[parent].time_))
up_heap(index);
else
down_heap(index);
}
}
- // Remove the timer from the hash.
- typedef typename hash_map<void*, timer>::iterator iterator;
- iterator it = timers_.find(t->token_);
- if (it != timers_.end())
- timers_.erase(it);
+ // Remove the timer from the linked list of active timers.
+ if (timers_ == &timer)
+ timers_ = timer.next_;
+ if (timer.prev_)
+ timer.prev_->next_ = timer.next_;
+ if (timer.next_)
+ timer.next_->prev_= timer.prev_;
+ timer.next_ = 0;
+ timer.prev_ = 0;
+ }
+
+ // Determine if the specified absolute time is positive infinity.
+ template <typename Time_Type>
+ static bool is_positive_infinity(const Time_Type&)
+ {
+ return false;
+ }
+
+ // Determine if the specified absolute time is positive infinity.
+ static bool is_positive_infinity(const boost::posix_time::ptime& time)
+ {
+ return time == boost::posix_time::pos_infin;
}
- // A hash of timer token to linked lists of timers.
- hash_map<void*, timer> timers_;
+ // The head of a linked list of all active timers.
+ per_timer_data* timers_;
+
+ struct heap_entry
+ {
+ // The time when the timer should fire.
+ time_type time_;
+
+ // The associated timer with enqueued operations.
+ per_timer_data* timer_;
+ };
// The heap of timers, with the earliest timer at the front.
- std::vector<timer*> heap_;
+ std::vector<heap_entry> heap_;
};
+#if !defined(ASIO_HEADER_ONLY)
+
+struct forwarding_posix_time_traits : time_traits<boost::posix_time::ptime> {};
+
+// Template specialisation for the commonly used instantation.
+template <>
+class timer_queue<time_traits<boost::posix_time::ptime> >
+ : public timer_queue_base
+{
+public:
+ // The time type.
+ typedef boost::posix_time::ptime time_type;
+
+ // The duration type.
+ typedef boost::posix_time::time_duration duration_type;
+
+ // Per-timer data.
+ typedef timer_queue<forwarding_posix_time_traits>::per_timer_data
+ per_timer_data;
+
+ // Constructor.
+ ASIO_DECL timer_queue();
+
+ // Destructor.
+ ASIO_DECL virtual ~timer_queue();
+
+ // Add a new timer to the queue. Returns true if this is the timer that is
+ // earliest in the queue, in which case the reactor's event demultiplexing
+ // function call may need to be interrupted and restarted.
+ ASIO_DECL bool enqueue_timer(const time_type& time,
+ per_timer_data& timer, timer_op* op);
+
+ // Whether there are no timers in the queue.
+ ASIO_DECL virtual bool empty() const;
+
+ // Get the time for the timer that is earliest in the queue.
+ ASIO_DECL virtual long wait_duration_msec(long max_duration) const;
+
+ // Get the time for the timer that is earliest in the queue.
+ ASIO_DECL virtual long wait_duration_usec(long max_duration) const;
+
+ // Dequeue all timers not later than the current time.
+ ASIO_DECL virtual void get_ready_timers(op_queue<operation>& ops);
+
+ // Dequeue all timers.
+ ASIO_DECL virtual void get_all_timers(op_queue<operation>& ops);
+
+ // Cancel and dequeue the timers with the given token.
+ ASIO_DECL std::size_t cancel_timer(
+ per_timer_data& timer, op_queue<operation>& ops);
+
+private:
+ timer_queue<forwarding_posix_time_traits> impl_;
+};
+
+#endif // !defined(ASIO_HEADER_ONLY)
+
} // namespace detail
} // namespace asio
diff --git a/ext/asio/asio/detail/timer_queue_base.hpp b/ext/asio/asio/detail/timer_queue_base.hpp
index f978667..0e8d4c7 100644
--- a/ext/asio/asio/detail/timer_queue_base.hpp
+++ b/ext/asio/asio/detail/timer_queue_base.hpp
@@ -1,8 +1,8 @@
//
-// timer_queue_base.hpp
-// ~~~~~~~~~~~~~~~~~~~~
+// detail/timer_queue_base.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,12 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/op_queue.hpp"
#include "asio/detail/operation.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
diff --git a/ext/asio/asio/detail/timer_queue_fwd.hpp b/ext/asio/asio/detail/timer_queue_fwd.hpp
index 5317244..0cca967 100644
--- a/ext/asio/asio/detail/timer_queue_fwd.hpp
+++ b/ext/asio/asio/detail/timer_queue_fwd.hpp
@@ -1,8 +1,8 @@
//
-// timer_queue_fwd.hpp
-// ~~~~~~~~~~~~~~~~~~~
+// detail/timer_queue_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,8 +15,6 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
namespace asio {
namespace detail {
@@ -26,6 +24,4 @@ class timer_queue;
} // namespace detail
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_TIMER_QUEUE_FWD_HPP
diff --git a/ext/asio/asio/detail/timer_queue_set.hpp b/ext/asio/asio/detail/timer_queue_set.hpp
index 6860867..9e8d428 100644
--- a/ext/asio/asio/detail/timer_queue_set.hpp
+++ b/ext/asio/asio/detail/timer_queue_set.hpp
@@ -1,8 +1,8 @@
//
-// timer_queue_set.hpp
-// ~~~~~~~~~~~~~~~~~~~
+// detail/timer_queue_set.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,10 +15,11 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/detail/timer_queue_base.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -26,82 +27,28 @@ class timer_queue_set
{
public:
// Constructor.
- timer_queue_set()
- : first_(0)
- {
- }
+ ASIO_DECL timer_queue_set();
// Add a timer queue to the set.
- void insert(timer_queue_base* q)
- {
- q->next_ = first_;
- first_ = q;
- }
+ ASIO_DECL void insert(timer_queue_base* q);
// Remove a timer queue from the set.
- void erase(timer_queue_base* q)
- {
- if (first_)
- {
- if (q == first_)
- {
- first_ = q->next_;
- q->next_ = 0;
- return;
- }
-
- for (timer_queue_base* p = first_; p->next_; p = p->next_)
- {
- if (p->next_ == q)
- {
- p->next_ = q->next_;
- q->next_ = 0;
- return;
- }
- }
- }
- }
+ ASIO_DECL void erase(timer_queue_base* q);
// Determine whether all queues are empty.
- bool all_empty() const
- {
- for (timer_queue_base* p = first_; p; p = p->next_)
- if (!p->empty())
- return false;
- return true;
- }
+ ASIO_DECL bool all_empty() const;
// Get the wait duration in milliseconds.
- long wait_duration_msec(long max_duration) const
- {
- long min_duration = max_duration;
- for (timer_queue_base* p = first_; p; p = p->next_)
- min_duration = p->wait_duration_msec(min_duration);
- return min_duration;
- }
+ ASIO_DECL long wait_duration_msec(long max_duration) const;
// Get the wait duration in microseconds.
- long wait_duration_usec(long max_duration) const
- {
- long min_duration = max_duration;
- for (timer_queue_base* p = first_; p; p = p->next_)
- min_duration = p->wait_duration_usec(min_duration);
- return min_duration;
- }
+ ASIO_DECL long wait_duration_usec(long max_duration) const;
// Dequeue all ready timers.
- void get_ready_timers(op_queue<operation>& ops)
- {
- for (timer_queue_base* p = first_; p; p = p->next_)
- p->get_ready_timers(ops);
- }
+ ASIO_DECL void get_ready_timers(op_queue<operation>& ops);
// Dequeue all timers.
- void get_all_timers(op_queue<operation>& ops)
- {
- for (timer_queue_base* p = first_; p; p = p->next_)
- p->get_all_timers(ops);
- }
+ ASIO_DECL void get_all_timers(op_queue<operation>& ops);
private:
timer_queue_base* first_;
@@ -112,4 +59,8 @@ private:
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/timer_queue_set.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
#endif // ASIO_DETAIL_TIMER_QUEUE_SET_HPP
diff --git a/ext/asio/asio/detail/timer_scheduler.hpp b/ext/asio/asio/detail/timer_scheduler.hpp
index 6822d3f..1222e63 100644
--- a/ext/asio/asio/detail/timer_scheduler.hpp
+++ b/ext/asio/asio/detail/timer_scheduler.hpp
@@ -1,8 +1,8 @@
//
-// timer_scheduler.hpp
-// ~~~~~~~~~~~~~~~~~~~
+// detail/timer_scheduler.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,8 +15,7 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/detail/timer_scheduler_fwd.hpp"
#if defined(ASIO_HAS_IOCP)
@@ -31,6 +30,4 @@
# include "asio/detail/select_reactor.hpp"
#endif
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_TIMER_SCHEDULER_HPP
diff --git a/ext/asio/asio/detail/timer_scheduler_fwd.hpp b/ext/asio/asio/detail/timer_scheduler_fwd.hpp
index d766a87..5fced50 100644
--- a/ext/asio/asio/detail/timer_scheduler_fwd.hpp
+++ b/ext/asio/asio/detail/timer_scheduler_fwd.hpp
@@ -1,8 +1,8 @@
//
-// timer_scheduler_fwd.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~
+// detail/timer_scheduler_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,13 +15,19 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/dev_poll_reactor_fwd.hpp"
-#include "asio/detail/epoll_reactor_fwd.hpp"
-#include "asio/detail/kqueue_reactor_fwd.hpp"
-#include "asio/detail/select_reactor_fwd.hpp"
-#include "asio/detail/win_iocp_io_service_fwd.hpp"
+#if defined(ASIO_HAS_IOCP)
+# include "asio/detail/win_iocp_io_service_fwd.hpp"
+#elif defined(ASIO_HAS_EPOLL)
+# include "asio/detail/epoll_reactor_fwd.hpp"
+#elif defined(ASIO_HAS_KQUEUE)
+# include "asio/detail/kqueue_reactor_fwd.hpp"
+#elif defined(ASIO_HAS_DEV_POLL)
+# include "asio/detail/dev_poll_reactor_fwd.hpp"
+#else
+# include "asio/detail/select_reactor_fwd.hpp"
+#endif
namespace asio {
namespace detail {
@@ -35,12 +41,10 @@ typedef kqueue_reactor timer_scheduler;
#elif defined(ASIO_HAS_DEV_POLL)
typedef dev_poll_reactor timer_scheduler;
#else
-typedef select_reactor<false> timer_scheduler;
+typedef select_reactor timer_scheduler;
#endif
} // namespace detail
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP
diff --git a/ext/asio/asio/detail/tss_ptr.hpp b/ext/asio/asio/detail/tss_ptr.hpp
index ac67d9f..0218f5f 100644
--- a/ext/asio/asio/detail/tss_ptr.hpp
+++ b/ext/asio/asio/detail/tss_ptr.hpp
@@ -1,8 +1,8 @@
//
-// tss_ptr.hpp
-// ~~~~~~~~~~~
+// detail/tss_ptr.hpp
+// ~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,11 +15,7 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS)
# include "asio/detail/null_tss_ptr.hpp"
@@ -31,6 +27,8 @@
# error Only Windows and POSIX are supported!
#endif
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
diff --git a/ext/asio/asio/detail/wait_handler.hpp b/ext/asio/asio/detail/wait_handler.hpp
new file mode 100644
index 0000000..0df16cb
--- /dev/null
+++ b/ext/asio/asio/detail/wait_handler.hpp
@@ -0,0 +1,76 @@
+//
+// detail/wait_handler.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WAIT_HANDLER_HPP
+#define ASIO_DETAIL_WAIT_HANDLER_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/timer_op.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Handler>
+class wait_handler : public timer_op
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(wait_handler);
+
+ wait_handler(Handler h)
+ : timer_op(&wait_handler::do_complete),
+ handler_(h)
+ {
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
+ {
+ // Take ownership of the handler object.
+ wait_handler* h(static_cast<wait_handler*>(base));
+ ptr p = { boost::addressof(h->handler_), h, h };
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder1<Handler, asio::error_code>
+ handler(h->handler_, h->ec_);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_DETAIL_WAIT_HANDLER_HPP
diff --git a/ext/asio/asio/detail/weak_ptr.hpp b/ext/asio/asio/detail/weak_ptr.hpp
new file mode 100644
index 0000000..35dca19
--- /dev/null
+++ b/ext/asio/asio/detail/weak_ptr.hpp
@@ -0,0 +1,39 @@
+//
+// detail/weak_ptr.hpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WEAK_PTR_HPP
+#define ASIO_DETAIL_WEAK_PTR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <boost/version.hpp>
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1600)
+# include <memory>
+#else
+# include <boost/weak_ptr.hpp>
+#endif
+
+namespace asio {
+namespace detail {
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1600)
+using std::weak_ptr;
+#else
+using boost::weak_ptr;
+#endif
+
+} // namespace detail
+} // namespace asio
+
+#endif // ASIO_DETAIL_WEAK_PTR_HPP
diff --git a/ext/asio/asio/detail/win_event.hpp b/ext/asio/asio/detail/win_event.hpp
index cabb2c3..71ad7e0 100644
--- a/ext/asio/asio/detail/win_event.hpp
+++ b/ext/asio/asio/detail/win_event.hpp
@@ -1,8 +1,8 @@
//
-// win_event.hpp
-// ~~~~~~~~~~~~~
+// detail/win_event.hpp
+// ~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,23 +15,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if defined(BOOST_WINDOWS)
-#include "asio/error.hpp"
-#include "asio/system_error.hpp"
+#include <boost/assert.hpp>
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/socket_types.hpp"
#include "asio/detail/push_options.hpp"
-#include <boost/assert.hpp>
-#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
namespace asio {
namespace detail {
@@ -41,19 +33,7 @@ class win_event
{
public:
// Constructor.
- win_event()
- : event_(::CreateEvent(0, true, false, 0))
- {
- if (!event_)
- {
- DWORD last_error = ::GetLastError();
- asio::system_error e(
- asio::error_code(last_error,
- asio::error::get_system_category()),
- "event");
- boost::throw_exception(e);
- }
- }
+ ASIO_DECL win_event();
// Destructor.
~win_event()
@@ -105,8 +85,12 @@ private:
} // namespace detail
} // namespace asio
-#endif // defined(BOOST_WINDOWS)
-
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/win_event.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(BOOST_WINDOWS)
+
#endif // ASIO_DETAIL_WIN_EVENT_HPP
diff --git a/ext/asio/asio/detail/win_fd_set_adapter.hpp b/ext/asio/asio/detail/win_fd_set_adapter.hpp
index 012a10f..0cb9d8c 100644
--- a/ext/asio/asio/detail/win_fd_set_adapter.hpp
+++ b/ext/asio/asio/detail/win_fd_set_adapter.hpp
@@ -1,8 +1,8 @@
//
-// win_fd_set_adapter.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~
+// detail/win_fd_set_adapter.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,11 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
#include "asio/detail/socket_types.hpp"
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
@@ -81,8 +83,8 @@ private:
} // namespace detail
} // namespace asio
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-
#include "asio/detail/pop_options.hpp"
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
#endif // ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP
diff --git a/ext/asio/asio/detail/win_fenced_block.hpp b/ext/asio/asio/detail/win_fenced_block.hpp
index 6338488..011f44b 100644
--- a/ext/asio/asio/detail/win_fenced_block.hpp
+++ b/ext/asio/asio/detail/win_fenced_block.hpp
@@ -1,8 +1,8 @@
//
-// win_fenced_block.hpp
-// ~~~~~~~~~~~~~~~~~~~~
+// detail/win_fenced_block.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if defined(BOOST_WINDOWS) && !defined(UNDER_CE)
#include "asio/detail/socket_types.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -35,7 +33,10 @@ public:
// Constructor.
win_fenced_block()
{
-#if defined(BOOST_MSVC) && (BOOST_MSVC < 1400)
+#if defined(__BORLANDC__)
+ LONG barrier = 0;
+ ::InterlockedExchange(&barrier, 1);
+#elif defined(BOOST_MSVC) && ((BOOST_MSVC < 1400) || !defined(MemoryBarrier))
# if defined(_M_IX86)
# pragma warning(push)
# pragma warning(disable:4793)
@@ -43,15 +44,18 @@ public:
__asm { xchg barrier, eax }
# pragma warning(pop)
# endif // defined(_M_IX86)
-#else // defined(BOOST_MSVC) && (BOOST_MSVC < 1400)
+#else
MemoryBarrier();
-#endif // defined(BOOST_MSVC) && (BOOST_MSVC < 1400)
+#endif
}
// Destructor.
~win_fenced_block()
{
-#if defined(BOOST_MSVC) && (BOOST_MSVC < 1400)
+#if defined(__BORLANDC__)
+ LONG barrier = 0;
+ ::InterlockedExchange(&barrier, 1);
+#elif defined(BOOST_MSVC) && ((BOOST_MSVC < 1400) || !defined(MemoryBarrier))
# if defined(_M_IX86)
# pragma warning(push)
# pragma warning(disable:4793)
@@ -59,17 +63,17 @@ public:
__asm { xchg barrier, eax }
# pragma warning(pop)
# endif // defined(_M_IX86)
-#else // defined(BOOST_MSVC) && (BOOST_MSVC < 1400)
+#else
MemoryBarrier();
-#endif // defined(BOOST_MSVC) && (BOOST_MSVC < 1400)
+#endif
}
};
} // namespace detail
} // namespace asio
-#endif // defined(BOOST_WINDOWS) && !defined(UNDER_CE)
-
#include "asio/detail/pop_options.hpp"
+#endif // defined(BOOST_WINDOWS) && !defined(UNDER_CE)
+
#endif // ASIO_DETAIL_WIN_FENCED_BLOCK_HPP
diff --git a/ext/asio/asio/detail/win_iocp_handle_read_op.hpp b/ext/asio/asio/detail/win_iocp_handle_read_op.hpp
new file mode 100644
index 0000000..fb358be
--- /dev/null
+++ b/ext/asio/asio/detail/win_iocp_handle_read_op.hpp
@@ -0,0 +1,101 @@
+//
+// detail/win_iocp_handle_read_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2008 Rep Invariant Systems, Inc. (info at repinvariant.com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_IOCP_HANDLE_READ_OP_HPP
+#define ASIO_DETAIL_WIN_IOCP_HANDLE_READ_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+
+#include "asio/error.hpp"
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/operation.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename MutableBufferSequence, typename Handler>
+class win_iocp_handle_read_op : public operation
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(win_iocp_handle_read_op);
+
+ win_iocp_handle_read_op(const MutableBufferSequence& buffers, Handler handler)
+ : operation(&win_iocp_handle_read_op::do_complete),
+ buffers_(buffers),
+ handler_(handler)
+ {
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code ec, std::size_t bytes_transferred)
+ {
+ // Take ownership of the operation object.
+ win_iocp_handle_read_op* o(static_cast<win_iocp_handle_read_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
+ if (owner)
+ {
+ // Check whether buffers are still valid.
+ buffer_sequence_adapter<asio::mutable_buffer,
+ MutableBufferSequence>::validate(o->buffers_);
+ }
+#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING)
+
+ // Map non-portable errors to their portable counterparts.
+ if (ec.value() == ERROR_HANDLE_EOF)
+ ec = asio::error::eof;
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder2<Handler, asio::error_code, std::size_t>
+ handler(o->handler_, ec, bytes_transferred);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ MutableBufferSequence buffers_;
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_IOCP)
+
+#endif // ASIO_DETAIL_WIN_IOCP_HANDLE_READ_OP_HPP
diff --git a/ext/asio/asio/detail/win_iocp_handle_service.hpp b/ext/asio/asio/detail/win_iocp_handle_service.hpp
index bfc159c..34f725f 100644
--- a/ext/asio/asio/detail/win_iocp_handle_service.hpp
+++ b/ext/asio/asio/detail/win_iocp_handle_service.hpp
@@ -1,8 +1,8 @@
//
-// win_iocp_handle_service.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/win_iocp_handle_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
// Copyright (c) 2008 Rep Invariant Systems, Inc. (info at repinvariant.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
@@ -16,26 +16,23 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/win_iocp_io_service_fwd.hpp"
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_IOCP)
-#include "asio/detail/push_options.hpp"
#include <boost/cstdint.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/error.hpp"
#include "asio/io_service.hpp"
-#include "asio/detail/bind_handler.hpp"
#include "asio/detail/buffer_sequence_adapter.hpp"
#include "asio/detail/handler_alloc_helpers.hpp"
-#include "asio/detail/handler_invoke_helpers.hpp"
#include "asio/detail/mutex.hpp"
#include "asio/detail/operation.hpp"
+#include "asio/detail/win_iocp_handle_read_op.hpp"
+#include "asio/detail/win_iocp_handle_write_op.hpp"
#include "asio/detail/win_iocp_io_service.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -76,75 +73,20 @@ public:
implementation_type* prev_;
};
- win_iocp_handle_service(asio::io_service& io_service)
- : iocp_service_(asio::use_service<win_iocp_io_service>(io_service)),
- mutex_(),
- impl_list_(0)
- {
- }
+ ASIO_DECL win_iocp_handle_service(asio::io_service& io_service);
// Destroy all user-defined handler objects owned by the service.
- void shutdown_service()
- {
- // Close all implementations, causing all operations to complete.
- asio::detail::mutex::scoped_lock lock(mutex_);
- implementation_type* impl = impl_list_;
- while (impl)
- {
- close_for_destruction(*impl);
- impl = impl->next_;
- }
- }
+ ASIO_DECL void shutdown_service();
// Construct a new handle implementation.
- void construct(implementation_type& impl)
- {
- impl.handle_ = INVALID_HANDLE_VALUE;
- impl.safe_cancellation_thread_id_ = 0;
-
- // Insert implementation into linked list of all implementations.
- asio::detail::mutex::scoped_lock lock(mutex_);
- impl.next_ = impl_list_;
- impl.prev_ = 0;
- if (impl_list_)
- impl_list_->prev_ = &impl;
- impl_list_ = &impl;
- }
+ ASIO_DECL void construct(implementation_type& impl);
// Destroy a handle implementation.
- void destroy(implementation_type& impl)
- {
- close_for_destruction(impl);
-
- // Remove implementation from linked list of all implementations.
- asio::detail::mutex::scoped_lock lock(mutex_);
- if (impl_list_ == &impl)
- impl_list_ = impl.next_;
- if (impl.prev_)
- impl.prev_->next_ = impl.next_;
- if (impl.next_)
- impl.next_->prev_= impl.prev_;
- impl.next_ = 0;
- impl.prev_ = 0;
- }
+ ASIO_DECL void destroy(implementation_type& impl);
// Assign a native handle to a handle implementation.
- asio::error_code assign(implementation_type& impl,
- const native_type& native_handle, asio::error_code& ec)
- {
- if (is_open(impl))
- {
- ec = asio::error::already_open;
- return ec;
- }
-
- if (iocp_service_.register_handle(native_handle, ec))
- return ec;
-
- impl.handle_ = native_handle;
- ec = asio::error_code();
- return ec;
- }
+ ASIO_DECL asio::error_code assign(implementation_type& impl,
+ const native_type& native_handle, asio::error_code& ec);
// Determine whether the handle is open.
bool is_open(const implementation_type& impl) const
@@ -153,26 +95,8 @@ public:
}
// Destroy a handle implementation.
- asio::error_code close(implementation_type& impl,
- asio::error_code& ec)
- {
- if (is_open(impl))
- {
- if (!::CloseHandle(impl.handle_))
- {
- DWORD last_error = ::GetLastError();
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- return ec;
- }
-
- impl.handle_ = INVALID_HANDLE_VALUE;
- impl.safe_cancellation_thread_id_ = 0;
- }
-
- ec = asio::error_code();
- return ec;
- }
+ ASIO_DECL asio::error_code close(implementation_type& impl,
+ asio::error_code& ec);
// Get the native handle representation.
native_type native(const implementation_type& impl) const
@@ -181,106 +105,8 @@ public:
}
// Cancel all operations associated with the handle.
- asio::error_code cancel(implementation_type& impl,
- asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- }
- else if (FARPROC cancel_io_ex_ptr = ::GetProcAddress(
- ::GetModuleHandleA("KERNEL32"), "CancelIoEx"))
- {
- // The version of Windows supports cancellation from any thread.
- typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED);
- cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr;
- if (!cancel_io_ex(impl.handle_, 0))
- {
- DWORD last_error = ::GetLastError();
- if (last_error == ERROR_NOT_FOUND)
- {
- // ERROR_NOT_FOUND means that there were no operations to be
- // cancelled. We swallow this error to match the behaviour on other
- // platforms.
- ec = asio::error_code();
- }
- else
- {
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- }
- }
- else
- {
- ec = asio::error_code();
- }
- }
- else if (impl.safe_cancellation_thread_id_ == 0)
- {
- // No operations have been started, so there's nothing to cancel.
- ec = asio::error_code();
- }
- else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId())
- {
- // Asynchronous operations have been started from the current thread only,
- // so it is safe to try to cancel them using CancelIo.
- if (!::CancelIo(impl.handle_))
- {
- DWORD last_error = ::GetLastError();
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- }
- else
- {
- ec = asio::error_code();
- }
- }
- else
- {
- // Asynchronous operations have been started from more than one thread,
- // so cancellation is not safe.
- ec = asio::error::operation_not_supported;
- }
-
- return ec;
- }
-
- class overlapped_wrapper
- : public OVERLAPPED
- {
- public:
- explicit overlapped_wrapper(asio::error_code& ec)
- {
- Internal = 0;
- InternalHigh = 0;
- Offset = 0;
- OffsetHigh = 0;
-
- // Create a non-signalled manual-reset event, for GetOverlappedResult.
- hEvent = ::CreateEvent(0, TRUE, FALSE, 0);
- if (hEvent)
- {
- // As documented in GetQueuedCompletionStatus, setting the low order
- // bit of this event prevents our synchronous writes from being treated
- // as completion port events.
- *reinterpret_cast<DWORD_PTR*>(&hEvent) |= 1;
- }
- else
- {
- DWORD last_error = ::GetLastError();
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- }
- }
-
- ~overlapped_wrapper()
- {
- if (hEvent)
- {
- ::CloseHandle(hEvent);
- }
- }
- };
+ ASIO_DECL asio::error_code cancel(implementation_type& impl,
+ asio::error_code& ec);
// Write the given data. Returns the number of bytes written.
template <typename ConstBufferSequence>
@@ -296,109 +122,13 @@ public:
size_t write_some_at(implementation_type& impl, boost::uint64_t offset,
const ConstBufferSequence& buffers, asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
asio::const_buffer buffer =
buffer_sequence_adapter<asio::const_buffer,
ConstBufferSequence>::first(buffers);
- // A request to write 0 bytes on a handle is a no-op.
- if (asio::buffer_size(buffer) == 0)
- {
- ec = asio::error_code();
- return 0;
- }
-
- overlapped_wrapper overlapped(ec);
- if (ec)
- {
- return 0;
- }
-
- // Write the data.
- overlapped.Offset = offset & 0xFFFFFFFF;
- overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
- BOOL ok = ::WriteFile(impl.handle_,
- asio::buffer_cast<LPCVOID>(buffer),
- static_cast<DWORD>(asio::buffer_size(buffer)), 0, &overlapped);
- if (!ok)
- {
- DWORD last_error = ::GetLastError();
- if (last_error != ERROR_IO_PENDING)
- {
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- return 0;
- }
- }
-
- // Wait for the operation to complete.
- DWORD bytes_transferred = 0;
- ok = ::GetOverlappedResult(impl.handle_,
- &overlapped, &bytes_transferred, TRUE);
- if (!ok)
- {
- DWORD last_error = ::GetLastError();
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- return 0;
- }
-
- ec = asio::error_code();
- return bytes_transferred;
+ return do_write(impl, offset, buffer, ec);
}
- template <typename ConstBufferSequence, typename Handler>
- class write_op : public operation
- {
- public:
- write_op(const ConstBufferSequence& buffers, Handler handler)
- : operation(&write_op::do_complete),
- buffers_(buffers),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code ec, std::size_t bytes_transferred)
- {
- // Take ownership of the operation object.
- write_op* o(static_cast<write_op*>(base));
- typedef handler_alloc_traits<Handler, write_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
-#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
- // Check whether buffers are still valid.
- buffer_sequence_adapter<asio::const_buffer,
- ConstBufferSequence>::validate(o->buffers_);
-#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING)
-
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder2<Handler, asio::error_code, std::size_t>
- handler(o->handler_, ec, bytes_transferred);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- ConstBufferSequence buffers_;
- Handler handler_;
- };
-
// Start an asynchronous write. The data being written must be valid for the
// lifetime of the asynchronous operation.
template <typename ConstBufferSequence, typename Handler>
@@ -415,15 +145,16 @@ public:
const ConstBufferSequence& buffers, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef write_op<ConstBufferSequence, Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, buffers, handler);
+ typedef win_iocp_handle_write_op<ConstBufferSequence, Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(buffers, handler);
start_write_op(impl, offset,
buffer_sequence_adapter<asio::const_buffer,
- ConstBufferSequence>::first(buffers), ptr.get());
- ptr.release();
+ ConstBufferSequence>::first(buffers), p.p);
+ p.v = p.p = 0;
}
// Read some data. Returns the number of bytes received.
@@ -439,129 +170,13 @@ public:
size_t read_some_at(implementation_type& impl, boost::uint64_t offset,
const MutableBufferSequence& buffers, asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
asio::mutable_buffer buffer =
buffer_sequence_adapter<asio::mutable_buffer,
MutableBufferSequence>::first(buffers);
- // A request to read 0 bytes on a stream handle is a no-op.
- if (asio::buffer_size(buffer) == 0)
- {
- ec = asio::error_code();
- return 0;
- }
-
- overlapped_wrapper overlapped(ec);
- if (ec)
- {
- return 0;
- }
-
- // Read some data.
- overlapped.Offset = offset & 0xFFFFFFFF;
- overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
- BOOL ok = ::ReadFile(impl.handle_,
- asio::buffer_cast<LPVOID>(buffer),
- static_cast<DWORD>(asio::buffer_size(buffer)), 0, &overlapped);
- if (!ok)
- {
- DWORD last_error = ::GetLastError();
- if (last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA)
- {
- if (last_error == ERROR_HANDLE_EOF)
- {
- ec = asio::error::eof;
- }
- else
- {
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- }
- return 0;
- }
- }
-
- // Wait for the operation to complete.
- DWORD bytes_transferred = 0;
- ok = ::GetOverlappedResult(impl.handle_,
- &overlapped, &bytes_transferred, TRUE);
- if (!ok)
- {
- DWORD last_error = ::GetLastError();
- if (last_error == ERROR_HANDLE_EOF)
- {
- ec = asio::error::eof;
- }
- else
- {
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- }
- return 0;
- }
-
- ec = asio::error_code();
- return bytes_transferred;
+ return do_read(impl, offset, buffer, ec);
}
- template <typename MutableBufferSequence, typename Handler>
- class read_op : public operation
- {
- public:
- read_op(const MutableBufferSequence& buffers, Handler handler)
- : operation(&read_op::do_complete),
- buffers_(buffers),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code ec, std::size_t bytes_transferred)
- {
- // Take ownership of the operation object.
- read_op* o(static_cast<read_op*>(base));
- typedef handler_alloc_traits<Handler, read_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
-#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
- // Check whether buffers are still valid.
- buffer_sequence_adapter<asio::mutable_buffer,
- MutableBufferSequence>::validate(o->buffers_);
-#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING)
-
- // Map non-portable errors to their portable counterparts.
- if (ec.value() == ERROR_HANDLE_EOF)
- {
- ec = asio::error::eof;
- }
-
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder2<Handler, asio::error_code, std::size_t>
- handler(o->handler_, ec, bytes_transferred);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- MutableBufferSequence buffers_;
- Handler handler_;
- };
-
// Start an asynchronous read. The buffer for the data being received must be
// valid for the lifetime of the asynchronous operation.
template <typename MutableBufferSequence, typename Handler>
@@ -579,15 +194,16 @@ public:
const MutableBufferSequence& buffers, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef read_op<MutableBufferSequence, Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, buffers, handler);
+ typedef win_iocp_handle_read_op<MutableBufferSequence, Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(buffers, handler);
start_read_op(impl, offset,
buffer_sequence_adapter<asio::mutable_buffer,
- MutableBufferSequence>::first(buffers), ptr.get());
- ptr.release();
+ MutableBufferSequence>::first(buffers), p.p);
+ p.v = p.p = 0;
}
private:
@@ -613,113 +229,42 @@ private:
void async_read_some_at(implementation_type& impl, boost::uint64_t offset,
const null_buffers& buffers, Handler handler);
+ // Helper class for waiting for synchronous operations to complete.
+ class overlapped_wrapper;
+
+ // Helper function to perform a synchronous write operation.
+ ASIO_DECL size_t do_write(implementation_type& impl,
+ boost::uint64_t offset, const asio::const_buffer& buffer,
+ asio::error_code& ec);
+
// Helper function to start a write operation.
- void start_write_op(implementation_type& impl, boost::uint64_t offset,
- const asio::const_buffer& buffer, operation* op)
- {
- update_cancellation_thread_id(impl);
- iocp_service_.work_started();
+ ASIO_DECL void start_write_op(implementation_type& impl,
+ boost::uint64_t offset, const asio::const_buffer& buffer,
+ operation* op);
- if (!is_open(impl))
- {
- iocp_service_.on_completion(op, asio::error::bad_descriptor);
- }
- else if (asio::buffer_size(buffer) == 0)
- {
- // A request to write 0 bytes on a handle is a no-op.
- iocp_service_.on_completion(op);
- }
- else
- {
- DWORD bytes_transferred = 0;
- op->Offset = offset & 0xFFFFFFFF;
- op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
- BOOL ok = ::WriteFile(impl.handle_,
- asio::buffer_cast<LPCVOID>(buffer),
- static_cast<DWORD>(asio::buffer_size(buffer)),
- &bytes_transferred, op);
- DWORD last_error = ::GetLastError();
- if (!ok && last_error != ERROR_IO_PENDING
- && last_error != ERROR_MORE_DATA)
- {
- iocp_service_.on_completion(op, last_error, bytes_transferred);
- }
- else
- {
- iocp_service_.on_pending(op);
- }
- }
- }
+ // Helper function to perform a synchronous write operation.
+ ASIO_DECL size_t do_read(implementation_type& impl,
+ boost::uint64_t offset, const asio::mutable_buffer& buffer,
+ asio::error_code& ec);
// Helper function to start a read operation.
- void start_read_op(implementation_type& impl, boost::uint64_t offset,
- const asio::mutable_buffer& buffer, operation* op)
- {
- update_cancellation_thread_id(impl);
- iocp_service_.work_started();
-
- if (!is_open(impl))
- {
- iocp_service_.on_completion(op, asio::error::bad_descriptor);
- }
- else if (asio::buffer_size(buffer) == 0)
- {
- // A request to read 0 bytes on a handle is a no-op.
- iocp_service_.on_completion(op);
- }
- else
- {
- DWORD bytes_transferred = 0;
- op->Offset = offset & 0xFFFFFFFF;
- op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
- BOOL ok = ::ReadFile(impl.handle_,
- asio::buffer_cast<LPVOID>(buffer),
- static_cast<DWORD>(asio::buffer_size(buffer)),
- &bytes_transferred, op);
- DWORD last_error = ::GetLastError();
- if (!ok && last_error != ERROR_IO_PENDING
- && last_error != ERROR_MORE_DATA)
- {
- iocp_service_.on_completion(op, last_error, bytes_transferred);
- }
- else
- {
- iocp_service_.on_pending(op);
- }
- }
- }
+ ASIO_DECL void start_read_op(implementation_type& impl,
+ boost::uint64_t offset, const asio::mutable_buffer& buffer,
+ operation* op);
// Update the ID of the thread from which cancellation is safe.
- void update_cancellation_thread_id(implementation_type& impl)
- {
-#if defined(ASIO_ENABLE_CANCELIO)
- if (impl.safe_cancellation_thread_id_ == 0)
- impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
- else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId())
- impl.safe_cancellation_thread_id_ = ~DWORD(0);
-#else // defined(ASIO_ENABLE_CANCELIO)
- (void)impl;
-#endif // defined(ASIO_ENABLE_CANCELIO)
- }
+ ASIO_DECL void update_cancellation_thread_id(implementation_type& impl);
// Helper function to close a handle when the associated object is being
// destroyed.
- void close_for_destruction(implementation_type& impl)
- {
- if (is_open(impl))
- {
- ::CloseHandle(impl.handle_);
- impl.handle_ = INVALID_HANDLE_VALUE;
- impl.safe_cancellation_thread_id_ = 0;
- }
- }
+ ASIO_DECL void close_for_destruction(implementation_type& impl);
// The IOCP service used for running asynchronous operations and dispatching
// handlers.
win_iocp_io_service& iocp_service_;
// Mutex to protect access to the linked list of implementations.
- asio::detail::mutex mutex_;
+ mutex mutex_;
// The head of a linked list of all implementations.
implementation_type* impl_list_;
@@ -728,8 +273,12 @@ private:
} // namespace detail
} // namespace asio
-#endif // defined(ASIO_HAS_IOCP)
-
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/win_iocp_handle_service.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(ASIO_HAS_IOCP)
+
#endif // ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP
diff --git a/ext/asio/asio/detail/win_iocp_handle_write_op.hpp b/ext/asio/asio/detail/win_iocp_handle_write_op.hpp
new file mode 100644
index 0000000..ddfe8fc
--- /dev/null
+++ b/ext/asio/asio/detail/win_iocp_handle_write_op.hpp
@@ -0,0 +1,97 @@
+//
+// detail/win_iocp_handle_write_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2008 Rep Invariant Systems, Inc. (info at repinvariant.com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_IOCP_HANDLE_WRITE_OP_HPP
+#define ASIO_DETAIL_WIN_IOCP_HANDLE_WRITE_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+
+#include "asio/error.hpp"
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/operation.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename ConstBufferSequence, typename Handler>
+class win_iocp_handle_write_op : public operation
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(win_iocp_handle_write_op);
+
+ win_iocp_handle_write_op(const ConstBufferSequence& buffers, Handler handler)
+ : operation(&win_iocp_handle_write_op::do_complete),
+ buffers_(buffers),
+ handler_(handler)
+ {
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code ec, std::size_t bytes_transferred)
+ {
+ // Take ownership of the operation object.
+ win_iocp_handle_write_op* o(static_cast<win_iocp_handle_write_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
+ if (owner)
+ {
+ // Check whether buffers are still valid.
+ buffer_sequence_adapter<asio::const_buffer,
+ ConstBufferSequence>::validate(o->buffers_);
+ }
+#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING)
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder2<Handler, asio::error_code, std::size_t>
+ handler(o->handler_, ec, bytes_transferred);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ ConstBufferSequence buffers_;
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_IOCP)
+
+#endif // ASIO_DETAIL_WIN_IOCP_HANDLE_WRITE_OP_HPP
diff --git a/ext/asio/asio/detail/win_iocp_io_service.hpp b/ext/asio/asio/detail/win_iocp_io_service.hpp
index fd899c1..c8974b6 100644
--- a/ext/asio/asio/detail/win_iocp_io_service.hpp
+++ b/ext/asio/asio/detail/win_iocp_io_service.hpp
@@ -1,8 +1,8 @@
//
-// win_iocp_io_service.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~
+// detail/win_iocp_io_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,33 +15,24 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/win_iocp_io_service_fwd.hpp"
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_IOCP)
-#include "asio/detail/push_options.hpp"
-#include <boost/limits.hpp>
-#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
-
+#include <boost/scoped_ptr.hpp>
#include "asio/io_service.hpp"
-#include "asio/system_error.hpp"
-#include "asio/detail/call_stack.hpp"
-#include "asio/detail/completion_handler.hpp"
-#include "asio/detail/fenced_block.hpp"
-#include "asio/detail/handler_alloc_helpers.hpp"
-#include "asio/detail/handler_invoke_helpers.hpp"
#include "asio/detail/mutex.hpp"
#include "asio/detail/op_queue.hpp"
-#include "asio/detail/service_base.hpp"
#include "asio/detail/socket_types.hpp"
#include "asio/detail/timer_op.hpp"
#include "asio/detail/timer_queue_base.hpp"
#include "asio/detail/timer_queue_fwd.hpp"
#include "asio/detail/timer_queue_set.hpp"
+#include "asio/detail/win_iocp_io_service_fwd.hpp"
#include "asio/detail/win_iocp_operation.hpp"
+#include "asio/detail/thread.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
@@ -52,69 +43,13 @@ class win_iocp_io_service
: public asio::detail::service_base<win_iocp_io_service>
{
public:
- typedef win_iocp_operation operation;
-
// Constructor.
- win_iocp_io_service(asio::io_service& io_service)
- : asio::detail::service_base<win_iocp_io_service>(io_service),
- iocp_(),
- outstanding_work_(0),
- stopped_(0),
- shutdown_(0),
- timer_thread_(0),
- timer_interrupt_issued_(false)
- {
- }
+ ASIO_DECL win_iocp_io_service(asio::io_service& io_service);
- void init(size_t concurrency_hint)
- {
- iocp_.handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0,
- static_cast<DWORD>((std::min<size_t>)(concurrency_hint, DWORD(~0))));
- if (!iocp_.handle)
- {
- DWORD last_error = ::GetLastError();
- asio::system_error e(
- asio::error_code(last_error,
- asio::error::get_system_category()),
- "iocp");
- boost::throw_exception(e);
- }
- }
+ ASIO_DECL void init(size_t concurrency_hint);
// Destroy all user-defined handler objects owned by the service.
- void shutdown_service()
- {
- ::InterlockedExchange(&shutdown_, 1);
-
- while (::InterlockedExchangeAdd(&outstanding_work_, 0) > 0)
- {
- op_queue<operation> ops;
- timer_queues_.get_all_timers(ops);
- ops.push(completed_ops_);
- if (!ops.empty())
- {
- while (operation* op = ops.front())
- {
- ops.pop();
- ::InterlockedDecrement(&outstanding_work_);
- op->destroy();
- }
- }
- else
- {
- DWORD bytes_transferred = 0;
- dword_ptr_t completion_key = 0;
- LPOVERLAPPED overlapped = 0;
- ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred,
- &completion_key, &overlapped, max_timeout);
- if (overlapped)
- {
- ::InterlockedDecrement(&outstanding_work_);
- static_cast<operation*>(overlapped)->destroy();
- }
- }
- }
- }
+ ASIO_DECL void shutdown_service();
// Initialise the task. Nothing to do here.
void init_task()
@@ -122,106 +57,23 @@ public:
}
// Register a handle with the IO completion port.
- asio::error_code register_handle(
- HANDLE handle, asio::error_code& ec)
- {
- if (::CreateIoCompletionPort(handle, iocp_.handle, 0, 0) == 0)
- {
- DWORD last_error = ::GetLastError();
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- }
- else
- {
- ec = asio::error_code();
- }
- return ec;
- }
+ ASIO_DECL asio::error_code register_handle(
+ HANDLE handle, asio::error_code& ec);
// Run the event loop until stopped or no more work.
- size_t run(asio::error_code& ec)
- {
- if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
- {
- stop();
- ec = asio::error_code();
- return 0;
- }
-
- call_stack<win_iocp_io_service>::context ctx(this);
-
- size_t n = 0;
- while (do_one(true, ec))
- if (n != (std::numeric_limits<size_t>::max)())
- ++n;
- return n;
- }
+ ASIO_DECL size_t run(asio::error_code& ec);
// Run until stopped or one operation is performed.
- size_t run_one(asio::error_code& ec)
- {
- if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
- {
- stop();
- ec = asio::error_code();
- return 0;
- }
-
- call_stack<win_iocp_io_service>::context ctx(this);
-
- return do_one(true, ec);
- }
+ ASIO_DECL size_t run_one(asio::error_code& ec);
// Poll for operations without blocking.
- size_t poll(asio::error_code& ec)
- {
- if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
- {
- stop();
- ec = asio::error_code();
- return 0;
- }
-
- call_stack<win_iocp_io_service>::context ctx(this);
-
- size_t n = 0;
- while (do_one(false, ec))
- if (n != (std::numeric_limits<size_t>::max)())
- ++n;
- return n;
- }
+ ASIO_DECL size_t poll(asio::error_code& ec);
// Poll for one operation without blocking.
- size_t poll_one(asio::error_code& ec)
- {
- if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
- {
- stop();
- ec = asio::error_code();
- return 0;
- }
-
- call_stack<win_iocp_io_service>::context ctx(this);
-
- return do_one(false, ec);
- }
+ ASIO_DECL size_t poll_one(asio::error_code& ec);
// Stop the event processing loop.
- void stop()
- {
- if (::InterlockedExchange(&stopped_, 1) == 0)
- {
- if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0))
- {
- DWORD last_error = ::GetLastError();
- asio::system_error e(
- asio::error_code(last_error,
- asio::error::get_system_category()),
- "pqcs");
- boost::throw_exception(e);
- }
- }
- }
+ ASIO_DECL void stop();
// Reset in preparation for a subsequent run invocation.
void reset()
@@ -244,34 +96,15 @@ public:
// Request invocation of the given handler.
template <typename Handler>
- void dispatch(Handler handler)
- {
- if (call_stack<win_iocp_io_service>::contains(this))
- {
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- else
- post(handler);
- }
+ void dispatch(Handler handler);
// Request invocation of the given handler and return immediately.
template <typename Handler>
- void post(Handler handler)
- {
- // Allocate and construct an operation to wrap the handler.
- typedef completion_handler<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, handler);
-
- post_immediate_completion(ptr.get());
- ptr.release();
- }
+ void post(Handler handler);
// Request invocation of the given operation and return immediately. Assumes
// that work_started() has not yet been called for the operation.
- void post_immediate_completion(operation* op)
+ void post_immediate_completion(win_iocp_operation* op)
{
work_started();
post_deferred_completion(op);
@@ -279,170 +112,50 @@ public:
// Request invocation of the given operation and return immediately. Assumes
// that work_started() was previously called for the operation.
- void post_deferred_completion(operation* op)
- {
- // Flag the operation as ready.
- op->ready_ = 1;
-
- // Enqueue the operation on the I/O completion port.
- if (!::PostQueuedCompletionStatus(iocp_.handle,
- 0, overlapped_contains_result, op))
- {
- // Out of resources. Put on completed queue instead.
- asio::detail::mutex::scoped_lock lock(timer_mutex_);
- completed_ops_.push(op);
- }
- }
+ ASIO_DECL void post_deferred_completion(win_iocp_operation* op);
// Request invocation of the given operation and return immediately. Assumes
// that work_started() was previously called for the operations.
- void post_deferred_completions(op_queue<operation>& ops)
- {
- while (operation* op = ops.front())
- {
- ops.pop();
-
- // Flag the operation as ready.
- op->ready_ = 1;
-
- // Enqueue the operation on the I/O completion port.
- if (!::PostQueuedCompletionStatus(iocp_.handle,
- 0, overlapped_contains_result, op))
- {
- // Out of resources. Put on completed queue instead.
- asio::detail::mutex::scoped_lock lock(timer_mutex_);
- completed_ops_.push(op);
- completed_ops_.push(ops);
- }
- }
- }
+ ASIO_DECL void post_deferred_completions(
+ op_queue<win_iocp_operation>& ops);
// Called after starting an overlapped I/O operation that did not complete
// immediately. The caller must have already called work_started() prior to
// starting the operation.
- void on_pending(operation* op)
- {
- if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1)
- {
- // Enqueue the operation on the I/O completion port.
- if (!::PostQueuedCompletionStatus(iocp_.handle,
- 0, overlapped_contains_result, op))
- {
- // Out of resources. Put on completed queue instead.
- asio::detail::mutex::scoped_lock lock(timer_mutex_);
- completed_ops_.push(op);
- }
- }
- }
+ ASIO_DECL void on_pending(win_iocp_operation* op);
// Called after starting an overlapped I/O operation that completed
// immediately. The caller must have already called work_started() prior to
// starting the operation.
- void on_completion(operation* op,
- DWORD last_error = 0, DWORD bytes_transferred = 0)
- {
- // Flag that the operation is ready for invocation.
- op->ready_ = 1;
-
- // Store results in the OVERLAPPED structure.
- op->Internal = asio::error::get_system_category();
- op->Offset = last_error;
- op->OffsetHigh = bytes_transferred;
-
- // Enqueue the operation on the I/O completion port.
- if (!::PostQueuedCompletionStatus(iocp_.handle,
- 0, overlapped_contains_result, op))
- {
- // Out of resources. Put on completed queue instead.
- asio::detail::mutex::scoped_lock lock(timer_mutex_);
- completed_ops_.push(op);
- }
- }
+ ASIO_DECL void on_completion(win_iocp_operation* op,
+ DWORD last_error = 0, DWORD bytes_transferred = 0);
// Called after starting an overlapped I/O operation that completed
// immediately. The caller must have already called work_started() prior to
// starting the operation.
- void on_completion(operation* op,
- const asio::error_code& ec, DWORD bytes_transferred = 0)
- {
- // Flag that the operation is ready for invocation.
- op->ready_ = 1;
-
- // Store results in the OVERLAPPED structure.
- op->Internal = ec.category();
- op->Offset = ec.value();
- op->OffsetHigh = bytes_transferred;
-
- // Enqueue the operation on the I/O completion port.
- if (!::PostQueuedCompletionStatus(iocp_.handle,
- 0, overlapped_contains_result, op))
- {
- // Out of resources. Put on completed queue instead.
- asio::detail::mutex::scoped_lock lock(timer_mutex_);
- completed_ops_.push(op);
- }
- }
+ ASIO_DECL void on_completion(win_iocp_operation* op,
+ const asio::error_code& ec, DWORD bytes_transferred = 0);
// Add a new timer queue to the service.
template <typename Time_Traits>
- void add_timer_queue(timer_queue<Time_Traits>& timer_queue)
- {
- asio::detail::mutex::scoped_lock lock(timer_mutex_);
- timer_queues_.insert(&timer_queue);
- }
+ void add_timer_queue(timer_queue<Time_Traits>& timer_queue);
// Remove a timer queue from the service.
template <typename Time_Traits>
- void remove_timer_queue(timer_queue<Time_Traits>& timer_queue)
- {
- asio::detail::mutex::scoped_lock lock(timer_mutex_);
- timer_queues_.erase(&timer_queue);
- }
+ void remove_timer_queue(timer_queue<Time_Traits>& timer_queue);
// Schedule a new operation in the given timer queue to expire at the
// specified absolute time.
template <typename Time_Traits>
- void schedule_timer(timer_queue<Time_Traits>& timer_queue,
- const typename Time_Traits::time_type& time, timer_op* op, void* token)
- {
- // If the service has been shut down we silently discard the timer.
- if (::InterlockedExchangeAdd(&shutdown_, 0) != 0)
- return;
-
- asio::detail::mutex::scoped_lock lock(timer_mutex_);
- bool interrupt = timer_queue.enqueue_timer(time, op, token);
- work_started();
- if (interrupt && !timer_interrupt_issued_)
- {
- timer_interrupt_issued_ = true;
- lock.unlock();
- ::PostQueuedCompletionStatus(iocp_.handle,
- 0, steal_timer_dispatching, 0);
- }
- }
+ void schedule_timer(timer_queue<Time_Traits>& queue,
+ const typename Time_Traits::time_type& time,
+ typename timer_queue<Time_Traits>::per_timer_data& timer, timer_op* op);
// Cancel the timer associated with the given token. Returns the number of
// handlers that have been posted or dispatched.
template <typename Time_Traits>
- std::size_t cancel_timer(timer_queue<Time_Traits>& timer_queue, void* token)
- {
- // If the service has been shut down we silently ignore the cancellation.
- if (::InterlockedExchangeAdd(&shutdown_, 0) != 0)
- return 0;
-
- asio::detail::mutex::scoped_lock lock(timer_mutex_);
- op_queue<operation> ops;
- std::size_t n = timer_queue.cancel_timer(token, ops);
- post_deferred_completions(ops);
- if (n > 0 && !timer_interrupt_issued_)
- {
- timer_interrupt_issued_ = true;
- lock.unlock();
- ::PostQueuedCompletionStatus(iocp_.handle,
- 0, steal_timer_dispatching, 0);
- }
- return n;
- }
+ std::size_t cancel_timer(timer_queue<Time_Traits>& queue,
+ typename timer_queue<Time_Traits>::per_timer_data& timer);
private:
#if defined(WINVER) && (WINVER < 0x0500)
@@ -456,181 +169,30 @@ private:
// Dequeues at most one operation from the I/O completion port, and then
// executes it. Returns the number of operations that were dequeued (i.e.
// either 0 or 1).
- size_t do_one(bool block, asio::error_code& ec)
- {
- long this_thread_id = static_cast<long>(::GetCurrentThreadId());
-
- for (;;)
- {
- // Try to acquire responsibility for dispatching timers.
- bool dispatching_timers = (::InterlockedCompareExchange(
- &timer_thread_, this_thread_id, 0) == 0);
-
- // Calculate timeout for GetQueuedCompletionStatus call.
- DWORD timeout = max_timeout;
- if (dispatching_timers)
- {
- asio::detail::mutex::scoped_lock lock(timer_mutex_);
- timer_interrupt_issued_ = false;
- timeout = get_timeout();
- }
-
- // Get the next operation from the queue.
- DWORD bytes_transferred = 0;
- dword_ptr_t completion_key = 0;
- LPOVERLAPPED overlapped = 0;
- ::SetLastError(0);
- BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred,
- &completion_key, &overlapped, block ? timeout : 0);
- DWORD last_error = ::GetLastError();
-
- // Dispatch any pending timers.
- if (dispatching_timers)
- {
- asio::detail::mutex::scoped_lock lock(timer_mutex_);
- op_queue<operation> ops;
- ops.push(completed_ops_);
- timer_queues_.get_ready_timers(ops);
- post_deferred_completions(ops);
- }
-
- if (!ok && overlapped == 0)
- {
- if (block && last_error == WAIT_TIMEOUT)
- {
- // Relinquish responsibility for dispatching timers.
- if (dispatching_timers)
- {
- ::InterlockedCompareExchange(&timer_thread_, 0, this_thread_id);
- }
-
- continue;
- }
-
- // Transfer responsibility for dispatching timers to another thread.
- if (dispatching_timers && ::InterlockedCompareExchange(
- &timer_thread_, 0, this_thread_id) == this_thread_id)
- {
- ::PostQueuedCompletionStatus(iocp_.handle,
- 0, transfer_timer_dispatching, 0);
- }
-
- ec = asio::error_code();
- return 0;
- }
- else if (overlapped)
- {
- operation* op = static_cast<operation*>(overlapped);
- asio::error_code result_ec(last_error,
- asio::error::get_system_category());
-
- // Transfer responsibility for dispatching timers to another thread.
- if (dispatching_timers && ::InterlockedCompareExchange(
- &timer_thread_, 0, this_thread_id) == this_thread_id)
- {
- ::PostQueuedCompletionStatus(iocp_.handle,
- 0, transfer_timer_dispatching, 0);
- }
-
- // We may have been passed the last_error and bytes_transferred in the
- // OVERLAPPED structure itself.
- if (completion_key == overlapped_contains_result)
- {
- result_ec = asio::error_code(static_cast<int>(op->Offset),
- static_cast<asio::error_category>(op->Internal));
- bytes_transferred = op->OffsetHigh;
- }
-
- // Otherwise ensure any result has been saved into the OVERLAPPED
- // structure.
- else
- {
- op->Internal = result_ec.category();
- op->Offset = result_ec.value();
- op->OffsetHigh = bytes_transferred;
- }
-
- // Dispatch the operation only if ready. The operation may not be ready
- // if the initiating function (e.g. a call to WSARecv) has not yet
- // returned. This is because the initiating function still wants access
- // to the operation's OVERLAPPED structure.
- if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1)
- {
- // Ensure the count of outstanding work is decremented on block exit.
- work_finished_on_block_exit on_exit = { this };
- (void)on_exit;
-
- op->complete(*this, result_ec, bytes_transferred);
- ec = asio::error_code();
- return 1;
- }
- }
- else if (completion_key == transfer_timer_dispatching)
- {
- // Woken up to try to acquire responsibility for dispatching timers.
- ::InterlockedCompareExchange(&timer_thread_, 0, this_thread_id);
- }
- else if (completion_key == steal_timer_dispatching)
- {
- // Woken up to steal responsibility for dispatching timers.
- ::InterlockedExchange(&timer_thread_, 0);
- }
- else
- {
- // Relinquish responsibility for dispatching timers. If the io_service
- // is not being stopped then the thread will get an opportunity to
- // reacquire timer responsibility on the next loop iteration.
- if (dispatching_timers)
- {
- ::InterlockedCompareExchange(&timer_thread_, 0, this_thread_id);
- }
-
- // The stopped_ flag is always checked to ensure that any leftover
- // interrupts from a previous run invocation are ignored.
- if (::InterlockedExchangeAdd(&stopped_, 0) != 0)
- {
- // Wake up next thread that is blocked on GetQueuedCompletionStatus.
- if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0))
- {
- last_error = ::GetLastError();
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- return 0;
- }
-
- ec = asio::error_code();
- return 0;
- }
- }
- }
- }
+ ASIO_DECL size_t do_one(bool block, asio::error_code& ec);
- // Get the timeout value for the GetQueuedCompletionStatus call. The timeout
- // value is returned as a number of milliseconds. We will wait no longer than
- // 1000 milliseconds.
- DWORD get_timeout()
- {
- return timer_queues_.wait_duration_msec(max_timeout);
- }
+ // Helper function to add a new timer queue.
+ ASIO_DECL void do_add_timer_queue(timer_queue_base& queue);
+
+ // Helper function to remove a timer queue.
+ ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue);
+
+ // Called to recalculate and update the timeout.
+ ASIO_DECL void update_timeout();
// Helper class to call work_finished() on block exit.
- struct work_finished_on_block_exit
- {
- ~work_finished_on_block_exit()
- {
- io_service_->work_finished();
- }
+ struct work_finished_on_block_exit;
- win_iocp_io_service* io_service_;
+ // Helper class for managing a HANDLE.
+ struct auto_handle
+ {
+ HANDLE handle;
+ auto_handle() : handle(0) {}
+ ~auto_handle() { if (handle) ::CloseHandle(handle); }
};
// The IO completion port used for queueing operations.
- struct iocp_holder
- {
- HANDLE handle;
- iocp_holder() : handle(0) {}
- ~iocp_holder() { if (handle) ::CloseHandle(handle); }
- } iocp_;
+ auto_handle iocp_;
// The count of unfinished work.
long outstanding_work_;
@@ -643,44 +205,61 @@ private:
enum
{
- // Maximum GetQueuedCompletionStatus timeout, in milliseconds.
- max_timeout = 500,
+ // Timeout to use with GetQueuedCompletionStatus. Some versions of windows
+ // have a "bug" where a call to GetQueuedCompletionStatus can appear stuck
+ // even though there are events waiting on the queue. Using a timeout helps
+ // to work around the issue.
+ gqcs_timeout = 500,
+
+ // Maximum waitable timer timeout, in milliseconds.
+ max_timeout_msec = 5 * 60 * 1000,
- // Completion key value to indicate that responsibility for dispatching
- // timers is being cooperatively transferred from one thread to another.
- transfer_timer_dispatching = 1,
+ // Maximum waitable timer timeout, in microseconds.
+ max_timeout_usec = max_timeout_msec * 1000,
- // Completion key value to indicate that responsibility for dispatching
- // timers should be stolen from another thread.
- steal_timer_dispatching = 2,
+ // Completion key value used to wake up a thread to dispatch timers or
+ // completed operations.
+ wake_for_dispatch = 1,
// Completion key value to indicate that an operation has posted with the
// original last_error and bytes_transferred values stored in the fields of
// the OVERLAPPED structure.
- overlapped_contains_result = 3
+ overlapped_contains_result = 2
};
- // The thread that's currently in charge of dispatching timers.
- long timer_thread_;
+ // Function object for processing timeouts in a background thread.
+ struct timer_thread_function;
+ friend struct timer_thread_function;
+
+ // Background thread used for processing timeouts.
+ boost::scoped_ptr<thread> timer_thread_;
+
+ // A waitable timer object used for waiting for timeouts.
+ auto_handle waitable_timer_;
- // Mutex for protecting access to the timer queues.
- mutex timer_mutex_;
+ // Non-zero if timers or completed operations need to be dispatched.
+ long dispatch_required_;
- // Whether a thread has been interrupted to process a new timeout.
- bool timer_interrupt_issued_;
+ // Mutex for protecting access to the timer queues and completed operations.
+ mutex dispatch_mutex_;
// The timer queues.
timer_queue_set timer_queues_;
// The operations that are ready to dispatch.
- op_queue<operation> completed_ops_;
+ op_queue<win_iocp_operation> completed_ops_;
};
} // namespace detail
} // namespace asio
-#endif // defined(ASIO_HAS_IOCP)
-
#include "asio/detail/pop_options.hpp"
+#include "asio/detail/impl/win_iocp_io_service.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/win_iocp_io_service.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(ASIO_HAS_IOCP)
+
#endif // ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP
diff --git a/ext/asio/asio/detail/win_iocp_io_service_fwd.hpp b/ext/asio/asio/detail/win_iocp_io_service_fwd.hpp
index 29d2a05..9f03743 100644
--- a/ext/asio/asio/detail/win_iocp_io_service_fwd.hpp
+++ b/ext/asio/asio/detail/win_iocp_io_service_fwd.hpp
@@ -1,8 +1,8 @@
//
-// win_iocp_io_service_fwd.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/win_iocp_io_service_fwd.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,22 +15,9 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/detail/socket_types.hpp"
-
-// This service is only supported on Win32 (NT4 and later).
-#if !defined(ASIO_DISABLE_IOCP)
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400)
-#if !defined(UNDER_CE)
-
-// Define this to indicate that IOCP is supported on the target platform.
-#define ASIO_HAS_IOCP 1
+#if defined(ASIO_HAS_IOCP)
namespace asio {
namespace detail {
@@ -41,11 +28,6 @@ class win_iocp_overlapped_ptr;
} // namespace detail
} // namespace asio
-#endif // !defined(UNDER_CE)
-#endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400)
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-#endif // !defined(ASIO_DISABLE_IOCP)
-
-#include "asio/detail/pop_options.hpp"
+#endif // defined(ASIO_HAS_IOCP)
#endif // ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP
diff --git a/ext/asio/asio/detail/win_iocp_null_buffers_op.hpp b/ext/asio/asio/detail/win_iocp_null_buffers_op.hpp
new file mode 100644
index 0000000..e39fff7
--- /dev/null
+++ b/ext/asio/asio/detail/win_iocp_null_buffers_op.hpp
@@ -0,0 +1,112 @@
+//
+// detail/win_iocp_null_buffers_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_IOCP_NULL_BUFFERS_OP_HPP
+#define ASIO_DETAIL_WIN_IOCP_NULL_BUFFERS_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/reactor_op.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Handler>
+class win_iocp_null_buffers_op : public reactor_op
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(win_iocp_null_buffers_op);
+
+ win_iocp_null_buffers_op(socket_ops::weak_cancel_token_type cancel_token,
+ Handler handler)
+ : reactor_op(&win_iocp_null_buffers_op::do_perform,
+ &win_iocp_null_buffers_op::do_complete),
+ cancel_token_(cancel_token),
+ handler_(handler)
+ {
+ }
+
+ static bool do_perform(reactor_op*)
+ {
+ return true;
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code ec, std::size_t bytes_transferred)
+ {
+ // Take ownership of the operation object.
+ win_iocp_null_buffers_op* o(static_cast<win_iocp_null_buffers_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+ // The reactor may have stored a result in the operation object.
+ if (o->ec_)
+ ec = o->ec_;
+
+ // Map non-portable errors to their portable counterparts.
+ if (ec.value() == ERROR_NETNAME_DELETED)
+ {
+ if (o->cancel_token_.expired())
+ ec = asio::error::operation_aborted;
+ else
+ ec = asio::error::connection_reset;
+ }
+ else if (ec.value() == ERROR_PORT_UNREACHABLE)
+ {
+ ec = asio::error::connection_refused;
+ }
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder2<Handler, asio::error_code, std::size_t>
+ handler(o->handler_, ec, bytes_transferred);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ socket_ops::weak_cancel_token_type cancel_token_;
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_IOCP)
+
+#endif // ASIO_DETAIL_WIN_IOCP_NULL_BUFFERS_OP_HPP
diff --git a/ext/asio/asio/detail/win_iocp_operation.hpp b/ext/asio/asio/detail/win_iocp_operation.hpp
index ac81062..649edd1 100644
--- a/ext/asio/asio/detail/win_iocp_operation.hpp
+++ b/ext/asio/asio/detail/win_iocp_operation.hpp
@@ -1,8 +1,8 @@
//
-// win_iocp_operation.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~
+// detail/win_iocp_operation.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,14 +15,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/win_iocp_io_service_fwd.hpp"
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_IOCP)
-#include "asio/error_code.hpp"
#include "asio/detail/op_queue.hpp"
+#include "asio/detail/win_iocp_io_service_fwd.hpp"
+#include "asio/error_code.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
@@ -82,8 +83,8 @@ private:
} // namespace detail
} // namespace asio
-#endif // defined(ASIO_HAS_IOCP)
-
#include "asio/detail/pop_options.hpp"
+#endif // defined(ASIO_HAS_IOCP)
+
#endif // ASIO_DETAIL_WIN_IOCP_OPERATION_HPP
diff --git a/ext/asio/asio/detail/win_iocp_overlapped_op.hpp b/ext/asio/asio/detail/win_iocp_overlapped_op.hpp
new file mode 100644
index 0000000..f2b7ddf
--- /dev/null
+++ b/ext/asio/asio/detail/win_iocp_overlapped_op.hpp
@@ -0,0 +1,84 @@
+//
+// detail/win_iocp_overlapped_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_IOCP_OVERLAPPED_OP_HPP
+#define ASIO_DETAIL_WIN_IOCP_OVERLAPPED_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+
+#include "asio/error.hpp"
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/operation.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Handler>
+class win_iocp_overlapped_op : public operation
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(win_iocp_overlapped_op);
+
+ win_iocp_overlapped_op(Handler handler)
+ : operation(&win_iocp_overlapped_op::do_complete),
+ handler_(handler)
+ {
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code ec, std::size_t bytes_transferred)
+ {
+ // Take ownership of the operation object.
+ win_iocp_overlapped_op* o(static_cast<win_iocp_overlapped_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder2<Handler, asio::error_code, std::size_t>
+ handler(o->handler_, ec, bytes_transferred);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_IOCP)
+
+#endif // ASIO_DETAIL_WIN_IOCP_OVERLAPPED_OP_HPP
diff --git a/ext/asio/asio/detail/win_iocp_overlapped_ptr.hpp b/ext/asio/asio/detail/win_iocp_overlapped_ptr.hpp
index 47a3f70..919affe 100644
--- a/ext/asio/asio/detail/win_iocp_overlapped_ptr.hpp
+++ b/ext/asio/asio/detail/win_iocp_overlapped_ptr.hpp
@@ -1,8 +1,8 @@
//
-// win_iocp_overlapped_ptr.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/win_iocp_overlapped_ptr.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,18 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/win_iocp_io_service_fwd.hpp"
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_IOCP)
-#include "asio/detail/fenced_block.hpp"
+#include <boost/utility/addressof.hpp>
+#include "asio/io_service.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
#include "asio/detail/noncopyable.hpp"
+#include "asio/detail/win_iocp_overlapped_op.hpp"
#include "asio/detail/win_iocp_io_service.hpp"
-#include "asio/detail/win_iocp_operation.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
@@ -74,13 +76,15 @@ public:
template <typename Handler>
void reset(asio::io_service& io_service, Handler handler)
{
- typedef overlapped_op<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, handler);
+ typedef win_iocp_overlapped_op<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(handler);
io_service.impl_.work_started();
reset();
- ptr_ = ptr.release();
+ ptr_ = p.p;
+ p.v = p.p = 0;
iocp_service_ = &io_service.impl_;
}
@@ -122,44 +126,6 @@ public:
}
private:
- template <typename Handler>
- struct overlapped_op : public win_iocp_operation
- {
- overlapped_op(Handler handler)
- : win_iocp_operation(&overlapped_op::do_complete),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code ec, std::size_t bytes_transferred)
- {
- // Take ownership of the operation object.
- overlapped_op* o(static_cast<overlapped_op*>(base));
- typedef handler_alloc_traits<Handler, overlapped_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder2<Handler, asio::error_code, std::size_t>
- handler(o->handler_, ec, bytes_transferred);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- Handler handler_;
- };
-
win_iocp_operation* ptr_;
win_iocp_io_service* iocp_service_;
};
@@ -167,8 +133,8 @@ private:
} // namespace detail
} // namespace asio
-#endif // defined(ASIO_HAS_IOCP)
-
#include "asio/detail/pop_options.hpp"
+#endif // defined(ASIO_HAS_IOCP)
+
#endif // ASIO_DETAIL_WIN_IOCP_OVERLAPPED_PTR_HPP
diff --git a/ext/asio/asio/detail/win_iocp_serial_port_service.hpp b/ext/asio/asio/detail/win_iocp_serial_port_service.hpp
index ed5f75e..bf3a692 100644
--- a/ext/asio/asio/detail/win_iocp_serial_port_service.hpp
+++ b/ext/asio/asio/detail/win_iocp_serial_port_service.hpp
@@ -1,8 +1,8 @@
//
-// win_iocp_serial_port_service.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/win_iocp_serial_port_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
// Copyright (c) 2008 Rep Invariant Systems, Inc. (info at repinvariant.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
@@ -16,21 +16,17 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <cstring>
-#include <string>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/detail/win_iocp_io_service_fwd.hpp"
+#include "asio/detail/config.hpp"
-#if defined(ASIO_HAS_IOCP)
+#if defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT)
+#include <string>
#include "asio/error.hpp"
#include "asio/io_service.hpp"
#include "asio/detail/win_iocp_handle_service.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -38,133 +34,56 @@ namespace detail {
class win_iocp_serial_port_service
{
public:
- // The native type of a stream handle.
+ // The native type of a serial port.
typedef win_iocp_handle_service::native_type native_type;
- // The implementation type of the stream handle.
+ // The implementation type of the serial port.
typedef win_iocp_handle_service::implementation_type implementation_type;
- win_iocp_serial_port_service(asio::io_service& io_service)
- : handle_service_(io_service)
- {
- }
+ // Constructor.
+ ASIO_DECL win_iocp_serial_port_service(
+ asio::io_service& io_service);
// Destroy all user-defined handler objects owned by the service.
- void shutdown_service()
- {
- }
+ ASIO_DECL void shutdown_service();
- // Construct a new handle implementation.
+ // Construct a new serial port implementation.
void construct(implementation_type& impl)
{
handle_service_.construct(impl);
}
- // Destroy a handle implementation.
+ // Destroy a serial port implementation.
void destroy(implementation_type& impl)
{
handle_service_.destroy(impl);
}
// Open the serial port using the specified device name.
- asio::error_code open(implementation_type& impl,
- const std::string& device, asio::error_code& ec)
- {
- if (is_open(impl))
- {
- ec = asio::error::already_open;
- return ec;
- }
-
- // For convenience, add a leading \\.\ sequence if not already present.
- std::string name = (device[0] == '\\') ? device : "\\\\.\\" + device;
-
- // Open a handle to the serial port.
- ::HANDLE handle = ::CreateFileA(name.c_str(),
- GENERIC_READ | GENERIC_WRITE, 0, 0,
- OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
- if (handle == INVALID_HANDLE_VALUE)
- {
- DWORD last_error = ::GetLastError();
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- return ec;
- }
-
- // Determine the initial serial port parameters.
- using namespace std; // For memcpy.
- ::DCB dcb;
- memset(&dcb, 0, sizeof(DCB));
- dcb.DCBlength = sizeof(DCB);
- if (!::GetCommState(handle, &dcb))
- {
- DWORD last_error = ::GetLastError();
- ::CloseHandle(handle);
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- return ec;
- }
-
- // Set some default serial port parameters. This implementation does not
- // support changing these, so they might as well be in a known state.
- dcb.fBinary = TRUE; // Win32 only supports binary mode.
- dcb.fDsrSensitivity = FALSE;
- dcb.fNull = FALSE; // Do not ignore NULL characters.
- dcb.fAbortOnError = FALSE; // Ignore serial framing errors.
- if (!::SetCommState(handle, &dcb))
- {
- DWORD last_error = ::GetLastError();
- ::CloseHandle(handle);
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- return ec;
- }
-
- // Set up timeouts so that the serial port will behave similarly to a
- // network socket. Reads wait for at least one byte, then return with
- // whatever they have. Writes return once everything is out the door.
- ::COMMTIMEOUTS timeouts;
- timeouts.ReadIntervalTimeout = 1;
- timeouts.ReadTotalTimeoutMultiplier = 0;
- timeouts.ReadTotalTimeoutConstant = 0;
- timeouts.WriteTotalTimeoutMultiplier = 0;
- timeouts.WriteTotalTimeoutConstant = 0;
- if (!::SetCommTimeouts(handle, &timeouts))
- {
- DWORD last_error = ::GetLastError();
- ::CloseHandle(handle);
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- return ec;
- }
-
- // We're done. Take ownership of the serial port handle.
- if (handle_service_.assign(impl, handle, ec))
- ::CloseHandle(handle);
- return ec;
- }
+ ASIO_DECL asio::error_code open(implementation_type& impl,
+ const std::string& device, asio::error_code& ec);
- // Assign a native handle to a handle implementation.
+ // Assign a native handle to a serial port implementation.
asio::error_code assign(implementation_type& impl,
const native_type& native_handle, asio::error_code& ec)
{
return handle_service_.assign(impl, native_handle, ec);
}
- // Determine whether the handle is open.
+ // Determine whether the serial port is open.
bool is_open(const implementation_type& impl) const
{
return handle_service_.is_open(impl);
}
- // Destroy a handle implementation.
+ // Destroy a serial port implementation.
asio::error_code close(implementation_type& impl,
asio::error_code& ec)
{
return handle_service_.close(impl, ec);
}
- // Get the native handle representation.
+ // Get the native serial port representation.
native_type native(implementation_type& impl)
{
return handle_service_.native(impl);
@@ -182,32 +101,9 @@ public:
asio::error_code set_option(implementation_type& impl,
const SettableSerialPortOption& option, asio::error_code& ec)
{
- using namespace std; // For memcpy.
-
- ::DCB dcb;
- memset(&dcb, 0, sizeof(DCB));
- dcb.DCBlength = sizeof(DCB);
- if (!::GetCommState(handle_service_.native(impl), &dcb))
- {
- DWORD last_error = ::GetLastError();
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- return ec;
- }
-
- if (option.store(dcb, ec))
- return ec;
-
- if (!::SetCommState(handle_service_.native(impl), &dcb))
- {
- DWORD last_error = ::GetLastError();
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- return ec;
- }
-
- ec = asio::error_code();
- return ec;
+ return do_set_option(impl,
+ &win_iocp_serial_port_service::store_option<SettableSerialPortOption>,
+ &option, ec);
}
// Get an option from the serial port.
@@ -215,20 +111,9 @@ public:
asio::error_code get_option(const implementation_type& impl,
GettableSerialPortOption& option, asio::error_code& ec) const
{
- using namespace std; // For memcpy.
-
- ::DCB dcb;
- memset(&dcb, 0, sizeof(DCB));
- dcb.DCBlength = sizeof(DCB);
- if (!::GetCommState(handle_service_.native(impl), &dcb))
- {
- DWORD last_error = ::GetLastError();
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- return ec;
- }
-
- return option.load(dcb, ec);
+ return do_get_option(impl,
+ &win_iocp_serial_port_service::load_option<GettableSerialPortOption>,
+ &option, ec);
}
// Send a break sequence to the serial port.
@@ -274,6 +159,41 @@ public:
}
private:
+ // Function pointer type for storing a serial port option.
+ typedef asio::error_code (*store_function_type)(
+ const void*, ::DCB&, asio::error_code&);
+
+ // Helper function template to store a serial port option.
+ template <typename SettableSerialPortOption>
+ static asio::error_code store_option(const void* option,
+ ::DCB& storage, asio::error_code& ec)
+ {
+ return static_cast<const SettableSerialPortOption*>(option)->store(
+ storage, ec);
+ }
+
+ // Helper function to set a serial port option.
+ ASIO_DECL asio::error_code do_set_option(
+ implementation_type& impl, store_function_type store,
+ const void* option, asio::error_code& ec);
+
+ // Function pointer type for loading a serial port option.
+ typedef asio::error_code (*load_function_type)(
+ void*, const ::DCB&, asio::error_code&);
+
+ // Helper function template to load a serial port option.
+ template <typename GettableSerialPortOption>
+ static asio::error_code load_option(void* option,
+ const ::DCB& storage, asio::error_code& ec)
+ {
+ return static_cast<GettableSerialPortOption*>(option)->load(storage, ec);
+ }
+
+ // Helper function to get a serial port option.
+ ASIO_DECL asio::error_code do_get_option(
+ const implementation_type& impl, load_function_type load,
+ void* option, asio::error_code& ec) const;
+
// The implementation used for initiating asynchronous operations.
win_iocp_handle_service handle_service_;
};
@@ -281,8 +201,12 @@ private:
} // namespace detail
} // namespace asio
-#endif // defined(ASIO_HAS_IOCP)
-
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/win_iocp_serial_port_service.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT)
+
#endif // ASIO_DETAIL_WIN_IOCP_SERIAL_PORT_SERVICE_HPP
diff --git a/ext/asio/asio/detail/win_iocp_socket_accept_op.hpp b/ext/asio/asio/detail/win_iocp_socket_accept_op.hpp
new file mode 100644
index 0000000..a71c7c0
--- /dev/null
+++ b/ext/asio/asio/detail/win_iocp_socket_accept_op.hpp
@@ -0,0 +1,158 @@
+//
+// detail/win_iocp_socket_accept_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_ACCEPT_OP_HPP
+#define ASIO_DETAIL_WIN_IOCP_SOCKET_ACCEPT_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/operation.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/win_iocp_socket_service_base.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename Socket, typename Protocol, typename Handler>
+class win_iocp_socket_accept_op : public operation
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_accept_op);
+
+ win_iocp_socket_accept_op(win_iocp_socket_service_base& socket_service,
+ socket_type socket, Socket& peer, const Protocol& protocol,
+ typename Protocol::endpoint* peer_endpoint,
+ bool enable_connection_aborted, Handler handler)
+ : operation(&win_iocp_socket_accept_op::do_complete),
+ socket_service_(socket_service),
+ socket_(socket),
+ peer_(peer),
+ protocol_(protocol),
+ peer_endpoint_(peer_endpoint),
+ enable_connection_aborted_(enable_connection_aborted),
+ handler_(handler)
+ {
+ }
+
+ socket_holder& new_socket()
+ {
+ return new_socket_;
+ }
+
+ void* output_buffer()
+ {
+ return output_buffer_;
+ }
+
+ DWORD address_length()
+ {
+ return sizeof(sockaddr_storage_type) + 16;
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code ec, std::size_t /*bytes_transferred*/)
+ {
+ // Take ownership of the operation object.
+ win_iocp_socket_accept_op* o(static_cast<win_iocp_socket_accept_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+ if (owner)
+ {
+ typename Protocol::endpoint peer_endpoint;
+ std::size_t addr_len = peer_endpoint.capacity();
+ socket_ops::complete_iocp_accept(o->socket_,
+ o->output_buffer(), o->address_length(),
+ peer_endpoint.data(), &addr_len,
+ o->new_socket_.get(), ec);
+
+ // Restart the accept operation if we got the connection_aborted error
+ // and the enable_connection_aborted socket option is not set.
+ if (ec == asio::error::connection_aborted
+ && !o->enable_connection_aborted_)
+ {
+ o->reset();
+ o->socket_service_.restart_accept_op(o->socket_,
+ o->new_socket_, o->protocol_.family(),
+ o->protocol_.type(), o->protocol_.protocol(),
+ o->output_buffer(), o->address_length(), o);
+ p.v = p.p = 0;
+ return;
+ }
+
+ // If the socket was successfully accepted, transfer ownership of the
+ // socket to the peer object.
+ if (!ec)
+ {
+ o->peer_.assign(o->protocol_,
+ typename Socket::native_type(
+ o->new_socket_.get(), peer_endpoint), ec);
+ if (!ec)
+ o->new_socket_.release();
+ }
+
+ // Pass endpoint back to caller.
+ if (o->peer_endpoint_)
+ *o->peer_endpoint_ = peer_endpoint;
+ }
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder1<Handler, asio::error_code>
+ handler(o->handler_, ec);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ win_iocp_socket_service_base& socket_service_;
+ socket_type socket_;
+ socket_holder new_socket_;
+ Socket& peer_;
+ Protocol protocol_;
+ typename Protocol::endpoint* peer_endpoint_;
+ unsigned char output_buffer_[(sizeof(sockaddr_storage_type) + 16) * 2];
+ bool enable_connection_aborted_;
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_IOCP)
+
+#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_ACCEPT_OP_HPP
diff --git a/ext/asio/asio/detail/win_iocp_socket_recv_op.hpp b/ext/asio/asio/detail/win_iocp_socket_recv_op.hpp
new file mode 100644
index 0000000..3ab4fcb
--- /dev/null
+++ b/ext/asio/asio/detail/win_iocp_socket_recv_op.hpp
@@ -0,0 +1,108 @@
+//
+// detail/win_iocp_socket_recv_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_RECV_OP_HPP
+#define ASIO_DETAIL_WIN_IOCP_SOCKET_RECV_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/operation.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename MutableBufferSequence, typename Handler>
+class win_iocp_socket_recv_op : public operation
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_recv_op);
+
+ win_iocp_socket_recv_op(socket_ops::state_type state,
+ socket_ops::weak_cancel_token_type cancel_token,
+ const MutableBufferSequence& buffers, Handler handler)
+ : operation(&win_iocp_socket_recv_op::do_complete),
+ state_(state),
+ cancel_token_(cancel_token),
+ buffers_(buffers),
+ handler_(handler)
+ {
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code ec, std::size_t bytes_transferred)
+ {
+ // Take ownership of the operation object.
+ win_iocp_socket_recv_op* o(static_cast<win_iocp_socket_recv_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
+ // Check whether buffers are still valid.
+ if (owner)
+ {
+ buffer_sequence_adapter<asio::mutable_buffer,
+ MutableBufferSequence>::validate(o->buffers_);
+ }
+#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING)
+
+ socket_ops::complete_iocp_recv(o->state_, o->cancel_token_,
+ buffer_sequence_adapter<asio::mutable_buffer,
+ MutableBufferSequence>::all_empty(o->buffers_),
+ ec, bytes_transferred);
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder2<Handler, asio::error_code, std::size_t>
+ handler(o->handler_, ec, bytes_transferred);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ socket_ops::state_type state_;
+ socket_ops::weak_cancel_token_type cancel_token_;
+ MutableBufferSequence buffers_;
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_IOCP)
+
+#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_RECV_OP_HPP
diff --git a/ext/asio/asio/detail/win_iocp_socket_recvfrom_op.hpp b/ext/asio/asio/detail/win_iocp_socket_recvfrom_op.hpp
new file mode 100644
index 0000000..d1e2ece
--- /dev/null
+++ b/ext/asio/asio/detail/win_iocp_socket_recvfrom_op.hpp
@@ -0,0 +1,116 @@
+//
+// detail/win_iocp_socket_recvfrom_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_RECVFROM_OP_HPP
+#define ASIO_DETAIL_WIN_IOCP_SOCKET_RECVFROM_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/operation.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename MutableBufferSequence, typename Endpoint, typename Handler>
+class win_iocp_socket_recvfrom_op : public operation
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_recvfrom_op);
+
+ win_iocp_socket_recvfrom_op(Endpoint& endpoint,
+ socket_ops::weak_cancel_token_type cancel_token,
+ const MutableBufferSequence& buffers, Handler handler)
+ : operation(&win_iocp_socket_recvfrom_op::do_complete),
+ endpoint_(endpoint),
+ endpoint_size_(static_cast<int>(endpoint.capacity())),
+ cancel_token_(cancel_token),
+ buffers_(buffers),
+ handler_(handler)
+ {
+ }
+
+ int& endpoint_size()
+ {
+ return endpoint_size_;
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code ec, std::size_t bytes_transferred)
+ {
+ // Take ownership of the operation object.
+ win_iocp_socket_recvfrom_op* o(
+ static_cast<win_iocp_socket_recvfrom_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
+ // Check whether buffers are still valid.
+ if (owner)
+ {
+ buffer_sequence_adapter<asio::mutable_buffer,
+ MutableBufferSequence>::validate(o->buffers_);
+ }
+#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING)
+
+ socket_ops::complete_iocp_recvfrom(o->cancel_token_, ec);
+
+ // Record the size of the endpoint returned by the operation.
+ o->endpoint_.resize(o->endpoint_size_);
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder2<Handler, asio::error_code, std::size_t>
+ handler(o->handler_, ec, bytes_transferred);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ Endpoint& endpoint_;
+ int endpoint_size_;
+ socket_ops::weak_cancel_token_type cancel_token_;
+ MutableBufferSequence buffers_;
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_IOCP)
+
+#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_RECVFROM_OP_HPP
diff --git a/ext/asio/asio/detail/win_iocp_socket_send_op.hpp b/ext/asio/asio/detail/win_iocp_socket_send_op.hpp
new file mode 100644
index 0000000..56f3f2d
--- /dev/null
+++ b/ext/asio/asio/detail/win_iocp_socket_send_op.hpp
@@ -0,0 +1,102 @@
+//
+// detail/win_iocp_socket_send_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_SEND_OP_HPP
+#define ASIO_DETAIL_WIN_IOCP_SOCKET_SEND_OP_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+
+#include <boost/utility/addressof.hpp>
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/operation.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+template <typename ConstBufferSequence, typename Handler>
+class win_iocp_socket_send_op : public operation
+{
+public:
+ ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_send_op);
+
+ win_iocp_socket_send_op(socket_ops::weak_cancel_token_type cancel_token,
+ const ConstBufferSequence& buffers, Handler handler)
+ : operation(&win_iocp_socket_send_op::do_complete),
+ cancel_token_(cancel_token),
+ buffers_(buffers),
+ handler_(handler)
+ {
+ }
+
+ static void do_complete(io_service_impl* owner, operation* base,
+ asio::error_code ec, std::size_t bytes_transferred)
+ {
+ // Take ownership of the operation object.
+ win_iocp_socket_send_op* o(static_cast<win_iocp_socket_send_op*>(base));
+ ptr p = { boost::addressof(o->handler_), o, o };
+
+#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
+ // Check whether buffers are still valid.
+ if (owner)
+ {
+ buffer_sequence_adapter<asio::const_buffer,
+ ConstBufferSequence>::validate(o->buffers_);
+ }
+#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING)
+
+ socket_ops::complete_iocp_send(o->cancel_token_, ec);
+
+ // Make a copy of the handler so that the memory can be deallocated before
+ // the upcall is made. Even if we're not about to make an upcall, a
+ // sub-object of the handler may be the true owner of the memory associated
+ // with the handler. Consequently, a local copy of the handler is required
+ // to ensure that any owning sub-object remains valid until after we have
+ // deallocated the memory here.
+ detail::binder2<Handler, asio::error_code, std::size_t>
+ handler(o->handler_, ec, bytes_transferred);
+ p.h = boost::addressof(handler.handler_);
+ p.reset();
+
+ // Make the upcall if required.
+ if (owner)
+ {
+ asio::detail::fenced_block b;
+ asio_handler_invoke_helpers::invoke(handler, handler.handler_);
+ }
+ }
+
+private:
+ socket_ops::weak_cancel_token_type cancel_token_;
+ ConstBufferSequence buffers_;
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_IOCP)
+
+#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SEND_OP_HPP
diff --git a/ext/asio/asio/detail/win_iocp_socket_service.hpp b/ext/asio/asio/detail/win_iocp_socket_service.hpp
index cb1d203..b476f8b 100644
--- a/ext/asio/asio/detail/win_iocp_socket_service.hpp
+++ b/ext/asio/asio/detail/win_iocp_socket_service.hpp
@@ -1,8 +1,8 @@
//
-// win_iocp_socket_service.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// detail/win_iocp_socket_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,19 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/win_iocp_io_service_fwd.hpp"
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_IOCP)
-#include "asio/detail/push_options.hpp"
#include <cstring>
-#include <boost/shared_ptr.hpp>
-#include <boost/type_traits/is_same.hpp>
-#include <boost/weak_ptr.hpp>
-#include "asio/detail/pop_options.hpp"
-
+#include <boost/utility/addressof.hpp>
#include "asio/error.hpp"
#include "asio/io_service.hpp"
#include "asio/socket_base.hpp"
@@ -37,20 +30,27 @@
#include "asio/detail/handler_alloc_helpers.hpp"
#include "asio/detail/handler_invoke_helpers.hpp"
#include "asio/detail/mutex.hpp"
-#include "asio/detail/null_buffers_op.hpp"
#include "asio/detail/operation.hpp"
+#include "asio/detail/reactive_socket_connect_op.hpp"
#include "asio/detail/reactor.hpp"
#include "asio/detail/reactor_op.hpp"
#include "asio/detail/socket_holder.hpp"
#include "asio/detail/socket_ops.hpp"
#include "asio/detail/socket_types.hpp"
#include "asio/detail/win_iocp_io_service.hpp"
+#include "asio/detail/win_iocp_null_buffers_op.hpp"
+#include "asio/detail/win_iocp_socket_accept_op.hpp"
+#include "asio/detail/win_iocp_socket_recvfrom_op.hpp"
+#include "asio/detail/win_iocp_socket_send_op.hpp"
+#include "asio/detail/win_iocp_socket_service_base.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
template <typename Protocol>
-class win_iocp_socket_service
+class win_iocp_socket_service : public win_iocp_socket_service_base
{
public:
// The protocol type.
@@ -59,10 +59,6 @@ public:
// The endpoint type.
typedef typename Protocol::endpoint endpoint_type;
- struct noop_deleter { void operator()(void*) {} };
- typedef boost::shared_ptr<void> shared_cancel_token_type;
- typedef boost::weak_ptr<void> weak_cancel_token_type;
-
// The native type of a socket.
class native_type
{
@@ -92,11 +88,6 @@ public:
return socket_;
}
- HANDLE as_handle() const
- {
- return reinterpret_cast<HANDLE>(socket_);
- }
-
bool have_remote_endpoint() const
{
return have_remote_endpoint_;
@@ -114,148 +105,44 @@ public:
};
// The implementation type of the socket.
- class implementation_type
+ struct implementation_type :
+ win_iocp_socket_service_base::base_implementation_type
{
- public:
// Default constructor.
implementation_type()
- : socket_(invalid_socket),
- flags_(0),
- cancel_token_(),
- protocol_(endpoint_type().protocol()),
- next_(0),
- prev_(0)
+ : protocol_(endpoint_type().protocol()),
+ have_remote_endpoint_(false),
+ remote_endpoint_()
{
}
- private:
- // Only this service will have access to the internal values.
- friend class win_iocp_socket_service;
-
- // The native socket representation.
- native_type socket_;
-
- enum
- {
- enable_connection_aborted = 1, // User wants connection_aborted errors.
- close_might_block = 2, // User set linger option for blocking close.
- user_set_non_blocking = 4 // The user wants a non-blocking socket.
- };
-
- // Flags indicating the current state of the socket.
- unsigned char flags_;
-
- // We use a shared pointer as a cancellation token here to work around the
- // broken Windows support for cancellation. MSDN says that when you call
- // closesocket any outstanding WSARecv or WSASend operations will complete
- // with the error ERROR_OPERATION_ABORTED. In practice they complete with
- // ERROR_NETNAME_DELETED, which means you can't tell the difference between
- // a local cancellation and the socket being hard-closed by the peer.
- shared_cancel_token_type cancel_token_;
-
// The protocol associated with the socket.
protocol_type protocol_;
- // Per-descriptor data used by the reactor.
- reactor::per_descriptor_data reactor_data_;
-
-#if defined(ASIO_ENABLE_CANCELIO)
- // The ID of the thread from which it is safe to cancel asynchronous
- // operations. 0 means no asynchronous operations have been started yet.
- // ~0 means asynchronous operations have been started from more than one
- // thread, and cancellation is not supported for the socket.
- DWORD safe_cancellation_thread_id_;
-#endif // defined(ASIO_ENABLE_CANCELIO)
+ // Whether we have a cached remote endpoint.
+ bool have_remote_endpoint_;
- // Pointers to adjacent socket implementations in linked list.
- implementation_type* next_;
- implementation_type* prev_;
+ // A cached remote endpoint.
+ endpoint_type remote_endpoint_;
};
// Constructor.
win_iocp_socket_service(asio::io_service& io_service)
- : io_service_(io_service),
- iocp_service_(use_service<win_iocp_io_service>(io_service)),
- reactor_(0),
- mutex_(),
- impl_list_(0)
- {
- }
-
- // Destroy all user-defined handler objects owned by the service.
- void shutdown_service()
+ : win_iocp_socket_service_base(io_service)
{
- // Close all implementations, causing all operations to complete.
- asio::detail::mutex::scoped_lock lock(mutex_);
- implementation_type* impl = impl_list_;
- while (impl)
- {
- asio::error_code ignored_ec;
- close_for_destruction(*impl);
- impl = impl->next_;
- }
- }
-
- // Construct a new socket implementation.
- void construct(implementation_type& impl)
- {
- impl.socket_ = invalid_socket;
- impl.flags_ = 0;
- impl.cancel_token_.reset();
-#if defined(ASIO_ENABLE_CANCELIO)
- impl.safe_cancellation_thread_id_ = 0;
-#endif // defined(ASIO_ENABLE_CANCELIO)
-
- // Insert implementation into linked list of all implementations.
- asio::detail::mutex::scoped_lock lock(mutex_);
- impl.next_ = impl_list_;
- impl.prev_ = 0;
- if (impl_list_)
- impl_list_->prev_ = &impl;
- impl_list_ = &impl;
- }
-
- // Destroy a socket implementation.
- void destroy(implementation_type& impl)
- {
- close_for_destruction(impl);
-
- // Remove implementation from linked list of all implementations.
- asio::detail::mutex::scoped_lock lock(mutex_);
- if (impl_list_ == &impl)
- impl_list_ = impl.next_;
- if (impl.prev_)
- impl.prev_->next_ = impl.next_;
- if (impl.next_)
- impl.next_->prev_= impl.prev_;
- impl.next_ = 0;
- impl.prev_ = 0;
}
// Open a new socket implementation.
asio::error_code open(implementation_type& impl,
const protocol_type& protocol, asio::error_code& ec)
{
- if (is_open(impl))
+ if (!do_open(impl, protocol.family(),
+ protocol.type(), protocol.protocol(), ec))
{
- ec = asio::error::already_open;
- return ec;
+ impl.protocol_ = protocol;
+ impl.have_remote_endpoint_ = false;
+ impl.remote_endpoint_ = endpoint_type();
}
-
- socket_holder sock(socket_ops::socket(protocol.family(), protocol.type(),
- protocol.protocol(), ec));
- if (sock.get() == invalid_socket)
- return ec;
-
- HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock.get());
- if (iocp_service_.register_handle(sock_as_handle, ec))
- return ec;
-
- impl.socket_ = sock.release();
- impl.flags_ = 0;
- impl.cancel_token_.reset(static_cast<void*>(0), noop_deleter());
- impl.protocol_ = protocol;
- ec = asio::error_code();
return ec;
}
@@ -264,247 +151,40 @@ public:
const protocol_type& protocol, const native_type& native_socket,
asio::error_code& ec)
{
- if (is_open(impl))
- {
- ec = asio::error::already_open;
- return ec;
- }
-
- if (iocp_service_.register_handle(native_socket.as_handle(), ec))
- return ec;
-
- impl.socket_ = native_socket;
- impl.flags_ = 0;
- impl.cancel_token_.reset(static_cast<void*>(0), noop_deleter());
- impl.protocol_ = protocol;
- ec = asio::error_code();
- return ec;
- }
-
- // Determine whether the socket is open.
- bool is_open(const implementation_type& impl) const
- {
- return impl.socket_ != invalid_socket;
- }
-
- // Destroy a socket implementation.
- asio::error_code close(implementation_type& impl,
- asio::error_code& ec)
- {
- if (is_open(impl))
+ if (!do_assign(impl, protocol.type(), native_socket, ec))
{
- // Check if the reactor was created, in which case we need to close the
- // socket on the reactor as well to cancel any operations that might be
- // running there.
- reactor* r = static_cast<reactor*>(
- interlocked_compare_exchange_pointer(
- reinterpret_cast<void**>(&reactor_), 0, 0));
- if (r)
- r->close_descriptor(impl.socket_, impl.reactor_data_);
-
- if (socket_ops::close(impl.socket_, ec) == socket_error_retval)
- return ec;
-
- impl.socket_ = invalid_socket;
- impl.flags_ = 0;
- impl.cancel_token_.reset();
-#if defined(ASIO_ENABLE_CANCELIO)
- impl.safe_cancellation_thread_id_ = 0;
-#endif // defined(ASIO_ENABLE_CANCELIO)
+ impl.protocol_ = protocol;
+ impl.have_remote_endpoint_ = native_socket.have_remote_endpoint();
+ impl.remote_endpoint_ = native_socket.remote_endpoint();
}
-
- ec = asio::error_code();
return ec;
}
// Get the native socket representation.
native_type native(implementation_type& impl)
{
- return impl.socket_;
- }
-
- // Cancel all operations associated with the socket.
- asio::error_code cancel(implementation_type& impl,
- asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
- else if (FARPROC cancel_io_ex_ptr = ::GetProcAddress(
- ::GetModuleHandleA("KERNEL32"), "CancelIoEx"))
- {
- // The version of Windows supports cancellation from any thread.
- typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED);
- cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr;
- socket_type sock = impl.socket_;
- HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock);
- if (!cancel_io_ex(sock_as_handle, 0))
- {
- DWORD last_error = ::GetLastError();
- if (last_error == ERROR_NOT_FOUND)
- {
- // ERROR_NOT_FOUND means that there were no operations to be
- // cancelled. We swallow this error to match the behaviour on other
- // platforms.
- ec = asio::error_code();
- }
- else
- {
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- }
- }
- else
- {
- ec = asio::error_code();
- }
- }
-#if defined(ASIO_ENABLE_CANCELIO)
- else if (impl.safe_cancellation_thread_id_ == 0)
- {
- // No operations have been started, so there's nothing to cancel.
- ec = asio::error_code();
- }
- else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId())
- {
- // Asynchronous operations have been started from the current thread only,
- // so it is safe to try to cancel them using CancelIo.
- socket_type sock = impl.socket_;
- HANDLE sock_as_handle = reinterpret_cast<HANDLE>(sock);
- if (!::CancelIo(sock_as_handle))
- {
- DWORD last_error = ::GetLastError();
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- }
- else
- {
- ec = asio::error_code();
- }
- }
- else
- {
- // Asynchronous operations have been started from more than one thread,
- // so cancellation is not safe.
- ec = asio::error::operation_not_supported;
- }
-#else // defined(ASIO_ENABLE_CANCELIO)
- else
- {
- // Cancellation is not supported as CancelIo may not be used.
- ec = asio::error::operation_not_supported;
- }
-#endif // defined(ASIO_ENABLE_CANCELIO)
-
- return ec;
- }
-
- // Determine whether the socket is at the out-of-band data mark.
- bool at_mark(const implementation_type& impl,
- asio::error_code& ec) const
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return false;
- }
-
- asio::detail::ioctl_arg_type value = 0;
- socket_ops::ioctl(impl.socket_, SIOCATMARK, &value, ec);
- return ec ? false : value != 0;
- }
-
- // Determine the number of bytes available for reading.
- std::size_t available(const implementation_type& impl,
- asio::error_code& ec) const
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
- asio::detail::ioctl_arg_type value = 0;
- socket_ops::ioctl(impl.socket_, FIONREAD, &value, ec);
- return ec ? static_cast<std::size_t>(0) : static_cast<std::size_t>(value);
+ if (impl.have_remote_endpoint_)
+ return native_type(impl.socket_, impl.remote_endpoint_);
+ return native_type(impl.socket_);
}
// Bind the socket to the specified local endpoint.
asio::error_code bind(implementation_type& impl,
const endpoint_type& endpoint, asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec);
return ec;
}
- // Place the socket into the state where it will listen for new connections.
- asio::error_code listen(implementation_type& impl, int backlog,
- asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
- socket_ops::listen(impl.socket_, backlog, ec);
- return ec;
- }
-
// Set a socket option.
template <typename Option>
asio::error_code set_option(implementation_type& impl,
const Option& option, asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
- if (option.level(impl.protocol_) == custom_socket_option_level
- && option.name(impl.protocol_) == enable_connection_aborted_option)
- {
- if (option.size(impl.protocol_) != sizeof(int))
- {
- ec = asio::error::invalid_argument;
- }
- else
- {
- if (*reinterpret_cast<const int*>(option.data(impl.protocol_)))
- impl.flags_ |= implementation_type::enable_connection_aborted;
- else
- impl.flags_ &= ~implementation_type::enable_connection_aborted;
- ec = asio::error_code();
- }
- return ec;
- }
- else
- {
- if (option.level(impl.protocol_) == SOL_SOCKET
- && option.name(impl.protocol_) == SO_LINGER)
- {
- const ::linger* linger_option =
- reinterpret_cast<const ::linger*>(option.data(impl.protocol_));
- if (linger_option->l_onoff != 0 && linger_option->l_linger != 0)
- impl.flags_ |= implementation_type::close_might_block;
- else
- impl.flags_ &= ~implementation_type::close_might_block;
- }
-
- socket_ops::setsockopt(impl.socket_,
- option.level(impl.protocol_), option.name(impl.protocol_),
- option.data(impl.protocol_), option.size(impl.protocol_), ec);
- return ec;
- }
+ socket_ops::setsockopt(impl.socket_, impl.state_,
+ option.level(impl.protocol_), option.name(impl.protocol_),
+ option.data(impl.protocol_), option.size(impl.protocol_), ec);
+ return ec;
}
// Set a socket option.
@@ -512,65 +192,12 @@ public:
asio::error_code get_option(const implementation_type& impl,
Option& option, asio::error_code& ec) const
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
- if (option.level(impl.protocol_) == custom_socket_option_level
- && option.name(impl.protocol_) == enable_connection_aborted_option)
- {
- if (option.size(impl.protocol_) != sizeof(int))
- {
- ec = asio::error::invalid_argument;
- }
- else
- {
- int* target = reinterpret_cast<int*>(option.data(impl.protocol_));
- if (impl.flags_ & implementation_type::enable_connection_aborted)
- *target = 1;
- else
- *target = 0;
- option.resize(impl.protocol_, sizeof(int));
- ec = asio::error_code();
- }
- return ec;
- }
- else
- {
- size_t size = option.size(impl.protocol_);
- socket_ops::getsockopt(impl.socket_,
- option.level(impl.protocol_), option.name(impl.protocol_),
- option.data(impl.protocol_), &size, ec);
- if (!ec)
- option.resize(impl.protocol_, size);
- return ec;
- }
- }
-
- // Perform an IO control command on the socket.
- template <typename IO_Control_Command>
- asio::error_code io_control(implementation_type& impl,
- IO_Control_Command& command, asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
- socket_ops::ioctl(impl.socket_, command.name(),
- static_cast<ioctl_arg_type*>(command.data()), ec);
-
- if (!ec && command.name() == static_cast<int>(FIONBIO))
- {
- if (*static_cast<ioctl_arg_type*>(command.data()))
- impl.flags_ |= implementation_type::user_set_non_blocking;
- else
- impl.flags_ &= ~implementation_type::user_set_non_blocking;
- }
-
+ std::size_t size = option.size(impl.protocol_);
+ socket_ops::getsockopt(impl.socket_, impl.state_,
+ option.level(impl.protocol_), option.name(impl.protocol_),
+ option.data(impl.protocol_), &size, ec);
+ if (!ec)
+ option.resize(impl.protocol_, size);
return ec;
}
@@ -578,12 +205,6 @@ public:
endpoint_type local_endpoint(const implementation_type& impl,
asio::error_code& ec) const
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return endpoint_type();
- }
-
endpoint_type endpoint;
std::size_t addr_len = endpoint.capacity();
if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec))
@@ -596,210 +217,13 @@ public:
endpoint_type remote_endpoint(const implementation_type& impl,
asio::error_code& ec) const
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
+ endpoint_type endpoint = impl.remote_endpoint_;
+ std::size_t addr_len = endpoint.capacity();
+ if (socket_ops::getpeername(impl.socket_, endpoint.data(),
+ &addr_len, impl.have_remote_endpoint_, ec))
return endpoint_type();
- }
-
- if (impl.socket_.have_remote_endpoint())
- {
- // Check if socket is still connected.
- DWORD connect_time = 0;
- size_t connect_time_len = sizeof(connect_time);
- if (socket_ops::getsockopt(impl.socket_, SOL_SOCKET, SO_CONNECT_TIME,
- &connect_time, &connect_time_len, ec) == socket_error_retval)
- {
- return endpoint_type();
- }
- if (connect_time == 0xFFFFFFFF)
- {
- ec = asio::error::not_connected;
- return endpoint_type();
- }
-
- ec = asio::error_code();
- return impl.socket_.remote_endpoint();
- }
- else
- {
- endpoint_type endpoint;
- std::size_t addr_len = endpoint.capacity();
- if (socket_ops::getpeername(impl.socket_, endpoint.data(), &addr_len, ec))
- return endpoint_type();
- endpoint.resize(addr_len);
- return endpoint;
- }
- }
-
- /// Disable sends or receives on the socket.
- asio::error_code shutdown(implementation_type& impl,
- socket_base::shutdown_type what, asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
- socket_ops::shutdown(impl.socket_, what, ec);
- return ec;
- }
-
- // Send the given data to the peer. Returns the number of bytes sent.
- template <typename ConstBufferSequence>
- size_t send(implementation_type& impl, const ConstBufferSequence& buffers,
- socket_base::message_flags flags, asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
- buffer_sequence_adapter<asio::const_buffer,
- ConstBufferSequence> bufs(buffers);
-
- // A request to receive 0 bytes on a stream socket is a no-op.
- if (impl.protocol_.type() == SOCK_STREAM && bufs.all_empty())
- {
- ec = asio::error_code();
- return 0;
- }
-
- // Send the data.
- DWORD bytes_transferred = 0;
- int result = ::WSASend(impl.socket_, bufs.buffers(),
- bufs.count(), &bytes_transferred, flags, 0, 0);
- if (result != 0)
- {
- DWORD last_error = ::WSAGetLastError();
- if (last_error == ERROR_NETNAME_DELETED)
- last_error = WSAECONNRESET;
- else if (last_error == ERROR_PORT_UNREACHABLE)
- last_error = WSAECONNREFUSED;
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- return 0;
- }
-
- ec = asio::error_code();
- return bytes_transferred;
- }
-
- // Wait until data can be sent without blocking.
- size_t send(implementation_type& impl, const null_buffers&,
- socket_base::message_flags, asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
- // Wait for socket to become ready.
- socket_ops::poll_write(impl.socket_, ec);
-
- return 0;
- }
-
- template <typename ConstBufferSequence, typename Handler>
- class send_op : public operation
- {
- public:
- send_op(weak_cancel_token_type cancel_token,
- const ConstBufferSequence& buffers, Handler handler)
- : operation(&send_op::do_complete),
- cancel_token_(cancel_token),
- buffers_(buffers),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code ec, std::size_t bytes_transferred)
- {
- // Take ownership of the operation object.
- send_op* o(static_cast<send_op*>(base));
- typedef handler_alloc_traits<Handler, send_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
-#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
- // Check whether buffers are still valid.
- buffer_sequence_adapter<asio::const_buffer,
- ConstBufferSequence>::validate(o->buffers_);
-#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING)
-
- // Map non-portable errors to their portable counterparts.
- if (ec.value() == ERROR_NETNAME_DELETED)
- {
- if (o->cancel_token_.expired())
- ec = asio::error::operation_aborted;
- else
- ec = asio::error::connection_reset;
- }
- else if (ec.value() == ERROR_PORT_UNREACHABLE)
- {
- ec = asio::error::connection_refused;
- }
-
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder2<Handler, asio::error_code, std::size_t>
- handler(o->handler_, ec, bytes_transferred);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- weak_cancel_token_type cancel_token_;
- ConstBufferSequence buffers_;
- Handler handler_;
- };
-
- // Start an asynchronous send. The data being sent must be valid for the
- // lifetime of the asynchronous operation.
- template <typename ConstBufferSequence, typename Handler>
- void async_send(implementation_type& impl, const ConstBufferSequence& buffers,
- socket_base::message_flags flags, Handler handler)
- {
- // Allocate and construct an operation to wrap the handler.
- typedef send_op<ConstBufferSequence, Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr,
- impl.cancel_token_, buffers, handler);
-
- buffer_sequence_adapter<asio::const_buffer,
- ConstBufferSequence> bufs(buffers);
-
- start_send_op(impl, bufs.buffers(), bufs.count(), flags,
- impl.protocol_.type() == SOCK_STREAM && bufs.all_empty(), ptr.get());
- ptr.release();
- }
-
- // Start an asynchronous wait until data can be sent without blocking.
- template <typename Handler>
- void async_send(implementation_type& impl, const null_buffers&,
- socket_base::message_flags, Handler handler)
- {
- // Allocate and construct an operation to wrap the handler.
- typedef null_buffers_op<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, handler);
-
- start_reactor_op(impl, reactor::write_op, ptr.get());
- ptr.release();
+ endpoint.resize(addr_len);
+ return endpoint;
}
// Send a datagram to the specified endpoint. Returns the number of bytes
@@ -809,107 +233,25 @@ public:
const endpoint_type& destination, socket_base::message_flags flags,
asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
buffer_sequence_adapter<asio::const_buffer,
ConstBufferSequence> bufs(buffers);
- // Send the data.
- DWORD bytes_transferred = 0;
- int result = ::WSASendTo(impl.socket_, bufs.buffers(), bufs.count(),
- &bytes_transferred, flags, destination.data(),
- static_cast<int>(destination.size()), 0, 0);
- if (result != 0)
- {
- DWORD last_error = ::WSAGetLastError();
- if (last_error == ERROR_PORT_UNREACHABLE)
- last_error = WSAECONNREFUSED;
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- return 0;
- }
-
- ec = asio::error_code();
- return bytes_transferred;
+ return socket_ops::sync_sendto(impl.socket_, impl.state_,
+ bufs.buffers(), bufs.count(), flags,
+ destination.data(), destination.size(), ec);
}
// Wait until data can be sent without blocking.
size_t send_to(implementation_type& impl, const null_buffers&,
- socket_base::message_flags, const endpoint_type&,
+ const endpoint_type&, socket_base::message_flags,
asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
// Wait for socket to become ready.
socket_ops::poll_write(impl.socket_, ec);
return 0;
}
- template <typename ConstBufferSequence, typename Handler>
- class send_to_op : public operation
- {
- public:
- send_to_op(weak_cancel_token_type cancel_token,
- const ConstBufferSequence& buffers, Handler handler)
- : operation(&send_to_op::do_complete),
- cancel_token_(cancel_token),
- buffers_(buffers),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code ec, std::size_t bytes_transferred)
- {
- // Take ownership of the operation object.
- send_to_op* o(static_cast<send_to_op*>(base));
- typedef handler_alloc_traits<Handler, send_to_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
-#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
- // Check whether buffers are still valid.
- buffer_sequence_adapter<asio::const_buffer,
- ConstBufferSequence>::validate(o->buffers_);
-#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING)
-
- // Map non-portable errors to their portable counterparts.
- if (ec.value() == ERROR_PORT_UNREACHABLE)
- {
- ec = asio::error::connection_refused;
- }
-
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder2<Handler, asio::error_code, std::size_t>
- handler(o->handler_, ec, bytes_transferred);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- weak_cancel_token_type cancel_token_;
- ConstBufferSequence buffers_;
- Handler handler_;
- };
-
// Start an asynchronous send. The data being sent must be valid for the
// lifetime of the asynchronous operation.
template <typename ConstBufferSequence, typename Handler>
@@ -918,233 +260,35 @@ public:
socket_base::message_flags flags, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef send_to_op<ConstBufferSequence, Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr,
- impl.cancel_token_, buffers, handler);
+ typedef win_iocp_socket_send_op<ConstBufferSequence, Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl.cancel_token_, buffers, handler);
buffer_sequence_adapter<asio::const_buffer,
ConstBufferSequence> bufs(buffers);
- start_send_to_op(impl, bufs.buffers(),
- bufs.count(), destination, flags, ptr.get());
- ptr.release();
+ start_send_to_op(impl, bufs.buffers(), bufs.count(),
+ destination.data(), static_cast<int>(destination.size()),
+ flags, p.p);
+ p.v = p.p = 0;
}
// Start an asynchronous wait until data can be sent without blocking.
template <typename Handler>
void async_send_to(implementation_type& impl, const null_buffers&,
- socket_base::message_flags, const endpoint_type&, Handler handler)
- {
- // Allocate and construct an operation to wrap the handler.
- typedef null_buffers_op<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, handler);
-
- start_reactor_op(impl, reactor::write_op, ptr.get());
- ptr.release();
- }
-
- // Receive some data from the peer. Returns the number of bytes received.
- template <typename MutableBufferSequence>
- size_t receive(implementation_type& impl,
- const MutableBufferSequence& buffers,
- socket_base::message_flags flags, asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
- buffer_sequence_adapter<asio::mutable_buffer,
- MutableBufferSequence> bufs(buffers);
-
- // A request to receive 0 bytes on a stream socket is a no-op.
- if (impl.protocol_.type() == SOCK_STREAM && bufs.all_empty())
- {
- ec = asio::error_code();
- return 0;
- }
-
- // Receive some data.
- DWORD bytes_transferred = 0;
- DWORD recv_flags = flags;
- int result = ::WSARecv(impl.socket_, bufs.buffers(),
- bufs.count(), &bytes_transferred, &recv_flags, 0, 0);
- if (result != 0)
- {
- DWORD last_error = ::WSAGetLastError();
- if (last_error == ERROR_NETNAME_DELETED)
- last_error = WSAECONNRESET;
- else if (last_error == ERROR_PORT_UNREACHABLE)
- last_error = WSAECONNREFUSED;
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- return 0;
- }
- if (bytes_transferred == 0 && impl.protocol_.type() == SOCK_STREAM)
- {
- ec = asio::error::eof;
- return 0;
- }
-
- ec = asio::error_code();
- return bytes_transferred;
- }
-
- // Wait until data can be received without blocking.
- size_t receive(implementation_type& impl, const null_buffers&,
- socket_base::message_flags, asio::error_code& ec)
- {
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
- // Wait for socket to become ready.
- socket_ops::poll_read(impl.socket_, ec);
-
- return 0;
- }
-
- template <typename MutableBufferSequence, typename Handler>
- class receive_op : public operation
- {
- public:
- receive_op(int protocol_type, weak_cancel_token_type cancel_token,
- const MutableBufferSequence& buffers, Handler handler)
- : operation(&receive_op::do_complete),
- protocol_type_(protocol_type),
- cancel_token_(cancel_token),
- buffers_(buffers),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code ec, std::size_t bytes_transferred)
- {
- // Take ownership of the operation object.
- receive_op* o(static_cast<receive_op*>(base));
- typedef handler_alloc_traits<Handler, receive_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
-#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
- // Check whether buffers are still valid.
- buffer_sequence_adapter<asio::mutable_buffer,
- MutableBufferSequence>::validate(o->buffers_);
-#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING)
-
- // Map non-portable errors to their portable counterparts.
- if (ec.value() == ERROR_NETNAME_DELETED)
- {
- if (o->cancel_token_.expired())
- ec = asio::error::operation_aborted;
- else
- ec = asio::error::connection_reset;
- }
- else if (ec.value() == ERROR_PORT_UNREACHABLE)
- {
- ec = asio::error::connection_refused;
- }
-
- // Check for connection closed.
- else if (!ec && bytes_transferred == 0
- && o->protocol_type_ == SOCK_STREAM
- && !buffer_sequence_adapter<asio::mutable_buffer,
- MutableBufferSequence>::all_empty(o->buffers_)
- && !boost::is_same<MutableBufferSequence, null_buffers>::value)
- {
- ec = asio::error::eof;
- }
-
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder2<Handler, asio::error_code, std::size_t>
- handler(o->handler_, ec, bytes_transferred);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- int protocol_type_;
- weak_cancel_token_type cancel_token_;
- MutableBufferSequence buffers_;
- Handler handler_;
- };
-
- // Start an asynchronous receive. The buffer for the data being received
- // must be valid for the lifetime of the asynchronous operation.
- template <typename MutableBufferSequence, typename Handler>
- void async_receive(implementation_type& impl,
- const MutableBufferSequence& buffers,
- socket_base::message_flags flags, Handler handler)
+ const endpoint_type&, socket_base::message_flags, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef receive_op<MutableBufferSequence, Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- int protocol_type = impl.protocol_.type();
- handler_ptr<alloc_traits> ptr(raw_ptr, protocol_type,
- impl.cancel_token_, buffers, handler);
-
- buffer_sequence_adapter<asio::mutable_buffer,
- MutableBufferSequence> bufs(buffers);
+ typedef win_iocp_null_buffers_op<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl.cancel_token_, handler);
- start_receive_op(impl, bufs.buffers(), bufs.count(), flags,
- protocol_type == SOCK_STREAM && bufs.all_empty(), ptr.get());
- ptr.release();
- }
-
- // Wait until data can be received without blocking.
- template <typename Handler>
- void async_receive(implementation_type& impl, const null_buffers& buffers,
- socket_base::message_flags flags, Handler handler)
- {
- if (impl.protocol_.type() == SOCK_STREAM)
- {
- // For stream sockets on Windows, we may issue a 0-byte overlapped
- // WSARecv to wait until there is data available on the socket.
-
- // Allocate and construct an operation to wrap the handler.
- typedef receive_op<null_buffers, Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- int protocol_type = impl.protocol_.type();
- handler_ptr<alloc_traits> ptr(raw_ptr, protocol_type,
- impl.cancel_token_, buffers, handler);
-
- ::WSABUF buf = { 0, 0 };
- start_receive_op(impl, &buf, 1, flags, false, ptr.get());
- ptr.release();
- }
- else
- {
- // Allocate and construct an operation to wrap the handler.
- typedef null_buffers_op<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, handler);
-
- start_reactor_op(impl,
- (flags & socket_base::message_out_of_band)
- ? reactor::except_op : reactor::read_op,
- ptr.get());
- ptr.release();
- }
+ start_reactor_op(impl, reactor::write_op, p.p);
+ p.v = p.p = 0;
}
// Receive a datagram with the endpoint of the sender. Returns the number of
@@ -1155,41 +299,18 @@ public:
endpoint_type& sender_endpoint, socket_base::message_flags flags,
asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
buffer_sequence_adapter<asio::mutable_buffer,
MutableBufferSequence> bufs(buffers);
- // Receive some data.
- DWORD bytes_transferred = 0;
- DWORD recv_flags = flags;
- int endpoint_size = static_cast<int>(sender_endpoint.capacity());
- int result = ::WSARecvFrom(impl.socket_, bufs.buffers(),
- bufs.count(), &bytes_transferred, &recv_flags,
- sender_endpoint.data(), &endpoint_size, 0, 0);
- if (result != 0)
- {
- DWORD last_error = ::WSAGetLastError();
- if (last_error == ERROR_PORT_UNREACHABLE)
- last_error = WSAECONNREFUSED;
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
- return 0;
- }
- if (bytes_transferred == 0 && impl.protocol_.type() == SOCK_STREAM)
- {
- ec = asio::error::eof;
- return 0;
- }
+ std::size_t addr_len = sender_endpoint.capacity();
+ std::size_t bytes_recvd = socket_ops::sync_recvfrom(
+ impl.socket_, impl.state_, bufs.buffers(), bufs.count(),
+ flags, sender_endpoint.data(), &addr_len, ec);
- sender_endpoint.resize(static_cast<std::size_t>(endpoint_size));
+ if (!ec)
+ sender_endpoint.resize(addr_len);
- ec = asio::error_code();
- return bytes_transferred;
+ return bytes_recvd;
}
// Wait until data can be received without blocking.
@@ -1197,12 +318,6 @@ public:
const null_buffers&, endpoint_type& sender_endpoint,
socket_base::message_flags, asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return 0;
- }
-
// Wait for socket to become ready.
socket_ops::poll_read(impl.socket_, ec);
@@ -1212,75 +327,6 @@ public:
return 0;
}
- template <typename MutableBufferSequence, typename Handler>
- class receive_from_op : public operation
- {
- public:
- receive_from_op(int protocol_type, endpoint_type& endpoint,
- const MutableBufferSequence& buffers, Handler handler)
- : operation(&receive_from_op::do_complete),
- protocol_type_(protocol_type),
- endpoint_(endpoint),
- endpoint_size_(static_cast<int>(endpoint.capacity())),
- buffers_(buffers),
- handler_(handler)
- {
- }
-
- int& endpoint_size()
- {
- return endpoint_size_;
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code ec, std::size_t bytes_transferred)
- {
- // Take ownership of the operation object.
- receive_from_op* o(static_cast<receive_from_op*>(base));
- typedef handler_alloc_traits<Handler, receive_from_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
-#if defined(ASIO_ENABLE_BUFFER_DEBUGGING)
- // Check whether buffers are still valid.
- buffer_sequence_adapter<asio::mutable_buffer,
- MutableBufferSequence>::validate(o->buffers_);
-#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING)
-
- // Map non-portable errors to their portable counterparts.
- if (ec.value() == ERROR_PORT_UNREACHABLE)
- {
- ec = asio::error::connection_refused;
- }
-
- // Record the size of the endpoint returned by the operation.
- o->endpoint_.resize(o->endpoint_size_);
-
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder2<Handler, asio::error_code, std::size_t>
- handler(o->handler_, ec, bytes_transferred);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- int protocol_type_;
- endpoint_type& endpoint_;
- int endpoint_size_;
- weak_cancel_token_type cancel_token_;
- MutableBufferSequence buffers_;
- Handler handler_;
- };
-
// Start an asynchronous receive. The buffer for the data being received and
// the sender_endpoint object must both be valid for the lifetime of the
// asynchronous operation.
@@ -1290,19 +336,19 @@ public:
socket_base::message_flags flags, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef receive_from_op<MutableBufferSequence, Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- int protocol_type = impl.protocol_.type();
- handler_ptr<alloc_traits> ptr(raw_ptr,
- protocol_type, sender_endp, buffers, handler);
+ typedef win_iocp_socket_recvfrom_op<
+ MutableBufferSequence, endpoint_type, Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(sender_endp, impl.cancel_token_, buffers, handler);
buffer_sequence_adapter<asio::mutable_buffer,
MutableBufferSequence> bufs(buffers);
start_receive_from_op(impl, bufs.buffers(), bufs.count(),
- sender_endp, flags, &ptr.get()->endpoint_size(), ptr.get());
- ptr.release();
+ sender_endp.data(), flags, &p.p->endpoint_size(), p.p);
+ p.v = p.p = 0;
}
// Wait until data can be received without blocking.
@@ -1312,19 +358,17 @@ public:
socket_base::message_flags flags, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef null_buffers_op<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, handler);
+ typedef win_iocp_null_buffers_op<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl.cancel_token_, handler);
// Reset endpoint since it can be given no sensible value at this time.
sender_endpoint = endpoint_type();
- start_reactor_op(impl,
- (flags & socket_base::message_out_of_band)
- ? reactor::except_op : reactor::read_op,
- ptr.get());
- ptr.release();
+ start_null_buffers_receive_op(impl, flags, p.p);
+ p.v = p.p = 0;
}
// Accept a new connection.
@@ -1332,12 +376,6 @@ public:
asio::error_code accept(implementation_type& impl, Socket& peer,
endpoint_type* peer_endpoint, asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
// We cannot accept a socket that is already open.
if (peer.is_open())
{
@@ -1345,222 +383,22 @@ public:
return ec;
}
- for (;;)
- {
- socket_holder new_socket;
- std::size_t addr_len = 0;
- if (peer_endpoint)
- {
- addr_len = peer_endpoint->capacity();
- new_socket.reset(socket_ops::accept(impl.socket_,
- peer_endpoint->data(), &addr_len, ec));
- }
- else
- {
- new_socket.reset(socket_ops::accept(impl.socket_, 0, 0, ec));
- }
-
- if (ec)
- {
- if (ec == asio::error::connection_aborted
- && !(impl.flags_ & implementation_type::enable_connection_aborted))
- {
- // Retry accept operation.
- continue;
- }
- else
- {
- return ec;
- }
- }
+ std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0;
+ socket_holder new_socket(socket_ops::sync_accept(impl.socket_,
+ impl.state_, peer_endpoint ? peer_endpoint->data() : 0,
+ peer_endpoint ? &addr_len : 0, ec));
+ // On success, assign new connection to peer socket object.
+ if (new_socket.get() >= 0)
+ {
if (peer_endpoint)
peer_endpoint->resize(addr_len);
-
- peer.assign(impl.protocol_, new_socket.get(), ec);
- if (!ec)
+ if (!peer.assign(impl.protocol_, new_socket.get(), ec))
new_socket.release();
- return ec;
- }
- }
-
- template <typename Socket, typename Handler>
- class accept_op : public operation
- {
- public:
- accept_op(win_iocp_io_service& iocp_service, socket_type socket,
- Socket& peer, const protocol_type& protocol,
- endpoint_type* peer_endpoint, bool enable_connection_aborted,
- Handler handler)
- : operation(&accept_op::do_complete),
- iocp_service_(iocp_service),
- socket_(socket),
- peer_(peer),
- protocol_(protocol),
- peer_endpoint_(peer_endpoint),
- enable_connection_aborted_(enable_connection_aborted),
- handler_(handler)
- {
}
- socket_holder& new_socket()
- {
- return new_socket_;
- }
-
- void* output_buffer()
- {
- return output_buffer_;
- }
-
- DWORD address_length()
- {
- return sizeof(sockaddr_storage_type) + 16;
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code ec, std::size_t /*bytes_transferred*/)
- {
- // Take ownership of the handler object.
- accept_op* o(static_cast<accept_op*>(base));
- typedef handler_alloc_traits<Handler, accept_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
- // Map Windows error ERROR_NETNAME_DELETED to connection_aborted.
- if (ec.value() == ERROR_NETNAME_DELETED)
- {
- ec = asio::error::connection_aborted;
- }
-
- // Restart the accept operation if we got the connection_aborted error
- // and the enable_connection_aborted socket option is not set.
- if (ec == asio::error::connection_aborted
- && !o->enable_connection_aborted_)
- {
- // Reset OVERLAPPED structure.
- o->reset();
-
- // Create a new socket for the next connection, since the AcceptEx
- // call fails with WSAEINVAL if we try to reuse the same socket.
- o->new_socket_.reset();
- o->new_socket_.reset(socket_ops::socket(o->protocol_.family(),
- o->protocol_.type(), o->protocol_.protocol(), ec));
- if (o->new_socket_.get() != invalid_socket)
- {
- // Accept a connection.
- DWORD bytes_read = 0;
- BOOL result = ::AcceptEx(o->socket_, o->new_socket_.get(),
- o->output_buffer(), 0, o->address_length(),
- o->address_length(), &bytes_read, o);
- DWORD last_error = ::WSAGetLastError();
- ec = asio::error_code(last_error,
- asio::error::get_system_category());
-
- // Check if the operation completed immediately.
- if (!result && last_error != WSA_IO_PENDING)
- {
- if (last_error == ERROR_NETNAME_DELETED
- || last_error == WSAECONNABORTED)
- {
- // Post this handler so that operation will be restarted again.
- o->iocp_service_.work_started();
- o->iocp_service_.on_completion(o, ec);
- ptr.release();
- return;
- }
- else
- {
- // Operation already complete. Continue with rest of this
- // handler.
- }
- }
- else
- {
- // Asynchronous operation has been successfully restarted.
- o->iocp_service_.work_started();
- o->iocp_service_.on_pending(o);
- ptr.release();
- return;
- }
- }
- }
-
- // Get the address of the peer.
- endpoint_type peer_endpoint;
- if (!ec)
- {
- LPSOCKADDR local_addr = 0;
- int local_addr_length = 0;
- LPSOCKADDR remote_addr = 0;
- int remote_addr_length = 0;
- GetAcceptExSockaddrs(o->output_buffer(), 0, o->address_length(),
- o->address_length(), &local_addr, &local_addr_length,
- &remote_addr, &remote_addr_length);
- if (static_cast<std::size_t>(remote_addr_length)
- > peer_endpoint.capacity())
- {
- ec = asio::error::invalid_argument;
- }
- else
- {
- using namespace std; // For memcpy.
- memcpy(peer_endpoint.data(), remote_addr, remote_addr_length);
- peer_endpoint.resize(static_cast<std::size_t>(remote_addr_length));
- }
- }
-
- // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname
- // and getpeername will work on the accepted socket.
- if (!ec)
- {
- SOCKET update_ctx_param = o->socket_;
- socket_ops::setsockopt(o->new_socket_.get(),
- SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
- &update_ctx_param, sizeof(SOCKET), ec);
- }
-
- // If the socket was successfully accepted, transfer ownership of the
- // socket to the peer object.
- if (!ec)
- {
- o->peer_.assign(o->protocol_,
- native_type(o->new_socket_.get(), peer_endpoint), ec);
- if (!ec)
- o->new_socket_.release();
- }
-
- // Pass endpoint back to caller.
- if (o->peer_endpoint_)
- *o->peer_endpoint_ = peer_endpoint;
-
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder1<Handler, asio::error_code>
- handler(o->handler_, ec);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- win_iocp_io_service& iocp_service_;
- socket_type socket_;
- socket_holder new_socket_;
- Socket& peer_;
- protocol_type protocol_;
- endpoint_type* peer_endpoint_;
- unsigned char output_buffer_[(sizeof(sockaddr_storage_type) + 16) * 2];
- bool enable_connection_aborted_;
- Handler handler_;
- };
+ return ec;
+ }
// Start an asynchronous accept. The peer and peer_endpoint objects
// must be valid until the accept's handler is invoked.
@@ -1569,442 +407,54 @@ public:
endpoint_type* peer_endpoint, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef accept_op<Socket, Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
+ typedef win_iocp_socket_accept_op<Socket, protocol_type, Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
bool enable_connection_aborted =
- (impl.flags_ & implementation_type::enable_connection_aborted);
- handler_ptr<alloc_traits> ptr(raw_ptr, iocp_service_, impl.socket_, peer,
- impl.protocol_, peer_endpoint, enable_connection_aborted, handler);
+ (impl.state_ & socket_ops::enable_connection_aborted) != 0;
+ p.p = new (p.v) op(*this, impl.socket_, peer, impl.protocol_,
+ peer_endpoint, enable_connection_aborted, handler);
- start_accept_op(impl, peer.is_open(), ptr.get()->new_socket(),
- ptr.get()->output_buffer(), ptr.get()->address_length(), ptr.get());
- ptr.release();
+ start_accept_op(impl, peer.is_open(), p.p->new_socket(),
+ impl.protocol_.family(), impl.protocol_.type(),
+ impl.protocol_.protocol(), p.p->output_buffer(),
+ p.p->address_length(), p.p);
+ p.v = p.p = 0;
}
// Connect the socket to the specified endpoint.
asio::error_code connect(implementation_type& impl,
const endpoint_type& peer_endpoint, asio::error_code& ec)
{
- if (!is_open(impl))
- {
- ec = asio::error::bad_descriptor;
- return ec;
- }
-
- // Perform the connect operation.
- socket_ops::connect(impl.socket_,
+ socket_ops::sync_connect(impl.socket_,
peer_endpoint.data(), peer_endpoint.size(), ec);
return ec;
}
- class connect_op_base : public reactor_op
- {
- public:
- connect_op_base(socket_type socket, func_type complete_func)
- : reactor_op(&connect_op_base::do_perform, complete_func),
- socket_(socket)
- {
- }
-
- static bool do_perform(reactor_op* base)
- {
- connect_op_base* o(static_cast<connect_op_base*>(base));
-
- // Get the error code from the connect operation.
- int connect_error = 0;
- size_t connect_error_len = sizeof(connect_error);
- if (socket_ops::getsockopt(o->socket_, SOL_SOCKET, SO_ERROR,
- &connect_error, &connect_error_len, o->ec_) == socket_error_retval)
- return true;
-
- // The connection failed so the handler will be posted with an error code.
- if (connect_error)
- {
- o->ec_ = asio::error_code(connect_error,
- asio::error::get_system_category());
- }
-
- return true;
- }
-
- private:
- socket_type socket_;
- };
-
- template <typename Handler>
- class connect_op : public connect_op_base
- {
- public:
- connect_op(socket_type socket, Handler handler)
- : connect_op_base(socket, &connect_op::do_complete),
- handler_(handler)
- {
- }
-
- static void do_complete(io_service_impl* owner, operation* base,
- asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
- {
- // Take ownership of the handler object.
- connect_op* o(static_cast<connect_op*>(base));
- typedef handler_alloc_traits<Handler, connect_op> alloc_traits;
- handler_ptr<alloc_traits> ptr(o->handler_, o);
-
- // Make the upcall if required.
- if (owner)
- {
- // Make a copy of the handler so that the memory can be deallocated
- // before the upcall is made. Even if we're not about to make an
- // upcall, a sub-object of the handler may be the true owner of the
- // memory associated with the handler. Consequently, a local copy of
- // the handler is required to ensure that any owning sub-object remains
- // valid until after we have deallocated the memory here.
- detail::binder1<Handler, asio::error_code>
- handler(o->handler_, o->ec_);
- ptr.reset();
- asio::detail::fenced_block b;
- asio_handler_invoke_helpers::invoke(handler, handler);
- }
- }
-
- private:
- Handler handler_;
- };
-
// Start an asynchronous connect.
template <typename Handler>
void async_connect(implementation_type& impl,
const endpoint_type& peer_endpoint, Handler handler)
{
// Allocate and construct an operation to wrap the handler.
- typedef connect_op<Handler> value_type;
- typedef handler_alloc_traits<Handler, value_type> alloc_traits;
- raw_handler_ptr<alloc_traits> raw_ptr(handler);
- handler_ptr<alloc_traits> ptr(raw_ptr, impl.socket_, handler);
-
- start_connect_op(impl, ptr.get(), peer_endpoint);
- ptr.release();
- }
-
-private:
- // Helper function to start an asynchronous send operation.
- void start_send_op(implementation_type& impl, WSABUF* buffers,
- std::size_t buffer_count, socket_base::message_flags flags,
- bool noop, operation* op)
- {
- update_cancellation_thread_id(impl);
- iocp_service_.work_started();
-
- if (noop)
- iocp_service_.on_completion(op);
- else if (!is_open(impl))
- iocp_service_.on_completion(op, asio::error::bad_descriptor);
- else
- {
- DWORD bytes_transferred = 0;
- int result = ::WSASend(impl.socket_, buffers,
- buffer_count, &bytes_transferred, flags, op, 0);
- DWORD last_error = ::WSAGetLastError();
- if (last_error == ERROR_PORT_UNREACHABLE)
- last_error = WSAECONNREFUSED;
- if (result != 0 && last_error != WSA_IO_PENDING)
- iocp_service_.on_completion(op, last_error, bytes_transferred);
- else
- iocp_service_.on_pending(op);
- }
- }
-
- // Helper function to start an asynchronous send_to operation.
- void start_send_to_op(implementation_type& impl, WSABUF* buffers,
- std::size_t buffer_count, const endpoint_type& destination,
- socket_base::message_flags flags, operation* op)
- {
- update_cancellation_thread_id(impl);
- iocp_service_.work_started();
+ typedef reactive_socket_connect_op<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl.socket_, handler);
- if (!is_open(impl))
- iocp_service_.on_completion(op, asio::error::bad_descriptor);
- else
- {
- DWORD bytes_transferred = 0;
- int result = ::WSASendTo(impl.socket_, buffers, buffer_count,
- &bytes_transferred, flags, destination.data(),
- static_cast<int>(destination.size()), op, 0);
- DWORD last_error = ::WSAGetLastError();
- if (last_error == ERROR_PORT_UNREACHABLE)
- last_error = WSAECONNREFUSED;
- if (result != 0 && last_error != WSA_IO_PENDING)
- iocp_service_.on_completion(op, last_error, bytes_transferred);
- else
- iocp_service_.on_pending(op);
- }
+ start_connect_op(impl, p.p, peer_endpoint.data(),
+ static_cast<int>(peer_endpoint.size()));
+ p.v = p.p = 0;
}
-
- // Helper function to start an asynchronous receive operation.
- void start_receive_op(implementation_type& impl, WSABUF* buffers,
- std::size_t buffer_count, socket_base::message_flags flags,
- bool noop, operation* op)
- {
- update_cancellation_thread_id(impl);
- iocp_service_.work_started();
-
- if (noop)
- iocp_service_.on_completion(op);
- else if (!is_open(impl))
- iocp_service_.on_completion(op, asio::error::bad_descriptor);
- else
- {
- DWORD bytes_transferred = 0;
- DWORD recv_flags = flags;
- int result = ::WSARecv(impl.socket_, buffers, buffer_count,
- &bytes_transferred, &recv_flags, op, 0);
- DWORD last_error = ::WSAGetLastError();
- if (last_error == ERROR_NETNAME_DELETED)
- last_error = WSAECONNRESET;
- else if (last_error == ERROR_PORT_UNREACHABLE)
- last_error = WSAECONNREFUSED;
- if (result != 0 && last_error != WSA_IO_PENDING)
- iocp_service_.on_completion(op, last_error, bytes_transferred);
- else
- iocp_service_.on_pending(op);
- }
- }
-
- // Helper function to start an asynchronous receive_from operation.
- void start_receive_from_op(implementation_type& impl, WSABUF* buffers,
- std::size_t buffer_count, endpoint_type& sender_endpoint,
- socket_base::message_flags flags, int* endpoint_size, operation* op)
- {
- update_cancellation_thread_id(impl);
- iocp_service_.work_started();
-
- if (!is_open(impl))
- iocp_service_.on_completion(op, asio::error::bad_descriptor);
- else
- {
- DWORD bytes_transferred = 0;
- DWORD recv_flags = flags;
- int result = ::WSARecvFrom(impl.socket_, buffers,
- buffer_count, &bytes_transferred, &recv_flags,
- sender_endpoint.data(), endpoint_size, op, 0);
- DWORD last_error = ::WSAGetLastError();
- if (last_error == ERROR_PORT_UNREACHABLE)
- last_error = WSAECONNREFUSED;
- if (result != 0 && last_error != WSA_IO_PENDING)
- iocp_service_.on_completion(op, last_error, bytes_transferred);
- else
- iocp_service_.on_pending(op);
- }
- }
-
- // Helper function to start an asynchronous receive_from operation.
- void start_accept_op(implementation_type& impl,
- bool peer_is_open, socket_holder& new_socket,
- void* output_buffer, DWORD address_length, operation* op)
- {
- update_cancellation_thread_id(impl);
- iocp_service_.work_started();
-
- if (!is_open(impl))
- iocp_service_.on_completion(op, asio::error::bad_descriptor);
- else if (peer_is_open)
- iocp_service_.on_completion(op, asio::error::already_open);
- else
- {
- asio::error_code ec;
- new_socket.reset(socket_ops::socket(impl.protocol_.family(),
- impl.protocol_.type(), impl.protocol_.protocol(), ec));
- if (new_socket.get() == invalid_socket)
- iocp_service_.on_completion(op, ec);
- else
- {
- DWORD bytes_read = 0;
- BOOL result = ::AcceptEx(impl.socket_, new_socket.get(), output_buffer,
- 0, address_length, address_length, &bytes_read, op);
- DWORD last_error = ::WSAGetLastError();
- if (!result && last_error != WSA_IO_PENDING)
- iocp_service_.on_completion(op, last_error);
- else
- iocp_service_.on_pending(op);
- }
- }
- }
-
- // Start an asynchronous read or write operation using the the reactor.
- void start_reactor_op(implementation_type& impl, int op_type, reactor_op* op)
- {
- reactor& r = get_reactor();
- update_cancellation_thread_id(impl);
-
- if (is_open(impl))
- {
- r.start_op(op_type, impl.socket_, impl.reactor_data_, op, false);
- return;
- }
- else
- op->ec_ = asio::error::bad_descriptor;
-
- iocp_service_.post_immediate_completion(op);
- }
-
- // Start the asynchronous connect operation using the reactor.
- void start_connect_op(implementation_type& impl,
- reactor_op* op, const endpoint_type& peer_endpoint)
- {
- reactor& r = get_reactor();
- update_cancellation_thread_id(impl);
-
- if (is_open(impl))
- {
- ioctl_arg_type non_blocking = 1;
- if (!socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, op->ec_))
- {
- if (socket_ops::connect(impl.socket_, peer_endpoint.data(),
- peer_endpoint.size(), op->ec_) != 0)
- {
- if (!op->ec_
- && !(impl.flags_ & implementation_type::user_set_non_blocking))
- {
- non_blocking = 0;
- socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, op->ec_);
- }
-
- if (op->ec_ == asio::error::in_progress
- || op->ec_ == asio::error::would_block)
- {
- op->ec_ = asio::error_code();
- r.start_op(reactor::connect_op, impl.socket_,
- impl.reactor_data_, op, true);
- return;
- }
- }
- }
- }
- else
- op->ec_ = asio::error::bad_descriptor;
-
- iocp_service_.post_immediate_completion(op);
- }
-
- // Helper function to close a socket when the associated object is being
- // destroyed.
- void close_for_destruction(implementation_type& impl)
- {
- if (is_open(impl))
- {
- // Check if the reactor was created, in which case we need to close the
- // socket on the reactor as well to cancel any operations that might be
- // running there.
- reactor* r = static_cast<reactor*>(
- interlocked_compare_exchange_pointer(
- reinterpret_cast<void**>(&reactor_), 0, 0));
- if (r)
- r->close_descriptor(impl.socket_, impl.reactor_data_);
-
- // The socket destructor must not block. If the user has changed the
- // linger option to block in the foreground, we will change it back to the
- // default so that the closure is performed in the background.
- if (impl.flags_ & implementation_type::close_might_block)
- {
- ::linger opt;
- opt.l_onoff = 0;
- opt.l_linger = 0;
- asio::error_code ignored_ec;
- socket_ops::setsockopt(impl.socket_,
- SOL_SOCKET, SO_LINGER, &opt, sizeof(opt), ignored_ec);
- }
-
- asio::error_code ignored_ec;
- socket_ops::close(impl.socket_, ignored_ec);
- impl.socket_ = invalid_socket;
- impl.flags_ = 0;
- impl.cancel_token_.reset();
-#if defined(ASIO_ENABLE_CANCELIO)
- impl.safe_cancellation_thread_id_ = 0;
-#endif // defined(ASIO_ENABLE_CANCELIO)
- }
- }
-
- // Update the ID of the thread from which cancellation is safe.
- void update_cancellation_thread_id(implementation_type& impl)
- {
-#if defined(ASIO_ENABLE_CANCELIO)
- if (impl.safe_cancellation_thread_id_ == 0)
- impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId();
- else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId())
- impl.safe_cancellation_thread_id_ = ~DWORD(0);
-#else // defined(ASIO_ENABLE_CANCELIO)
- (void)impl;
-#endif // defined(ASIO_ENABLE_CANCELIO)
- }
-
- // Helper function to get the reactor. If no reactor has been created yet, a
- // new one is obtained from the io_service and a pointer to it is cached in
- // this service.
- reactor& get_reactor()
- {
- reactor* r = static_cast<reactor*>(
- interlocked_compare_exchange_pointer(
- reinterpret_cast<void**>(&reactor_), 0, 0));
- if (!r)
- {
- r = &(use_service<reactor>(io_service_));
- interlocked_exchange_pointer(reinterpret_cast<void**>(&reactor_), r);
- }
- return *r;
- }
-
- // Helper function to emulate InterlockedCompareExchangePointer functionality
- // for:
- // - very old Platform SDKs; and
- // - platform SDKs where MSVC's /Wp64 option causes spurious warnings.
- void* interlocked_compare_exchange_pointer(void** dest, void* exch, void* cmp)
- {
-#if defined(_M_IX86)
- return reinterpret_cast<void*>(InterlockedCompareExchange(
- reinterpret_cast<PLONG>(dest), reinterpret_cast<LONG>(exch),
- reinterpret_cast<LONG>(cmp)));
-#else
- return InterlockedCompareExchangePointer(dest, exch, cmp);
-#endif
- }
-
- // Helper function to emulate InterlockedExchangePointer functionality for:
- // - very old Platform SDKs; and
- // - platform SDKs where MSVC's /Wp64 option causes spurious warnings.
- void* interlocked_exchange_pointer(void** dest, void* val)
- {
-#if defined(_M_IX86)
- return reinterpret_cast<void*>(InterlockedExchange(
- reinterpret_cast<PLONG>(dest), reinterpret_cast<LONG>(val)));
-#else
- return InterlockedExchangePointer(dest, val);
-#endif
- }
-
- // The io_service used to obtain the reactor, if required.
- asio::io_service& io_service_;
-
- // The IOCP service used for running asynchronous operations and dispatching
- // handlers.
- win_iocp_io_service& iocp_service_;
-
- // The reactor used for performing connect operations. This object is created
- // only if needed.
- reactor* reactor_;
-
- // Mutex to protect access to the linked list of implementations.
- asio::detail::mutex mutex_;
-
- // The head of a linked list of all implementations.
- implementation_type* impl_list_;
};
} // namespace detail
} // namespace asio
-#endif // defined(ASIO_HAS_IOCP)
-
#include "asio/detail/pop_options.hpp"
+#endif // defined(ASIO_HAS_IOCP)
+
#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP
diff --git a/ext/asio/asio/detail/win_iocp_socket_service_base.hpp b/ext/asio/asio/detail/win_iocp_socket_service_base.hpp
new file mode 100644
index 0000000..62010b3
--- /dev/null
+++ b/ext/asio/asio/detail/win_iocp_socket_service_base.hpp
@@ -0,0 +1,385 @@
+//
+// detail/win_iocp_socket_service_base.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_BASE_HPP
+#define ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_BASE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+
+#include <boost/type_traits/is_same.hpp>
+#include <boost/utility/addressof.hpp>
+#include "asio/error.hpp"
+#include "asio/io_service.hpp"
+#include "asio/socket_base.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/buffer_sequence_adapter.hpp"
+#include "asio/detail/fenced_block.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/mutex.hpp"
+#include "asio/detail/operation.hpp"
+#include "asio/detail/reactor.hpp"
+#include "asio/detail/reactor_op.hpp"
+#include "asio/detail/socket_holder.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/socket_types.hpp"
+#include "asio/detail/win_iocp_io_service.hpp"
+#include "asio/detail/win_iocp_null_buffers_op.hpp"
+#include "asio/detail/win_iocp_socket_send_op.hpp"
+#include "asio/detail/win_iocp_socket_recv_op.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace detail {
+
+class win_iocp_socket_service_base
+{
+public:
+ // The implementation type of the socket.
+ struct base_implementation_type
+ {
+ // The native socket representation.
+ socket_type socket_;
+
+ // The current state of the socket.
+ socket_ops::state_type state_;
+
+ // We use a shared pointer as a cancellation token here to work around the
+ // broken Windows support for cancellation. MSDN says that when you call
+ // closesocket any outstanding WSARecv or WSASend operations will complete
+ // with the error ERROR_OPERATION_ABORTED. In practice they complete with
+ // ERROR_NETNAME_DELETED, which means you can't tell the difference between
+ // a local cancellation and the socket being hard-closed by the peer.
+ socket_ops::shared_cancel_token_type cancel_token_;
+
+ // Per-descriptor data used by the reactor.
+ reactor::per_descriptor_data reactor_data_;
+
+#if defined(ASIO_ENABLE_CANCELIO)
+ // The ID of the thread from which it is safe to cancel asynchronous
+ // operations. 0 means no asynchronous operations have been started yet.
+ // ~0 means asynchronous operations have been started from more than one
+ // thread, and cancellation is not supported for the socket.
+ DWORD safe_cancellation_thread_id_;
+#endif // defined(ASIO_ENABLE_CANCELIO)
+
+ // Pointers to adjacent socket implementations in linked list.
+ base_implementation_type* next_;
+ base_implementation_type* prev_;
+ };
+
+ // Constructor.
+ ASIO_DECL win_iocp_socket_service_base(
+ asio::io_service& io_service);
+
+ // Destroy all user-defined handler objects owned by the service.
+ ASIO_DECL void shutdown_service();
+
+ // Construct a new socket implementation.
+ ASIO_DECL void construct(base_implementation_type& impl);
+
+ // Destroy a socket implementation.
+ ASIO_DECL void destroy(base_implementation_type& impl);
+
+ // Determine whether the socket is open.
+ bool is_open(const base_implementation_type& impl) const
+ {
+ return impl.socket_ != invalid_socket;
+ }
+
+ // Destroy a socket implementation.
+ ASIO_DECL asio::error_code close(
+ base_implementation_type& impl, asio::error_code& ec);
+
+ // Cancel all operations associated with the socket.
+ ASIO_DECL asio::error_code cancel(
+ base_implementation_type& impl, asio::error_code& ec);
+
+ // Determine whether the socket is at the out-of-band data mark.
+ bool at_mark(const base_implementation_type& impl,
+ asio::error_code& ec) const
+ {
+ return socket_ops::sockatmark(impl.socket_, ec);
+ }
+
+ // Determine the number of bytes available for reading.
+ std::size_t available(const base_implementation_type& impl,
+ asio::error_code& ec) const
+ {
+ return socket_ops::available(impl.socket_, ec);
+ }
+
+ // Place the socket into the state where it will listen for new connections.
+ asio::error_code listen(base_implementation_type& impl,
+ int backlog, asio::error_code& ec)
+ {
+ socket_ops::listen(impl.socket_, backlog, ec);
+ return ec;
+ }
+
+ // Perform an IO control command on the socket.
+ template <typename IO_Control_Command>
+ asio::error_code io_control(base_implementation_type& impl,
+ IO_Control_Command& command, asio::error_code& ec)
+ {
+ socket_ops::ioctl(impl.socket_, impl.state_, command.name(),
+ static_cast<ioctl_arg_type*>(command.data()), ec);
+ return ec;
+ }
+
+ /// Disable sends or receives on the socket.
+ asio::error_code shutdown(base_implementation_type& impl,
+ socket_base::shutdown_type what, asio::error_code& ec)
+ {
+ socket_ops::shutdown(impl.socket_, what, ec);
+ return ec;
+ }
+
+ // Send the given data to the peer. Returns the number of bytes sent.
+ template <typename ConstBufferSequence>
+ size_t send(base_implementation_type& impl,
+ const ConstBufferSequence& buffers,
+ socket_base::message_flags flags, asio::error_code& ec)
+ {
+ buffer_sequence_adapter<asio::const_buffer,
+ ConstBufferSequence> bufs(buffers);
+
+ return socket_ops::sync_send(impl.socket_, impl.state_,
+ bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec);
+ }
+
+ // Wait until data can be sent without blocking.
+ size_t send(base_implementation_type& impl, const null_buffers&,
+ socket_base::message_flags, asio::error_code& ec)
+ {
+ // Wait for socket to become ready.
+ socket_ops::poll_write(impl.socket_, ec);
+
+ return 0;
+ }
+
+ // Start an asynchronous send. The data being sent must be valid for the
+ // lifetime of the asynchronous operation.
+ template <typename ConstBufferSequence, typename Handler>
+ void async_send(base_implementation_type& impl,
+ const ConstBufferSequence& buffers,
+ socket_base::message_flags flags, Handler handler)
+ {
+ // Allocate and construct an operation to wrap the handler.
+ typedef win_iocp_socket_send_op<ConstBufferSequence, Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl.cancel_token_, buffers, handler);
+
+ buffer_sequence_adapter<asio::const_buffer,
+ ConstBufferSequence> bufs(buffers);
+
+ start_send_op(impl, bufs.buffers(), bufs.count(), flags,
+ (impl.state_ & socket_ops::stream_oriented) != 0 && bufs.all_empty(),
+ p.p);
+ p.v = p.p = 0;
+ }
+
+ // Start an asynchronous wait until data can be sent without blocking.
+ template <typename Handler>
+ void async_send(base_implementation_type& impl, const null_buffers&,
+ socket_base::message_flags, Handler handler)
+ {
+ // Allocate and construct an operation to wrap the handler.
+ typedef win_iocp_null_buffers_op<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl.cancel_token_, handler);
+
+ start_reactor_op(impl, reactor::write_op, p.p);
+ p.v = p.p = 0;
+ }
+
+ // Receive some data from the peer. Returns the number of bytes received.
+ template <typename MutableBufferSequence>
+ size_t receive(base_implementation_type& impl,
+ const MutableBufferSequence& buffers,
+ socket_base::message_flags flags, asio::error_code& ec)
+ {
+ buffer_sequence_adapter<asio::mutable_buffer,
+ MutableBufferSequence> bufs(buffers);
+
+ return socket_ops::sync_recv(impl.socket_, impl.state_,
+ bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec);
+ }
+
+ // Wait until data can be received without blocking.
+ size_t receive(base_implementation_type& impl, const null_buffers&,
+ socket_base::message_flags, asio::error_code& ec)
+ {
+ // Wait for socket to become ready.
+ socket_ops::poll_read(impl.socket_, ec);
+
+ return 0;
+ }
+
+ // Start an asynchronous receive. The buffer for the data being received
+ // must be valid for the lifetime of the asynchronous operation.
+ template <typename MutableBufferSequence, typename Handler>
+ void async_receive(base_implementation_type& impl,
+ const MutableBufferSequence& buffers,
+ socket_base::message_flags flags, Handler handler)
+ {
+ // Allocate and construct an operation to wrap the handler.
+ typedef win_iocp_socket_recv_op<MutableBufferSequence, Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl.state_, impl.cancel_token_, buffers, handler);
+
+ buffer_sequence_adapter<asio::mutable_buffer,
+ MutableBufferSequence> bufs(buffers);
+
+ start_receive_op(impl, bufs.buffers(), bufs.count(), flags,
+ (impl.state_ & socket_ops::stream_oriented) != 0 && bufs.all_empty(),
+ p.p);
+ p.v = p.p = 0;
+ }
+
+ // Wait until data can be received without blocking.
+ template <typename Handler>
+ void async_receive(base_implementation_type& impl, const null_buffers&,
+ socket_base::message_flags flags, Handler handler)
+ {
+ // Allocate and construct an operation to wrap the handler.
+ typedef win_iocp_null_buffers_op<Handler> op;
+ typename op::ptr p = { boost::addressof(handler),
+ asio_handler_alloc_helpers::allocate(
+ sizeof(op), handler), 0 };
+ p.p = new (p.v) op(impl.cancel_token_, handler);
+
+ start_null_buffers_receive_op(impl, flags, p.p);
+ p.v = p.p = 0;
+ }
+
+ // Helper function to restart an asynchronous accept operation.
+ ASIO_DECL void restart_accept_op(socket_type s,
+ socket_holder& new_socket, int family, int type, int protocol,
+ void* output_buffer, DWORD address_length, operation* op);
+
+protected:
+ // Open a new socket implementation.
+ ASIO_DECL asio::error_code do_open(
+ base_implementation_type& impl, int family, int type,
+ int protocol, asio::error_code& ec);
+
+ // Assign a native socket to a socket implementation.
+ ASIO_DECL asio::error_code do_assign(
+ base_implementation_type& impl, int type,
+ socket_type native_socket, asio::error_code& ec);
+
+ // Helper function to start an asynchronous send operation.
+ ASIO_DECL void start_send_op(base_implementation_type& impl,
+ WSABUF* buffers, std::size_t buffer_count,
+ socket_base::message_flags flags, bool noop, operation* op);
+
+ // Helper function to start an asynchronous send_to operation.
+ ASIO_DECL void start_send_to_op(base_implementation_type& impl,
+ WSABUF* buffers, std::size_t buffer_count,
+ const socket_addr_type* addr, int addrlen,
+ socket_base::message_flags flags, operation* op);
+
+ // Helper function to start an asynchronous receive operation.
+ ASIO_DECL void start_receive_op(base_implementation_type& impl,
+ WSABUF* buffers, std::size_t buffer_count,
+ socket_base::message_flags flags, bool noop, operation* op);
+
+ // Helper function to start an asynchronous null_buffers receive operation.
+ ASIO_DECL void start_null_buffers_receive_op(
+ base_implementation_type& impl,
+ socket_base::message_flags flags, reactor_op* op);
+
+ // Helper function to start an asynchronous receive_from operation.
+ ASIO_DECL void start_receive_from_op(base_implementation_type& impl,
+ WSABUF* buffers, std::size_t buffer_count, socket_addr_type* addr,
+ socket_base::message_flags flags, int* addrlen, operation* op);
+
+ // Helper function to start an asynchronous accept operation.
+ ASIO_DECL void start_accept_op(base_implementation_type& impl,
+ bool peer_is_open, socket_holder& new_socket, int family, int type,
+ int protocol, void* output_buffer, DWORD address_length, operation* op);
+
+ // Start an asynchronous read or write operation using the the reactor.
+ ASIO_DECL void start_reactor_op(base_implementation_type& impl,
+ int op_type, reactor_op* op);
+
+ // Start the asynchronous connect operation using the reactor.
+ ASIO_DECL void start_connect_op(base_implementation_type& impl,
+ reactor_op* op, const socket_addr_type* addr, std::size_t addrlen);
+
+ // Helper function to close a socket when the associated object is being
+ // destroyed.
+ ASIO_DECL void close_for_destruction(base_implementation_type& impl);
+
+ // Update the ID of the thread from which cancellation is safe.
+ ASIO_DECL void update_cancellation_thread_id(
+ base_implementation_type& impl);
+
+ // Helper function to get the reactor. If no reactor has been created yet, a
+ // new one is obtained from the io_service and a pointer to it is cached in
+ // this service.
+ ASIO_DECL reactor& get_reactor();
+
+ // Helper function to emulate InterlockedCompareExchangePointer functionality
+ // for:
+ // - very old Platform SDKs; and
+ // - platform SDKs where MSVC's /Wp64 option causes spurious warnings.
+ ASIO_DECL void* interlocked_compare_exchange_pointer(
+ void** dest, void* exch, void* cmp);
+
+ // Helper function to emulate InterlockedExchangePointer functionality for:
+ // - very old Platform SDKs; and
+ // - platform SDKs where MSVC's /Wp64 option causes spurious warnings.
+ ASIO_DECL void* interlocked_exchange_pointer(void** dest, void* val);
+
+ // The io_service used to obtain the reactor, if required.
+ asio::io_service& io_service_;
+
+ // The IOCP service used for running asynchronous operations and dispatching
+ // handlers.
+ win_iocp_io_service& iocp_service_;
+
+ // The reactor used for performing connect operations. This object is created
+ // only if needed.
+ reactor* reactor_;
+
+ // Mutex to protect access to the linked list of implementations.
+ asio::detail::mutex mutex_;
+
+ // The head of a linked list of all implementations.
+ base_implementation_type* impl_list_;
+};
+
+} // namespace detail
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/win_iocp_socket_service_base.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(ASIO_HAS_IOCP)
+
+#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_BASE_HPP
diff --git a/ext/asio/asio/detail/win_mutex.hpp b/ext/asio/asio/detail/win_mutex.hpp
index 1280a4e..59ad697 100644
--- a/ext/asio/asio/detail/win_mutex.hpp
+++ b/ext/asio/asio/detail/win_mutex.hpp
@@ -1,8 +1,8 @@
//
-// win_mutex.hpp
-// ~~~~~~~~~~~~~
+// detail/win_mutex.hpp
+// ~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,23 +15,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if defined(BOOST_WINDOWS)
-#include "asio/error.hpp"
-#include "asio/system_error.hpp"
#include "asio/detail/noncopyable.hpp"
-#include "asio/detail/socket_types.hpp"
#include "asio/detail/scoped_lock.hpp"
+#include "asio/detail/socket_types.hpp"
#include "asio/detail/push_options.hpp"
-#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
namespace asio {
namespace detail {
@@ -43,18 +35,7 @@ public:
typedef asio::detail::scoped_lock<win_mutex> scoped_lock;
// Constructor.
- win_mutex()
- {
- int error = do_init();
- if (error != 0)
- {
- asio::system_error e(
- asio::error_code(error,
- asio::error::get_system_category()),
- "mutex");
- boost::throw_exception(e);
- }
- }
+ ASIO_DECL win_mutex();
// Destructor.
~win_mutex()
@@ -78,35 +59,7 @@ private:
// Initialisation must be performed in a separate function to the constructor
// since the compiler does not support the use of structured exceptions and
// C++ exceptions in the same function.
- int do_init()
- {
-#if defined(__MINGW32__)
- // Not sure if MinGW supports structured exception handling, so for now
- // we'll just call the Windows API and hope.
-# if defined(UNDER_CE)
- ::InitializeCriticalSection(&crit_section_);
-# else
- ::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000);
-# endif
- return 0;
-#else
- __try
- {
-# if defined(UNDER_CE)
- ::InitializeCriticalSection(&crit_section_);
-# else
- ::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000);
-# endif
- }
- __except(GetExceptionCode() == STATUS_NO_MEMORY
- ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
- {
- return ERROR_OUTOFMEMORY;
- }
-
- return 0;
-#endif
- }
+ ASIO_DECL int do_init();
::CRITICAL_SECTION crit_section_;
};
@@ -114,8 +67,12 @@ private:
} // namespace detail
} // namespace asio
-#endif // defined(BOOST_WINDOWS)
-
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/win_mutex.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(BOOST_WINDOWS)
+
#endif // ASIO_DETAIL_WIN_MUTEX_HPP
diff --git a/ext/asio/asio/detail/win_thread.hpp b/ext/asio/asio/detail/win_thread.hpp
index 9bf0665..e73d1a4 100644
--- a/ext/asio/asio/detail/win_thread.hpp
+++ b/ext/asio/asio/detail/win_thread.hpp
@@ -1,8 +1,8 @@
//
-// win_thread.hpp
-// ~~~~~~~~~~~~~~
+// detail/win_thread.hpp
+// ~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,34 +15,24 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if defined(BOOST_WINDOWS) && !defined(UNDER_CE)
-#include "asio/error.hpp"
-#include "asio/system_error.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/socket_types.hpp"
#include "asio/detail/push_options.hpp"
-#include <boost/throw_exception.hpp>
-#include <memory>
-#include <process.h>
-#include "asio/detail/pop_options.hpp"
namespace asio {
namespace detail {
-unsigned int __stdcall win_thread_function(void* arg);
+ASIO_DECL unsigned int __stdcall win_thread_function(void* arg);
#if defined(WINVER) && (WINVER < 0x0500)
-void __stdcall apc_function(ULONG data);
+ASIO_DECL void __stdcall apc_function(ULONG data);
#else
-void __stdcall apc_function(ULONG_PTR data);
+ASIO_DECL void __stdcall apc_function(ULONG_PTR data);
#endif
template <typename T>
@@ -73,92 +63,26 @@ class win_thread
public:
// Constructor.
template <typename Function>
- win_thread(Function f)
- : exit_event_(0)
+ win_thread(Function f, unsigned int stack_size = 0)
+ : thread_(0),
+ exit_event_(0)
{
- std::auto_ptr<func_base> arg(new func<Function>(f));
-
- ::HANDLE entry_event = 0;
- arg->entry_event_ = entry_event = ::CreateEvent(0, true, false, 0);
- if (!entry_event)
- {
- DWORD last_error = ::GetLastError();
- asio::system_error e(
- asio::error_code(last_error,
- asio::error::get_system_category()),
- "thread.entry_event");
- boost::throw_exception(e);
- }
-
- arg->exit_event_ = exit_event_ = ::CreateEvent(0, true, false, 0);
- if (!exit_event_)
- {
- DWORD last_error = ::GetLastError();
- ::CloseHandle(entry_event);
- asio::system_error e(
- asio::error_code(last_error,
- asio::error::get_system_category()),
- "thread.exit_event");
- boost::throw_exception(e);
- }
-
- unsigned int thread_id = 0;
- thread_ = reinterpret_cast<HANDLE>(::_beginthreadex(0, 0,
- win_thread_function, arg.get(), 0, &thread_id));
- if (!thread_)
- {
- DWORD last_error = ::GetLastError();
- if (entry_event)
- ::CloseHandle(entry_event);
- if (exit_event_)
- ::CloseHandle(exit_event_);
- asio::system_error e(
- asio::error_code(last_error,
- asio::error::get_system_category()),
- "thread");
- boost::throw_exception(e);
- }
- arg.release();
-
- if (entry_event)
- {
- ::WaitForSingleObject(entry_event, INFINITE);
- ::CloseHandle(entry_event);
- }
+ start_thread(new func<Function>(f), stack_size);
}
// Destructor.
- ~win_thread()
- {
- ::CloseHandle(thread_);
-
- // The exit_event_ handle is deliberately allowed to leak here since it
- // is an error for the owner of an internal thread not to join() it.
- }
+ ASIO_DECL ~win_thread();
// Wait for the thread to exit.
- void join()
- {
- ::WaitForSingleObject(exit_event_, INFINITE);
- ::CloseHandle(exit_event_);
- if (terminate_threads())
- {
- ::TerminateThread(thread_, 0);
- }
- else
- {
- ::QueueUserAPC(apc_function, thread_, 0);
- ::WaitForSingleObject(thread_, INFINITE);
- }
- }
+ ASIO_DECL void join();
private:
- friend unsigned int __stdcall win_thread_function(void* arg);
+ friend ASIO_DECL unsigned int __stdcall win_thread_function(void* arg);
#if defined(WINVER) && (WINVER < 0x0500)
- friend void __stdcall apc_function(ULONG);
+ friend ASIO_DECL void __stdcall apc_function(ULONG);
#else
- friend void __stdcall apc_function(ULONG_PTR);
+ friend ASIO_DECL void __stdcall apc_function(ULONG_PTR);
#endif
class func_base
@@ -170,6 +94,12 @@ private:
::HANDLE exit_event_;
};
+ struct auto_func_base_ptr
+ {
+ func_base* ptr;
+ ~auto_func_base_ptr() { delete ptr; }
+ };
+
template <typename Function>
class func
: public func_base
@@ -189,44 +119,21 @@ private:
Function f_;
};
+ ASIO_DECL void start_thread(func_base* arg, unsigned int stack_size);
+
::HANDLE thread_;
::HANDLE exit_event_;
};
-inline unsigned int __stdcall win_thread_function(void* arg)
-{
- std::auto_ptr<win_thread::func_base> func(
- static_cast<win_thread::func_base*>(arg));
-
- ::SetEvent(func->entry_event_);
-
- func->run();
-
- // Signal that the thread has finished its work, but rather than returning go
- // to sleep to put the thread into a well known state. If the thread is being
- // joined during global object destruction then it may be killed using
- // TerminateThread (to avoid a deadlock in DllMain). Otherwise, the SleepEx
- // call will be interrupted using QueueUserAPC and the thread will shut down
- // cleanly.
- HANDLE exit_event = func->exit_event_;
- func.reset();
- ::SetEvent(exit_event);
- ::SleepEx(INFINITE, TRUE);
-
- return 0;
-}
-
-#if defined(WINVER) && (WINVER < 0x0500)
-inline void __stdcall apc_function(ULONG) {}
-#else
-inline void __stdcall apc_function(ULONG_PTR) {}
-#endif
-
} // namespace detail
} // namespace asio
-#endif // defined(BOOST_WINDOWS) && !defined(UNDER_CE)
-
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/win_thread.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(BOOST_WINDOWS) && !defined(UNDER_CE)
+
#endif // ASIO_DETAIL_WIN_THREAD_HPP
diff --git a/ext/asio/asio/detail/win_tss_ptr.hpp b/ext/asio/asio/detail/win_tss_ptr.hpp
index 5a4ed33..fb055f6 100644
--- a/ext/asio/asio/detail/win_tss_ptr.hpp
+++ b/ext/asio/asio/detail/win_tss_ptr.hpp
@@ -1,8 +1,8 @@
//
-// win_tss_ptr.hpp
-// ~~~~~~~~~~~~~~~
+// detail/win_tss_ptr.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,50 +15,30 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if defined(BOOST_WINDOWS)
-#include "asio/error.hpp"
-#include "asio/system_error.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/socket_types.hpp"
#include "asio/detail/push_options.hpp"
-#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
namespace asio {
namespace detail {
+// Helper function to create thread-specific storage.
+ASIO_DECL DWORD win_tss_ptr_create();
+
template <typename T>
class win_tss_ptr
: private noncopyable
{
public:
-#if defined(UNDER_CE)
- enum { out_of_indexes = 0xFFFFFFFF };
-#else
- enum { out_of_indexes = TLS_OUT_OF_INDEXES };
-#endif
-
// Constructor.
win_tss_ptr()
+ : tss_key_(win_tss_ptr_create())
{
- tss_key_ = ::TlsAlloc();
- if (tss_key_ == out_of_indexes)
- {
- DWORD last_error = ::GetLastError();
- asio::system_error e(
- asio::error_code(last_error,
- asio::error::get_system_category()),
- "tss");
- boost::throw_exception(e);
- }
}
// Destructor.
@@ -88,8 +68,12 @@ private:
} // namespace detail
} // namespace asio
-#endif // defined(BOOST_WINDOWS)
-
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/win_tss_ptr.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(BOOST_WINDOWS)
+
#endif // ASIO_DETAIL_WIN_TSS_PTR_HPP
diff --git a/ext/asio/asio/detail/wince_thread.hpp b/ext/asio/asio/detail/wince_thread.hpp
index 0b6de48..1dc9990 100644
--- a/ext/asio/asio/detail/wince_thread.hpp
+++ b/ext/asio/asio/detail/wince_thread.hpp
@@ -1,8 +1,8 @@
//
-// wince_thread.hpp
-// ~~~~~~~~~~~~~~~~
+// detail/wince_thread.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,23 +15,17 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if defined(BOOST_WINDOWS) && defined(UNDER_CE)
-#include "asio/error.hpp"
-#include "asio/system_error.hpp"
+#include <memory>
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/socket_types.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
#include "asio/detail/push_options.hpp"
-#include <boost/throw_exception.hpp>
-#include <memory>
-#include "asio/detail/pop_options.hpp"
namespace asio {
namespace detail {
@@ -53,11 +47,9 @@ public:
if (!thread_)
{
DWORD last_error = ::GetLastError();
- asio::system_error e(
- asio::error_code(last_error,
- asio::error::get_system_category()),
- "thread");
- boost::throw_exception(e);
+ asio::error_code ec(last_error,
+ asio::error::get_system_category());
+ asio::detail::throw_error(ec, "thread");
}
arg.release();
}
@@ -117,8 +109,8 @@ inline DWORD WINAPI wince_thread_function(LPVOID arg)
} // namespace detail
} // namespace asio
-#endif // defined(BOOST_WINDOWS) && defined(UNDER_CE)
-
#include "asio/detail/pop_options.hpp"
+#endif // defined(BOOST_WINDOWS) && defined(UNDER_CE)
+
#endif // ASIO_DETAIL_WINCE_THREAD_HPP
diff --git a/ext/asio/asio/detail/winsock_init.hpp b/ext/asio/asio/detail/winsock_init.hpp
index ae5c4bf..a410859 100644
--- a/ext/asio/asio/detail/winsock_init.hpp
+++ b/ext/asio/asio/detail/winsock_init.hpp
@@ -1,8 +1,8 @@
//
-// winsock_init.hpp
-// ~~~~~~~~~~~~~~~~
+// detail/winsock_init.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,106 +15,76 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
#include "asio/detail/push_options.hpp"
-#include <boost/shared_ptr.hpp>
-#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error.hpp"
-#include "asio/system_error.hpp"
-#include "asio/detail/noncopyable.hpp"
-#include "asio/detail/socket_types.hpp"
namespace asio {
namespace detail {
-template <int Major = 2, int Minor = 0>
-class winsock_init
- : private noncopyable
+class winsock_init_base
{
-private:
- // Structure to perform the actual initialisation.
- struct do_init
+protected:
+ // Structure to track result of initialisation and number of uses. POD is used
+ // to ensure that the values are zero-initialised prior to any code being run.
+ struct data
{
- do_init()
- {
- WSADATA wsa_data;
- result_ = ::WSAStartup(MAKEWORD(Major, Minor), &wsa_data);
- }
-
- ~do_init()
- {
- ::WSACleanup();
- }
-
- int result() const
- {
- return result_;
- }
-
- // Helper function to manage a do_init singleton. The static instance of the
- // winsock_init object ensures that this function is always called before
- // main, and therefore before any other threads can get started. The do_init
- // instance must be static in this function to ensure that it gets
- // initialised before any other global objects try to use it.
- static boost::shared_ptr<do_init> instance()
- {
- static boost::shared_ptr<do_init> init(new do_init);
- return init;
- }
-
- private:
- int result_;
+ long init_count_;
+ long result_;
};
+ ASIO_DECL static void startup(data& d,
+ unsigned char major, unsigned char minor);
+
+ ASIO_DECL static void cleanup(data& d);
+
+ ASIO_DECL static void throw_on_error(data& d);
+};
+
+template <int Major = 2, int Minor = 0>
+class winsock_init : private winsock_init_base
+{
public:
- // Constructor.
- winsock_init()
- : ref_(do_init::instance())
+ winsock_init(bool allow_throw = true)
+ {
+ startup(data_, Major, Minor);
+ if (allow_throw)
+ throw_on_error(data_);
+ }
+
+ winsock_init(const winsock_init&)
{
- // Check whether winsock was successfully initialised. This check is not
- // performed for the global instance since there will be nobody around to
- // catch the exception.
- if (this != &instance_ && ref_->result() != 0)
- {
- asio::system_error e(
- asio::error_code(ref_->result(),
- asio::error::get_system_category()),
- "winsock");
- boost::throw_exception(e);
- }
+ startup(data_, Major, Minor);
+ throw_on_error(data_);
}
- // Destructor.
~winsock_init()
{
+ cleanup(data_);
}
private:
- // Instance to force initialisation of winsock at global scope.
- static winsock_init instance_;
-
- // Reference to singleton do_init object to ensure that winsock does not get
- // cleaned up until the last user has finished with it.
- boost::shared_ptr<do_init> ref_;
+ static data data_;
};
template <int Major, int Minor>
-winsock_init<Major, Minor> winsock_init<Major, Minor>::instance_;
+winsock_init_base::data winsock_init<Major, Minor>::data_;
+
+// Static variable to ensure that winsock is initialised before main, and
+// therefore before any other threads can get started.
+static const winsock_init<>& winsock_init_instance = winsock_init<>(false);
} // namespace detail
} // namespace asio
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/detail/impl/winsock_init.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
#endif // ASIO_DETAIL_WINSOCK_INIT_HPP
diff --git a/ext/asio/asio/detail/wrapped_handler.hpp b/ext/asio/asio/detail/wrapped_handler.hpp
index e40a7f1..bcebf6e 100644
--- a/ext/asio/asio/detail/wrapped_handler.hpp
+++ b/ext/asio/asio/detail/wrapped_handler.hpp
@@ -1,8 +1,8 @@
//
-// wrapped_handler.hpp
-// ~~~~~~~~~~~~~~~~~~~
+// detail/wrapped_handler.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/type_traits/add_reference.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/detail/bind_handler.hpp"
#include "asio/detail/handler_alloc_helpers.hpp"
#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
@@ -34,9 +30,7 @@ class wrapped_handler
public:
typedef void result_type;
- wrapped_handler(
- typename boost::add_reference<Dispatcher>::type dispatcher,
- Handler handler)
+ wrapped_handler(Dispatcher dispatcher, Handler handler)
: dispatcher_(dispatcher),
handler_(handler)
{
diff --git a/ext/asio/asio/error.hpp b/ext/asio/asio/error.hpp
index 73caac6..138b5d7 100644
--- a/ext/asio/asio/error.hpp
+++ b/ext/asio/asio/error.hpp
@@ -2,7 +2,7 @@
// error.hpp
// ~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,15 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <cerrno>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error_code.hpp"
-#include "asio/detail/socket_types.hpp"
+#include "asio/detail/config.hpp"
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# include <winerror.h>
+#else
+# include <cerrno>
+# include <netdb.h>
+#endif
#if defined(GENERATING_DOCUMENTATION)
/// INTERNAL ONLY.
@@ -50,6 +48,8 @@
# define ASIO_WIN_OR_POSIX(e_win, e_posix) e_posix
#endif
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace error {
@@ -212,7 +212,21 @@ enum ssl_errors
{
};
-// boostify: error category definitions go here.
+// boostify: error category definitions start here.
+
+} // namespace error
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/error_code.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace error {
+
+// boostify: error category definitions end here.
inline asio::error_code make_error_code(basic_errors e)
{
@@ -247,14 +261,16 @@ inline asio::error_code make_error_code(ssl_errors e)
} // namespace error
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#undef ASIO_NATIVE_ERROR
#undef ASIO_SOCKET_ERROR
#undef ASIO_NETDB_ERROR
#undef ASIO_GETADDRINFO_ERROR
#undef ASIO_WIN_OR_POSIX
-#include "asio/impl/error_code.ipp"
-
-#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/impl/error.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
#endif // ASIO_ERROR_HPP
diff --git a/ext/asio/asio/error_code.hpp b/ext/asio/asio/error_code.hpp
index 6657f3f..1af449b 100644
--- a/ext/asio/asio/error_code.hpp
+++ b/ext/asio/asio/error_code.hpp
@@ -2,7 +2,7 @@
// error_code.hpp
// ~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,12 +15,8 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
+#include "asio/detail/config.hpp"
#include <string>
-#include "asio/detail/pop_options.hpp"
#if defined(GENERATING_DOCUMENTATION)
# define ASIO_WIN_OR_POSIX(e_win, e_posix) implementation_defined
@@ -30,6 +26,8 @@
# define ASIO_WIN_OR_POSIX(e_win, e_posix) e_posix
#endif
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace error
@@ -106,7 +104,7 @@ public:
}
/// Get the message associated with the error.
- std::string message() const;
+ ASIO_DECL std::string message() const;
struct unspecified_bool_type_t
{
@@ -114,14 +112,12 @@ public:
typedef void (*unspecified_bool_type)(unspecified_bool_type_t);
- static void unspecified_bool_true(unspecified_bool_type_t)
- {
- }
+ static void unspecified_bool_true(unspecified_bool_type_t) {}
/// Operator returns non-null if there is a non-success error code.
operator unspecified_bool_type() const
{
- if (value_ == 0)
+ if (!value_)
return 0;
else
return &error_code::unspecified_bool_true;
@@ -130,7 +126,7 @@ public:
/// Operator to test if the error represents success.
bool operator!() const
{
- return value_ == 0;
+ return !value_;
}
/// Equality operator to compare two error objects.
@@ -155,10 +151,12 @@ private:
} // namespace asio
-#undef ASIO_WIN_OR_POSIX
+#include "asio/detail/pop_options.hpp"
-#include "asio/error.hpp"
+#undef ASIO_WIN_OR_POSIX
-#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/impl/error_code.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
#endif // ASIO_ERROR_CODE_HPP
diff --git a/ext/asio/asio/handler_alloc_hook.hpp b/ext/asio/asio/handler_alloc_hook.hpp
index 87783cd..dd930b6 100644
--- a/ext/asio/asio/handler_alloc_hook.hpp
+++ b/ext/asio/asio/handler_alloc_hook.hpp
@@ -2,7 +2,7 @@
// handler_alloc_hook.hpp
// ~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,12 +15,10 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+#include <cstddef>
#include "asio/detail/push_options.hpp"
-#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
namespace asio {
diff --git a/ext/asio/asio/handler_invoke_hook.hpp b/ext/asio/asio/handler_invoke_hook.hpp
index b3d7e45..ef22359 100644
--- a/ext/asio/asio/handler_invoke_hook.hpp
+++ b/ext/asio/asio/handler_invoke_hook.hpp
@@ -2,7 +2,7 @@
// handler_invoke_hook.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,6 +15,8 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+#include "asio/detail/config.hpp"
+
#include "asio/detail/push_options.hpp"
namespace asio {
diff --git a/ext/asio/asio/impl/error.ipp b/ext/asio/asio/impl/error.ipp
new file mode 100644
index 0000000..54388b3
--- /dev/null
+++ b/ext/asio/asio/impl/error.ipp
@@ -0,0 +1,33 @@
+//
+// impl/error.ipp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IMPL_ERROR_IPP
+#define ASIO_IMPL_ERROR_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace error {
+
+// boostify: error category function definitions go here.
+
+} // namespace error
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IMPL_ERROR_IPP
diff --git a/ext/asio/asio/impl/error_code.ipp b/ext/asio/asio/impl/error_code.ipp
index 614925d..ed37a17 100644
--- a/ext/asio/asio/impl/error_code.ipp
+++ b/ext/asio/asio/impl/error_code.ipp
@@ -1,50 +1,55 @@
//
-// error_code.ipp
-// ~~~~~~~~~~~~~~
+// impl/error_code.ipp
+// ~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
-#ifndef ASIO_ERROR_CODE_IPP
-#define ASIO_ERROR_CODE_IPP
+#ifndef ASIO_IMPL_ERROR_CODE_IPP
+#define ASIO_IMPL_ERROR_CODE_IPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include <cerrno>
-#include <cstring>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error.hpp"
+#include "asio/detail/config.hpp"
#include "asio/detail/local_free_on_block_exit.hpp"
#include "asio/detail/socket_types.hpp"
+#include "asio/error.hpp"
+#include "asio/error_code.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
-inline std::string error_code::message() const
+std::string error_code::message() const
{
- if (*this == error::already_open)
- return "Already open.";
- if (*this == error::not_found)
- return "Not found.";
- if (*this == error::fd_set_failure)
- return "The descriptor does not fit into the select call's fd_set.";
+ if (category_ == error::get_misc_category())
+ {
+ if (value_ == error::already_open)
+ return "Already open.";
+ if (value_ == error::not_found)
+ return "Not found.";
+ if (value_ == error::fd_set_failure)
+ return "The descriptor does not fit into the select call's fd_set.";
+ if (value_ == error::not_found)
+ return "Element not found.";
+#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+ if (value_ == error::eof)
+ return "End of file.";
+#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
+ }
if (category_ == error::get_ssl_category())
return "SSL error.";
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
value_type value = value_;
- if (category() != error::get_system_category() && *this != error::eof)
- return "asio error";
- if (*this == error::eof)
+ if (category_ == error::get_misc_category() && value_ == error::eof)
value = ERROR_HANDLE_EOF;
+ else if (category_ != error::get_system_category())
+ return "asio error";
char* msg = 0;
DWORD length = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
@@ -60,29 +65,31 @@ inline std::string error_code::message() const
else
return "asio error";
#else // defined(BOOST_WINDOWS)
- if (*this == error::eof)
- return "End of file.";
- if (*this == error::host_not_found)
- return "Host not found (authoritative).";
- if (*this == error::host_not_found_try_again)
- return "Host not found (non-authoritative), try again later.";
- if (*this == error::no_recovery)
- return "A non-recoverable error occurred during database lookup.";
- if (*this == error::no_data)
- return "The query is valid, but it does not have associated data.";
- if (*this == error::not_found)
- return "Element not found.";
+ if (category_ == error::get_netdb_category())
+ {
+ if (value_ == error::host_not_found)
+ return "Host not found (authoritative).";
+ if (value_ == error::host_not_found_try_again)
+ return "Host not found (non-authoritative), try again later.";
+ if (value_ == error::no_recovery)
+ return "A non-recoverable error occurred during database lookup.";
+ if (value_ == error::no_data)
+ return "The query is valid, but it does not have associated data.";
+ }
+ if (category_ == error::get_addrinfo_category())
+ {
+ if (value_ == error::service_not_found)
+ return "Service not found.";
+ if (value_ == error::socket_type_not_supported)
+ return "Socket type not supported.";
+ }
+ if (category_ != error::get_system_category())
+ return "asio error";
#if !defined(__sun)
- if (*this == error::operation_aborted)
+ if (value_ == error::operation_aborted)
return "Operation aborted.";
#endif // !defined(__sun)
- if (*this == error::service_not_found)
- return "Service not found.";
- if (*this == error::socket_type_not_supported)
- return "Socket type not supported.";
- if (category() != error::get_system_category())
- return "asio error";
-#if defined(__sun) || defined(__QNX__)
+#if defined(__sun) || defined(__QNX__) || defined(__SYMBIAN32__)
using namespace std;
return strerror(value_);
#elif defined(__MACH__) && defined(__APPLE__) \
@@ -102,4 +109,4 @@ inline std::string error_code::message() const
#include "asio/detail/pop_options.hpp"
-#endif // ASIO_ERROR_CODE_IPP
+#endif // ASIO_IMPL_ERROR_CODE_IPP
diff --git a/ext/asio/asio/impl/io_service.hpp b/ext/asio/asio/impl/io_service.hpp
new file mode 100644
index 0000000..f0c1730
--- /dev/null
+++ b/ext/asio/asio/impl/io_service.hpp
@@ -0,0 +1,132 @@
+//
+// impl/io_service.hpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IMPL_IO_SERVICE_HPP
+#define ASIO_IMPL_IO_SERVICE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/service_registry.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+
+template <typename Service>
+inline Service& use_service(io_service& ios)
+{
+ // Check that Service meets the necessary type requirements.
+ (void)static_cast<io_service::service*>(static_cast<Service*>(0));
+ (void)static_cast<const io_service::id*>(&Service::id);
+
+ return ios.service_registry_->template use_service<Service>();
+}
+
+template <typename Service>
+inline void add_service(io_service& ios, Service* svc)
+{
+ // Check that Service meets the necessary type requirements.
+ (void)static_cast<io_service::service*>(static_cast<Service*>(0));
+ (void)static_cast<const io_service::id*>(&Service::id);
+
+ ios.service_registry_->template add_service<Service>(svc);
+}
+
+template <typename Service>
+inline bool has_service(io_service& ios)
+{
+ // Check that Service meets the necessary type requirements.
+ (void)static_cast<io_service::service*>(static_cast<Service*>(0));
+ (void)static_cast<const io_service::id*>(&Service::id);
+
+ return ios.service_registry_->template has_service<Service>();
+}
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+# include "asio/detail/win_iocp_io_service.hpp"
+#else
+# include "asio/detail/task_io_service.hpp"
+#endif
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+
+template <typename Handler>
+inline void io_service::dispatch(Handler handler)
+{
+ impl_.dispatch(handler);
+}
+
+template <typename Handler>
+inline void io_service::post(Handler handler)
+{
+ impl_.post(handler);
+}
+
+template <typename Handler>
+#if defined(GENERATING_DOCUMENTATION)
+unspecified
+#else
+inline detail::wrapped_handler<io_service&, Handler>
+#endif
+io_service::wrap(Handler handler)
+{
+ return detail::wrapped_handler<io_service&, Handler>(*this, handler);
+}
+
+inline io_service::work::work(asio::io_service& io_service)
+ : io_service_(io_service)
+{
+ io_service_.impl_.work_started();
+}
+
+inline io_service::work::work(const work& other)
+ : io_service_(other.io_service_)
+{
+ io_service_.impl_.work_started();
+}
+
+inline io_service::work::~work()
+{
+ io_service_.impl_.work_finished();
+}
+
+inline asio::io_service& io_service::work::io_service()
+{
+ return io_service_;
+}
+
+inline asio::io_service& io_service::work::get_io_service()
+{
+ return io_service_;
+}
+
+inline asio::io_service& io_service::service::io_service()
+{
+ return owner_;
+}
+
+inline asio::io_service& io_service::service::get_io_service()
+{
+ return owner_;
+}
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IMPL_IO_SERVICE_HPP
diff --git a/ext/asio/asio/impl/io_service.ipp b/ext/asio/asio/impl/io_service.ipp
index c3fed3b..eb19b8f 100644
--- a/ext/asio/asio/impl/io_service.ipp
+++ b/ext/asio/asio/impl/io_service.ipp
@@ -1,26 +1,23 @@
//
-// io_service.ipp
-// ~~~~~~~~~~~~~~
+// impl/io_service.ipp
+// ~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
-#ifndef ASIO_IO_SERVICE_IPP
-#define ASIO_IO_SERVICE_IPP
+#ifndef ASIO_IMPL_IO_SERVICE_IPP
+#define ASIO_IMPL_IO_SERVICE_IPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <boost/limits.hpp>
-#include "asio/detail/pop_options.hpp"
-
+#include "asio/io_service.hpp"
#include "asio/detail/service_registry.hpp"
#include "asio/detail/throw_error.hpp"
@@ -28,31 +25,32 @@
# include "asio/detail/win_iocp_io_service.hpp"
#else
# include "asio/detail/task_io_service.hpp"
-# include "asio/detail/reactor.hpp"
#endif
+#include "asio/detail/push_options.hpp"
+
namespace asio {
-inline io_service::io_service()
+io_service::io_service()
: service_registry_(new asio::detail::service_registry(*this)),
impl_(service_registry_->use_service<impl_type>())
{
impl_.init((std::numeric_limits<std::size_t>::max)());
}
-inline io_service::io_service(std::size_t concurrency_hint)
+io_service::io_service(std::size_t concurrency_hint)
: service_registry_(new asio::detail::service_registry(*this)),
impl_(service_registry_->use_service<impl_type>())
{
impl_.init(concurrency_hint);
}
-inline io_service::~io_service()
+io_service::~io_service()
{
delete service_registry_;
}
-inline std::size_t io_service::run()
+std::size_t io_service::run()
{
asio::error_code ec;
std::size_t s = impl_.run(ec);
@@ -60,12 +58,12 @@ inline std::size_t io_service::run()
return s;
}
-inline std::size_t io_service::run(asio::error_code& ec)
+std::size_t io_service::run(asio::error_code& ec)
{
return impl_.run(ec);
}
-inline std::size_t io_service::run_one()
+std::size_t io_service::run_one()
{
asio::error_code ec;
std::size_t s = impl_.run_one(ec);
@@ -73,12 +71,12 @@ inline std::size_t io_service::run_one()
return s;
}
-inline std::size_t io_service::run_one(asio::error_code& ec)
+std::size_t io_service::run_one(asio::error_code& ec)
{
return impl_.run_one(ec);
}
-inline std::size_t io_service::poll()
+std::size_t io_service::poll()
{
asio::error_code ec;
std::size_t s = impl_.poll(ec);
@@ -86,12 +84,12 @@ inline std::size_t io_service::poll()
return s;
}
-inline std::size_t io_service::poll(asio::error_code& ec)
+std::size_t io_service::poll(asio::error_code& ec)
{
return impl_.poll(ec);
}
-inline std::size_t io_service::poll_one()
+std::size_t io_service::poll_one()
{
asio::error_code ec;
std::size_t s = impl_.poll_one(ec);
@@ -99,126 +97,43 @@ inline std::size_t io_service::poll_one()
return s;
}
-inline std::size_t io_service::poll_one(asio::error_code& ec)
+std::size_t io_service::poll_one(asio::error_code& ec)
{
return impl_.poll_one(ec);
}
-inline void io_service::stop()
+void io_service::stop()
{
impl_.stop();
}
-inline void io_service::reset()
+void io_service::reset()
{
impl_.reset();
}
-template <typename Handler>
-inline void io_service::dispatch(Handler handler)
-{
- impl_.dispatch(handler);
-}
-
-template <typename Handler>
-inline void io_service::post(Handler handler)
-{
- impl_.post(handler);
-}
-
-template <typename Handler>
-#if defined(GENERATING_DOCUMENTATION)
-unspecified
-#else
-inline detail::wrapped_handler<io_service&, Handler>
-#endif
-io_service::wrap(Handler handler)
-{
- return detail::wrapped_handler<io_service&, Handler>(*this, handler);
-}
-
-inline io_service::work::work(asio::io_service& io_service)
- : io_service_(io_service)
-{
- io_service_.impl_.work_started();
-}
-
-inline io_service::work::work(const work& other)
- : io_service_(other.io_service_)
-{
- io_service_.impl_.work_started();
-}
-
-inline io_service::work::~work()
-{
- io_service_.impl_.work_finished();
-}
-
-inline asio::io_service& io_service::work::io_service()
-{
- return io_service_;
-}
-
-inline asio::io_service& io_service::work::get_io_service()
-{
- return io_service_;
-}
-
-inline io_service::service::service(asio::io_service& owner)
+io_service::service::service(asio::io_service& owner)
: owner_(owner),
next_(0)
{
}
-inline io_service::service::~service()
-{
-}
-
-inline asio::io_service& io_service::service::io_service()
-{
- return owner_;
-}
-
-inline asio::io_service& io_service::service::get_io_service()
+io_service::service::~service()
{
- return owner_;
}
-template <typename Service>
-inline Service& use_service(io_service& ios)
+service_already_exists::service_already_exists()
+ : std::logic_error("Service already exists.")
{
- // Check that Service meets the necessary type requirements.
- (void)static_cast<io_service::service*>(static_cast<Service*>(0));
- (void)static_cast<const io_service::id*>(&Service::id);
-
- return ios.service_registry_->template use_service<Service>();
}
-template <typename Service>
-void add_service(io_service& ios, Service* svc)
+invalid_service_owner::invalid_service_owner()
+ : std::logic_error("Invalid service owner.")
{
- // Check that Service meets the necessary type requirements.
- (void)static_cast<io_service::service*>(static_cast<Service*>(0));
- (void)static_cast<const io_service::id*>(&Service::id);
-
- if (&ios != &svc->io_service())
- boost::throw_exception(invalid_service_owner());
- if (!ios.service_registry_->template add_service<Service>(svc))
- boost::throw_exception(service_already_exists());
-}
-
-template <typename Service>
-bool has_service(io_service& ios)
-{
- // Check that Service meets the necessary type requirements.
- (void)static_cast<io_service::service*>(static_cast<Service*>(0));
- (void)static_cast<const io_service::id*>(&Service::id);
-
- return ios.service_registry_->template has_service<Service>();
}
} // namespace asio
#include "asio/detail/pop_options.hpp"
-#endif // ASIO_IO_SERVICE_IPP
+#endif // ASIO_IMPL_IO_SERVICE_IPP
diff --git a/ext/asio/asio/impl/read.hpp b/ext/asio/asio/impl/read.hpp
new file mode 100644
index 0000000..bd5d7b5
--- /dev/null
+++ b/ext/asio/asio/impl/read.hpp
@@ -0,0 +1,386 @@
+//
+// impl/read.hpp
+// ~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IMPL_READ_HPP
+#define ASIO_IMPL_READ_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include <algorithm>
+#include "asio/buffer.hpp"
+#include "asio/completion_condition.hpp"
+#include "asio/detail/base_from_completion_cond.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/consuming_buffers.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+
+template <typename SyncReadStream, typename MutableBufferSequence,
+ typename CompletionCondition>
+std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers,
+ CompletionCondition completion_condition, asio::error_code& ec)
+{
+ ec = asio::error_code();
+ asio::detail::consuming_buffers<
+ mutable_buffer, MutableBufferSequence> tmp(buffers);
+ std::size_t total_transferred = 0;
+ tmp.prepare(detail::adapt_completion_condition_result(
+ completion_condition(ec, total_transferred)));
+ while (tmp.begin() != tmp.end())
+ {
+ std::size_t bytes_transferred = s.read_some(tmp, ec);
+ tmp.consume(bytes_transferred);
+ total_transferred += bytes_transferred;
+ tmp.prepare(detail::adapt_completion_condition_result(
+ completion_condition(ec, total_transferred)));
+ }
+ return total_transferred;
+}
+
+template <typename SyncReadStream, typename MutableBufferSequence>
+inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = read(s, buffers, transfer_all(), ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+template <typename SyncReadStream, typename MutableBufferSequence,
+ typename CompletionCondition>
+inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers,
+ CompletionCondition completion_condition)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = read(s, buffers, completion_condition, ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+#if !defined(BOOST_NO_IOSTREAM)
+
+template <typename SyncReadStream, typename Allocator,
+ typename CompletionCondition>
+std::size_t read(SyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b,
+ CompletionCondition completion_condition, asio::error_code& ec)
+{
+ ec = asio::error_code();
+ std::size_t total_transferred = 0;
+ std::size_t max_size = detail::adapt_completion_condition_result(
+ completion_condition(ec, total_transferred));
+ std::size_t bytes_available = read_size_helper(b, max_size);
+ while (bytes_available > 0)
+ {
+ std::size_t bytes_transferred = s.read_some(b.prepare(bytes_available), ec);
+ b.commit(bytes_transferred);
+ total_transferred += bytes_transferred;
+ max_size = detail::adapt_completion_condition_result(
+ completion_condition(ec, total_transferred));
+ bytes_available = read_size_helper(b, max_size);
+ }
+ return total_transferred;
+}
+
+template <typename SyncReadStream, typename Allocator>
+inline std::size_t read(SyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = read(s, b, transfer_all(), ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+template <typename SyncReadStream, typename Allocator,
+ typename CompletionCondition>
+inline std::size_t read(SyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b,
+ CompletionCondition completion_condition)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = read(s, b, completion_condition, ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+#endif // !defined(BOOST_NO_IOSTREAM)
+
+namespace detail
+{
+ template <typename AsyncReadStream, typename MutableBufferSequence,
+ typename CompletionCondition, typename ReadHandler>
+ class read_op
+ : detail::base_from_completion_cond<CompletionCondition>
+ {
+ public:
+ read_op(AsyncReadStream& stream, const MutableBufferSequence& buffers,
+ CompletionCondition completion_condition, ReadHandler handler)
+ : detail::base_from_completion_cond<
+ CompletionCondition>(completion_condition),
+ stream_(stream),
+ buffers_(buffers),
+ total_transferred_(0),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ std::size_t bytes_transferred, int start = 0)
+ {
+ switch (start)
+ {
+ case 1:
+ buffers_.prepare(this->check_for_completion(ec, total_transferred_));
+ for (;;)
+ {
+ stream_.async_read_some(buffers_, *this);
+ return; default:
+ total_transferred_ += bytes_transferred;
+ buffers_.consume(bytes_transferred);
+ buffers_.prepare(this->check_for_completion(ec, total_transferred_));
+ if ((!ec && bytes_transferred == 0)
+ || buffers_.begin() == buffers_.end())
+ break;
+ }
+
+ handler_(ec, static_cast<const std::size_t&>(total_transferred_));
+ }
+ }
+
+ //private:
+ AsyncReadStream& stream_;
+ asio::detail::consuming_buffers<
+ mutable_buffer, MutableBufferSequence> buffers_;
+ std::size_t total_transferred_;
+ ReadHandler handler_;
+ };
+
+ template <typename AsyncReadStream,
+ typename CompletionCondition, typename ReadHandler>
+ class read_op<AsyncReadStream, asio::mutable_buffers_1,
+ CompletionCondition, ReadHandler>
+ : detail::base_from_completion_cond<CompletionCondition>
+ {
+ public:
+ read_op(AsyncReadStream& stream,
+ const asio::mutable_buffers_1& buffers,
+ CompletionCondition completion_condition,
+ ReadHandler handler)
+ : detail::base_from_completion_cond<
+ CompletionCondition>(completion_condition),
+ stream_(stream),
+ buffer_(buffers),
+ total_transferred_(0),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ std::size_t bytes_transferred, int start = 0)
+ {
+ std::size_t n = 0;
+ switch (start)
+ {
+ case 1:
+ n = this->check_for_completion(ec, total_transferred_);
+ for (;;)
+ {
+ stream_.async_read_some(asio::buffer(
+ buffer_ + total_transferred_, n), *this);
+ return; default:
+ total_transferred_ += bytes_transferred;
+ if ((!ec && bytes_transferred == 0)
+ || (n = this->check_for_completion(ec, total_transferred_)) == 0
+ || total_transferred_ == asio::buffer_size(buffer_))
+ break;
+ }
+
+ handler_(ec, static_cast<const std::size_t&>(total_transferred_));
+ }
+ }
+
+ //private:
+ AsyncReadStream& stream_;
+ asio::mutable_buffer buffer_;
+ std::size_t total_transferred_;
+ ReadHandler handler_;
+ };
+
+ template <typename AsyncReadStream, typename MutableBufferSequence,
+ typename CompletionCondition, typename ReadHandler>
+ inline void* asio_handler_allocate(std::size_t size,
+ read_op<AsyncReadStream, MutableBufferSequence,
+ CompletionCondition, ReadHandler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, this_handler->handler_);
+ }
+
+ template <typename AsyncReadStream, typename MutableBufferSequence,
+ typename CompletionCondition, typename ReadHandler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ read_op<AsyncReadStream, MutableBufferSequence,
+ CompletionCondition, ReadHandler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, this_handler->handler_);
+ }
+
+ template <typename Function, typename AsyncReadStream,
+ typename MutableBufferSequence, typename CompletionCondition,
+ typename ReadHandler>
+ inline void asio_handler_invoke(const Function& function,
+ read_op<AsyncReadStream, MutableBufferSequence,
+ CompletionCondition, ReadHandler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename AsyncReadStream, typename MutableBufferSequence,
+ typename CompletionCondition, typename ReadHandler>
+inline void async_read(AsyncReadStream& s, const MutableBufferSequence& buffers,
+ CompletionCondition completion_condition, ReadHandler handler)
+{
+ detail::read_op<AsyncReadStream, MutableBufferSequence,
+ CompletionCondition, ReadHandler>(
+ s, buffers, completion_condition, handler)(
+ asio::error_code(), 0, 1);
+}
+
+template <typename AsyncReadStream, typename MutableBufferSequence,
+ typename ReadHandler>
+inline void async_read(AsyncReadStream& s, const MutableBufferSequence& buffers,
+ ReadHandler handler)
+{
+ async_read(s, buffers, transfer_all(), handler);
+}
+
+#if !defined(BOOST_NO_IOSTREAM)
+
+namespace detail
+{
+ template <typename AsyncReadStream, typename Allocator,
+ typename CompletionCondition, typename ReadHandler>
+ class read_streambuf_op
+ : detail::base_from_completion_cond<CompletionCondition>
+ {
+ public:
+ read_streambuf_op(AsyncReadStream& stream,
+ basic_streambuf<Allocator>& streambuf,
+ CompletionCondition completion_condition, ReadHandler handler)
+ : detail::base_from_completion_cond<
+ CompletionCondition>(completion_condition),
+ stream_(stream),
+ streambuf_(streambuf),
+ total_transferred_(0),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ std::size_t bytes_transferred, int start = 0)
+ {
+ std::size_t max_size, bytes_available;
+ switch (start)
+ {
+ case 1:
+ max_size = this->check_for_completion(ec, total_transferred_);
+ bytes_available = read_size_helper(streambuf_, max_size);
+ for (;;)
+ {
+ stream_.async_read_some(streambuf_.prepare(bytes_available), *this);
+ return; default:
+ total_transferred_ += bytes_transferred;
+ streambuf_.commit(bytes_transferred);
+ max_size = this->check_for_completion(ec, total_transferred_);
+ bytes_available = read_size_helper(streambuf_, max_size);
+ if ((!ec && bytes_transferred == 0) || bytes_available == 0)
+ break;
+ }
+
+ handler_(ec, static_cast<const std::size_t&>(total_transferred_));
+ }
+ }
+
+ //private:
+ AsyncReadStream& stream_;
+ asio::basic_streambuf<Allocator>& streambuf_;
+ std::size_t total_transferred_;
+ ReadHandler handler_;
+ };
+
+ template <typename AsyncReadStream, typename Allocator,
+ typename CompletionCondition, typename ReadHandler>
+ inline void* asio_handler_allocate(std::size_t size,
+ read_streambuf_op<AsyncReadStream, Allocator,
+ CompletionCondition, ReadHandler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, this_handler->handler_);
+ }
+
+ template <typename AsyncReadStream, typename Allocator,
+ typename CompletionCondition, typename ReadHandler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ read_streambuf_op<AsyncReadStream, Allocator,
+ CompletionCondition, ReadHandler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, this_handler->handler_);
+ }
+
+ template <typename Function, typename AsyncReadStream,
+ typename Allocator, typename CompletionCondition, typename ReadHandler>
+ inline void asio_handler_invoke(const Function& function,
+ read_streambuf_op<AsyncReadStream, Allocator,
+ CompletionCondition, ReadHandler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename AsyncReadStream, typename Allocator,
+ typename CompletionCondition, typename ReadHandler>
+inline void async_read(AsyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b,
+ CompletionCondition completion_condition, ReadHandler handler)
+{
+ detail::read_streambuf_op<AsyncReadStream,
+ Allocator, CompletionCondition, ReadHandler>(
+ s, b, completion_condition, handler)(
+ asio::error_code(), 0, 1);
+}
+
+template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
+inline void async_read(AsyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b, ReadHandler handler)
+{
+ async_read(s, b, transfer_all(), handler);
+}
+
+#endif // !defined(BOOST_NO_IOSTREAM)
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IMPL_READ_HPP
diff --git a/ext/asio/asio/impl/read_at.hpp b/ext/asio/asio/impl/read_at.hpp
new file mode 100644
index 0000000..da57fe3
--- /dev/null
+++ b/ext/asio/asio/impl/read_at.hpp
@@ -0,0 +1,410 @@
+//
+// impl/read_at.hpp
+// ~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IMPL_READ_AT_HPP
+#define ASIO_IMPL_READ_AT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include <algorithm>
+#include "asio/buffer.hpp"
+#include "asio/completion_condition.hpp"
+#include "asio/detail/base_from_completion_cond.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/consuming_buffers.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+
+template <typename SyncRandomAccessReadDevice, typename MutableBufferSequence,
+ typename CompletionCondition>
+std::size_t read_at(SyncRandomAccessReadDevice& d,
+ boost::uint64_t offset, const MutableBufferSequence& buffers,
+ CompletionCondition completion_condition, asio::error_code& ec)
+{
+ ec = asio::error_code();
+ asio::detail::consuming_buffers<
+ mutable_buffer, MutableBufferSequence> tmp(buffers);
+ std::size_t total_transferred = 0;
+ tmp.prepare(detail::adapt_completion_condition_result(
+ completion_condition(ec, total_transferred)));
+ while (tmp.begin() != tmp.end())
+ {
+ std::size_t bytes_transferred = d.read_some_at(
+ offset + total_transferred, tmp, ec);
+ tmp.consume(bytes_transferred);
+ total_transferred += bytes_transferred;
+ tmp.prepare(detail::adapt_completion_condition_result(
+ completion_condition(ec, total_transferred)));
+ }
+ return total_transferred;
+}
+
+template <typename SyncRandomAccessReadDevice, typename MutableBufferSequence>
+inline std::size_t read_at(SyncRandomAccessReadDevice& d,
+ boost::uint64_t offset, const MutableBufferSequence& buffers)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = read_at(
+ d, offset, buffers, transfer_all(), ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+template <typename SyncRandomAccessReadDevice, typename MutableBufferSequence,
+ typename CompletionCondition>
+inline std::size_t read_at(SyncRandomAccessReadDevice& d,
+ boost::uint64_t offset, const MutableBufferSequence& buffers,
+ CompletionCondition completion_condition)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = read_at(
+ d, offset, buffers, completion_condition, ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+#if !defined(BOOST_NO_IOSTREAM)
+
+template <typename SyncRandomAccessReadDevice, typename Allocator,
+ typename CompletionCondition>
+std::size_t read_at(SyncRandomAccessReadDevice& d,
+ boost::uint64_t offset, asio::basic_streambuf<Allocator>& b,
+ CompletionCondition completion_condition, asio::error_code& ec)
+{
+ ec = asio::error_code();
+ std::size_t total_transferred = 0;
+ std::size_t max_size = detail::adapt_completion_condition_result(
+ completion_condition(ec, total_transferred));
+ std::size_t bytes_available = read_size_helper(b, max_size);
+ while (bytes_available > 0)
+ {
+ std::size_t bytes_transferred = d.read_some_at(
+ offset + total_transferred, b.prepare(bytes_available), ec);
+ b.commit(bytes_transferred);
+ total_transferred += bytes_transferred;
+ max_size = detail::adapt_completion_condition_result(
+ completion_condition(ec, total_transferred));
+ bytes_available = read_size_helper(b, max_size);
+ }
+ return total_transferred;
+}
+
+template <typename SyncRandomAccessReadDevice, typename Allocator>
+inline std::size_t read_at(SyncRandomAccessReadDevice& d,
+ boost::uint64_t offset, asio::basic_streambuf<Allocator>& b)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = read_at(
+ d, offset, b, transfer_all(), ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+template <typename SyncRandomAccessReadDevice, typename Allocator,
+ typename CompletionCondition>
+inline std::size_t read_at(SyncRandomAccessReadDevice& d,
+ boost::uint64_t offset, asio::basic_streambuf<Allocator>& b,
+ CompletionCondition completion_condition)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = read_at(
+ d, offset, b, completion_condition, ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+#endif // !defined(BOOST_NO_IOSTREAM)
+
+namespace detail
+{
+ template <typename AsyncRandomAccessReadDevice,
+ typename MutableBufferSequence, typename CompletionCondition,
+ typename ReadHandler>
+ class read_at_op
+ : detail::base_from_completion_cond<CompletionCondition>
+ {
+ public:
+ read_at_op(AsyncRandomAccessReadDevice& device,
+ boost::uint64_t offset, const MutableBufferSequence& buffers,
+ CompletionCondition completion_condition, ReadHandler handler)
+ : detail::base_from_completion_cond<
+ CompletionCondition>(completion_condition),
+ device_(device),
+ offset_(offset),
+ buffers_(buffers),
+ total_transferred_(0),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ std::size_t bytes_transferred, int start = 0)
+ {
+ switch (start)
+ {
+ case 1:
+ buffers_.prepare(this->check_for_completion(ec, total_transferred_));
+ for (;;)
+ {
+ device_.async_read_some_at(
+ offset_ + total_transferred_, buffers_, *this);
+ return; default:
+ total_transferred_ += bytes_transferred;
+ buffers_.consume(bytes_transferred);
+ buffers_.prepare(this->check_for_completion(ec, total_transferred_));
+ if ((!ec && bytes_transferred == 0)
+ || buffers_.begin() == buffers_.end())
+ break;
+ }
+
+ handler_(ec, static_cast<const std::size_t&>(total_transferred_));
+ }
+ }
+
+ //private:
+ AsyncRandomAccessReadDevice& device_;
+ boost::uint64_t offset_;
+ asio::detail::consuming_buffers<
+ mutable_buffer, MutableBufferSequence> buffers_;
+ std::size_t total_transferred_;
+ ReadHandler handler_;
+ };
+
+ template <typename AsyncRandomAccessReadDevice,
+ typename CompletionCondition, typename ReadHandler>
+ class read_at_op<AsyncRandomAccessReadDevice,
+ asio::mutable_buffers_1, CompletionCondition, ReadHandler>
+ : detail::base_from_completion_cond<CompletionCondition>
+ {
+ public:
+ read_at_op(AsyncRandomAccessReadDevice& device,
+ boost::uint64_t offset, const asio::mutable_buffers_1& buffers,
+ CompletionCondition completion_condition, ReadHandler handler)
+ : detail::base_from_completion_cond<
+ CompletionCondition>(completion_condition),
+ device_(device),
+ offset_(offset),
+ buffer_(buffers),
+ total_transferred_(0),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ std::size_t bytes_transferred, int start = 0)
+ {
+ std::size_t n = 0;
+ switch (start)
+ {
+ case 1:
+ n = this->check_for_completion(ec, total_transferred_);
+ for (;;)
+ {
+ device_.async_read_some_at(offset_ + total_transferred_,
+ asio::buffer(buffer_ + total_transferred_, n), *this);
+ return; default:
+ total_transferred_ += bytes_transferred;
+ if ((!ec && bytes_transferred == 0)
+ || (n = this->check_for_completion(ec, total_transferred_)) == 0
+ || total_transferred_ == asio::buffer_size(buffer_))
+ break;
+ }
+
+ handler_(ec, static_cast<const std::size_t&>(total_transferred_));
+ }
+ }
+
+ //private:
+ AsyncRandomAccessReadDevice& device_;
+ boost::uint64_t offset_;
+ asio::mutable_buffer buffer_;
+ std::size_t total_transferred_;
+ ReadHandler handler_;
+ };
+
+ template <typename AsyncRandomAccessReadDevice,
+ typename MutableBufferSequence, typename CompletionCondition,
+ typename ReadHandler>
+ inline void* asio_handler_allocate(std::size_t size,
+ read_at_op<AsyncRandomAccessReadDevice, MutableBufferSequence,
+ CompletionCondition, ReadHandler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, this_handler->handler_);
+ }
+
+ template <typename AsyncRandomAccessReadDevice,
+ typename MutableBufferSequence, typename CompletionCondition,
+ typename ReadHandler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ read_at_op<AsyncRandomAccessReadDevice, MutableBufferSequence,
+ CompletionCondition, ReadHandler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, this_handler->handler_);
+ }
+
+ template <typename Function, typename AsyncRandomAccessReadDevice,
+ typename MutableBufferSequence, typename CompletionCondition,
+ typename ReadHandler>
+ inline void asio_handler_invoke(const Function& function,
+ read_at_op<AsyncRandomAccessReadDevice, MutableBufferSequence,
+ CompletionCondition, ReadHandler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename AsyncRandomAccessReadDevice, typename MutableBufferSequence,
+ typename CompletionCondition, typename ReadHandler>
+inline void async_read_at(AsyncRandomAccessReadDevice& d,
+ boost::uint64_t offset, const MutableBufferSequence& buffers,
+ CompletionCondition completion_condition, ReadHandler handler)
+{
+ detail::read_at_op<AsyncRandomAccessReadDevice,
+ MutableBufferSequence, CompletionCondition, ReadHandler>(
+ d, offset, buffers, completion_condition, handler)(
+ asio::error_code(), 0, 1);
+}
+
+template <typename AsyncRandomAccessReadDevice, typename MutableBufferSequence,
+ typename ReadHandler>
+inline void async_read_at(AsyncRandomAccessReadDevice& d,
+ boost::uint64_t offset, const MutableBufferSequence& buffers,
+ ReadHandler handler)
+{
+ async_read_at(d, offset, buffers, transfer_all(), handler);
+}
+
+#if !defined(BOOST_NO_IOSTREAM)
+
+namespace detail
+{
+ template <typename AsyncRandomAccessReadDevice, typename Allocator,
+ typename CompletionCondition, typename ReadHandler>
+ class read_at_streambuf_op
+ : detail::base_from_completion_cond<CompletionCondition>
+ {
+ public:
+ read_at_streambuf_op(AsyncRandomAccessReadDevice& device,
+ boost::uint64_t offset, basic_streambuf<Allocator>& streambuf,
+ CompletionCondition completion_condition, ReadHandler handler)
+ : detail::base_from_completion_cond<
+ CompletionCondition>(completion_condition),
+ device_(device),
+ offset_(offset),
+ streambuf_(streambuf),
+ total_transferred_(0),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ std::size_t bytes_transferred, int start = 0)
+ {
+ std::size_t max_size, bytes_available;
+ switch (start)
+ {
+ case 1:
+ max_size = this->check_for_completion(ec, total_transferred_);
+ bytes_available = read_size_helper(streambuf_, max_size);
+ for (;;)
+ {
+ device_.async_read_some_at(offset_ + total_transferred_,
+ streambuf_.prepare(bytes_available), *this);
+ return; default:
+ total_transferred_ += bytes_transferred;
+ streambuf_.commit(bytes_transferred);
+ max_size = this->check_for_completion(ec, total_transferred_);
+ bytes_available = read_size_helper(streambuf_, max_size);
+ if ((!ec && bytes_transferred == 0) || bytes_available == 0)
+ break;
+ }
+
+ handler_(ec, static_cast<const std::size_t&>(total_transferred_));
+ }
+ }
+
+ //private:
+ AsyncRandomAccessReadDevice& device_;
+ boost::uint64_t offset_;
+ asio::basic_streambuf<Allocator>& streambuf_;
+ std::size_t total_transferred_;
+ ReadHandler handler_;
+ };
+
+ template <typename AsyncRandomAccessReadDevice, typename Allocator,
+ typename CompletionCondition, typename ReadHandler>
+ inline void* asio_handler_allocate(std::size_t size,
+ read_at_streambuf_op<AsyncRandomAccessReadDevice, Allocator,
+ CompletionCondition, ReadHandler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, this_handler->handler_);
+ }
+
+ template <typename AsyncRandomAccessReadDevice, typename Allocator,
+ typename CompletionCondition, typename ReadHandler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ read_at_streambuf_op<AsyncRandomAccessReadDevice, Allocator,
+ CompletionCondition, ReadHandler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, this_handler->handler_);
+ }
+
+ template <typename Function, typename AsyncRandomAccessReadDevice,
+ typename Allocator, typename CompletionCondition, typename ReadHandler>
+ inline void asio_handler_invoke(const Function& function,
+ read_at_streambuf_op<AsyncRandomAccessReadDevice, Allocator,
+ CompletionCondition, ReadHandler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename AsyncRandomAccessReadDevice, typename Allocator,
+ typename CompletionCondition, typename ReadHandler>
+inline void async_read_at(AsyncRandomAccessReadDevice& d,
+ boost::uint64_t offset, asio::basic_streambuf<Allocator>& b,
+ CompletionCondition completion_condition, ReadHandler handler)
+{
+ detail::read_at_streambuf_op<AsyncRandomAccessReadDevice,
+ Allocator, CompletionCondition, ReadHandler>(
+ d, offset, b, completion_condition, handler)(
+ asio::error_code(), 0, 1);
+}
+
+template <typename AsyncRandomAccessReadDevice, typename Allocator,
+ typename ReadHandler>
+inline void async_read_at(AsyncRandomAccessReadDevice& d,
+ boost::uint64_t offset, asio::basic_streambuf<Allocator>& b,
+ ReadHandler handler)
+{
+ async_read_at(d, offset, b, transfer_all(), handler);
+}
+
+#endif // !defined(BOOST_NO_IOSTREAM)
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IMPL_READ_AT_HPP
diff --git a/ext/asio/asio/impl/read_until.hpp b/ext/asio/asio/impl/read_until.hpp
new file mode 100644
index 0000000..ceacca7
--- /dev/null
+++ b/ext/asio/asio/impl/read_until.hpp
@@ -0,0 +1,902 @@
+//
+// impl/read_until.hpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IMPL_READ_UNTIL_HPP
+#define ASIO_IMPL_READ_UNTIL_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include <algorithm>
+#include <string>
+#include <vector>
+#include <utility>
+#include <boost/limits.hpp>
+#include "asio/buffer.hpp"
+#include "asio/buffers_iterator.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/throw_error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+
+template <typename SyncReadStream, typename Allocator>
+inline std::size_t read_until(SyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b, char delim)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = read_until(s, b, delim, ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+template <typename SyncReadStream, typename Allocator>
+std::size_t read_until(SyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b, char delim,
+ asio::error_code& ec)
+{
+ std::size_t search_position = 0;
+ for (;;)
+ {
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::buffers_iterator<const_buffers_type> iterator;
+ const_buffers_type buffers = b.data();
+ iterator begin = iterator::begin(buffers);
+ iterator start = begin + search_position;
+ iterator end = iterator::end(buffers);
+
+ // Look for a match.
+ iterator iter = std::find(start, end, delim);
+ if (iter != end)
+ {
+ // Found a match. We're done.
+ ec = asio::error_code();
+ return iter - begin + 1;
+ }
+ else
+ {
+ // No match. Next search can start with the new data.
+ search_position = end - begin;
+ }
+
+ // Check if buffer is full.
+ if (b.size() == b.max_size())
+ {
+ ec = error::not_found;
+ return 0;
+ }
+
+ // Need more data.
+ std::size_t bytes_to_read = read_size_helper(b, 65536);
+ b.commit(s.read_some(b.prepare(bytes_to_read), ec));
+ if (ec)
+ return 0;
+ }
+}
+
+template <typename SyncReadStream, typename Allocator>
+inline std::size_t read_until(SyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b, const std::string& delim)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = read_until(s, b, delim, ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+namespace detail
+{
+ // Algorithm that finds a subsequence of equal values in a sequence. Returns
+ // (iterator,true) if a full match was found, in which case the iterator
+ // points to the beginning of the match. Returns (iterator,false) if a
+ // partial match was found at the end of the first sequence, in which case
+ // the iterator points to the beginning of the partial match. Returns
+ // (last1,false) if no full or partial match was found.
+ template <typename Iterator1, typename Iterator2>
+ std::pair<Iterator1, bool> partial_search(
+ Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
+ {
+ for (Iterator1 iter1 = first1; iter1 != last1; ++iter1)
+ {
+ Iterator1 test_iter1 = iter1;
+ Iterator2 test_iter2 = first2;
+ for (;; ++test_iter1, ++test_iter2)
+ {
+ if (test_iter2 == last2)
+ return std::make_pair(iter1, true);
+ if (test_iter1 == last1)
+ {
+ if (test_iter2 != first2)
+ return std::make_pair(iter1, false);
+ else
+ break;
+ }
+ if (*test_iter1 != *test_iter2)
+ break;
+ }
+ }
+ return std::make_pair(last1, false);
+ }
+} // namespace detail
+
+template <typename SyncReadStream, typename Allocator>
+std::size_t read_until(SyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b, const std::string& delim,
+ asio::error_code& ec)
+{
+ std::size_t search_position = 0;
+ for (;;)
+ {
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::buffers_iterator<const_buffers_type> iterator;
+ const_buffers_type buffers = b.data();
+ iterator begin = iterator::begin(buffers);
+ iterator start = begin + search_position;
+ iterator end = iterator::end(buffers);
+
+ // Look for a match.
+ std::pair<iterator, bool> result = detail::partial_search(
+ start, end, delim.begin(), delim.end());
+ if (result.first != end)
+ {
+ if (result.second)
+ {
+ // Full match. We're done.
+ ec = asio::error_code();
+ return result.first - begin + delim.length();
+ }
+ else
+ {
+ // Partial match. Next search needs to start from beginning of match.
+ search_position = result.first - begin;
+ }
+ }
+ else
+ {
+ // No match. Next search can start with the new data.
+ search_position = end - begin;
+ }
+
+ // Check if buffer is full.
+ if (b.size() == b.max_size())
+ {
+ ec = error::not_found;
+ return 0;
+ }
+
+ // Need more data.
+ std::size_t bytes_to_read = read_size_helper(b, 65536);
+ b.commit(s.read_some(b.prepare(bytes_to_read), ec));
+ if (ec)
+ return 0;
+ }
+}
+
+template <typename SyncReadStream, typename Allocator>
+inline std::size_t read_until(SyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b, const boost::regex& expr)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = read_until(s, b, expr, ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+template <typename SyncReadStream, typename Allocator>
+std::size_t read_until(SyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b, const boost::regex& expr,
+ asio::error_code& ec)
+{
+ std::size_t search_position = 0;
+ for (;;)
+ {
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::buffers_iterator<const_buffers_type> iterator;
+ const_buffers_type buffers = b.data();
+ iterator begin = iterator::begin(buffers);
+ iterator start = begin + search_position;
+ iterator end = iterator::end(buffers);
+
+ // Look for a match.
+ boost::match_results<iterator,
+ typename std::vector<boost::sub_match<iterator> >::allocator_type>
+ match_results;
+ if (regex_search(start, end, match_results, expr,
+ boost::match_default | boost::match_partial))
+ {
+ if (match_results[0].matched)
+ {
+ // Full match. We're done.
+ ec = asio::error_code();
+ return match_results[0].second - begin;
+ }
+ else
+ {
+ // Partial match. Next search needs to start from beginning of match.
+ search_position = match_results[0].first - begin;
+ }
+ }
+ else
+ {
+ // No match. Next search can start with the new data.
+ search_position = end - begin;
+ }
+
+ // Check if buffer is full.
+ if (b.size() == b.max_size())
+ {
+ ec = error::not_found;
+ return 0;
+ }
+
+ // Need more data.
+ std::size_t bytes_to_read = read_size_helper(b, 65536);
+ b.commit(s.read_some(b.prepare(bytes_to_read), ec));
+ if (ec)
+ return 0;
+ }
+}
+
+template <typename SyncReadStream, typename Allocator, typename MatchCondition>
+std::size_t read_until(SyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b,
+ MatchCondition match_condition, asio::error_code& ec,
+ typename boost::enable_if<is_match_condition<MatchCondition> >::type*)
+{
+ std::size_t search_position = 0;
+ for (;;)
+ {
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::buffers_iterator<const_buffers_type> iterator;
+ const_buffers_type buffers = b.data();
+ iterator begin = iterator::begin(buffers);
+ iterator start = begin + search_position;
+ iterator end = iterator::end(buffers);
+
+ // Look for a match.
+ std::pair<iterator, bool> result = match_condition(start, end);
+ if (result.second)
+ {
+ // Full match. We're done.
+ ec = asio::error_code();
+ return result.first - begin;
+ }
+ else if (result.first != end)
+ {
+ // Partial match. Next search needs to start from beginning of match.
+ search_position = result.first - begin;
+ }
+ else
+ {
+ // No match. Next search can start with the new data.
+ search_position = end - begin;
+ }
+
+ // Check if buffer is full.
+ if (b.size() == b.max_size())
+ {
+ ec = error::not_found;
+ return 0;
+ }
+
+ // Need more data.
+ std::size_t bytes_to_read = read_size_helper(b, 65536);
+ b.commit(s.read_some(b.prepare(bytes_to_read), ec));
+ if (ec)
+ return 0;
+ }
+}
+
+template <typename SyncReadStream, typename Allocator, typename MatchCondition>
+inline std::size_t read_until(SyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b, MatchCondition match_condition,
+ typename boost::enable_if<is_match_condition<MatchCondition> >::type*)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = read_until(s, b, match_condition, ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+namespace detail
+{
+ template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
+ class read_until_delim_op
+ {
+ public:
+ read_until_delim_op(AsyncReadStream& stream,
+ asio::basic_streambuf<Allocator>& streambuf,
+ char delim, ReadHandler handler)
+ : stream_(stream),
+ streambuf_(streambuf),
+ delim_(delim),
+ search_position_(0),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ std::size_t bytes_transferred, int start = 0)
+ {
+ const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
+ std::size_t bytes_to_read;
+ switch (start)
+ {
+ case 1:
+ for (;;)
+ {
+ {
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::buffers_iterator<const_buffers_type> iterator;
+ const_buffers_type buffers = streambuf_.data();
+ iterator begin = iterator::begin(buffers);
+ iterator start = begin + search_position_;
+ iterator end = iterator::end(buffers);
+
+ // Look for a match.
+ iterator iter = std::find(start, end, delim_);
+ if (iter != end)
+ {
+ // Found a match. We're done.
+ search_position_ = iter - begin + 1;
+ bytes_to_read = 0;
+ }
+
+ // No match yet. Check if buffer is full.
+ else if (streambuf_.size() == streambuf_.max_size())
+ {
+ search_position_ = not_found;
+ bytes_to_read = 0;
+ }
+
+ // Need to read some more data.
+ else
+ {
+ // Next search can start with the new data.
+ search_position_ = end - begin;
+ bytes_to_read = read_size_helper(streambuf_, 65536);
+ }
+ }
+
+ // Check if we're done.
+ if (!start && bytes_to_read == 0)
+ break;
+
+ // Start a new asynchronous read operation to obtain more data.
+ stream_.async_read_some(streambuf_.prepare(bytes_to_read), *this);
+ return; default:
+ streambuf_.commit(bytes_transferred);
+ if (ec || bytes_transferred == 0)
+ break;
+ }
+
+ const asio::error_code result_ec =
+ (search_position_ == not_found)
+ ? error::not_found : ec;
+
+ const std::size_t result_n =
+ (ec || search_position_ == not_found)
+ ? 0 : search_position_;
+
+ handler_(result_ec, result_n);
+ }
+ }
+
+ //private:
+ AsyncReadStream& stream_;
+ asio::basic_streambuf<Allocator>& streambuf_;
+ char delim_;
+ std::size_t search_position_;
+ ReadHandler handler_;
+ };
+
+ template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
+ inline void* asio_handler_allocate(std::size_t size,
+ read_until_delim_op<AsyncReadStream,
+ Allocator, ReadHandler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, this_handler->handler_);
+ }
+
+ template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ read_until_delim_op<AsyncReadStream,
+ Allocator, ReadHandler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, this_handler->handler_);
+ }
+
+ template <typename Function, typename AsyncReadStream, typename Allocator,
+ typename ReadHandler>
+ inline void asio_handler_invoke(const Function& function,
+ read_until_delim_op<AsyncReadStream,
+ Allocator, ReadHandler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
+void async_read_until(AsyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b, char delim, ReadHandler handler)
+{
+ detail::read_until_delim_op<
+ AsyncReadStream, Allocator, ReadHandler>(
+ s, b, delim, handler)(
+ asio::error_code(), 0, 1);
+}
+
+namespace detail
+{
+ template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
+ class read_until_delim_string_op
+ {
+ public:
+ read_until_delim_string_op(AsyncReadStream& stream,
+ asio::basic_streambuf<Allocator>& streambuf,
+ const std::string& delim, ReadHandler handler)
+ : stream_(stream),
+ streambuf_(streambuf),
+ delim_(delim),
+ search_position_(0),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ std::size_t bytes_transferred, int start = 0)
+ {
+ const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
+ std::size_t bytes_to_read;
+ switch (start)
+ {
+ case 1:
+ for (;;)
+ {
+ {
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::buffers_iterator<const_buffers_type> iterator;
+ const_buffers_type buffers = streambuf_.data();
+ iterator begin = iterator::begin(buffers);
+ iterator start = begin + search_position_;
+ iterator end = iterator::end(buffers);
+
+ // Look for a match.
+ std::pair<iterator, bool> result = detail::partial_search(
+ start, end, delim_.begin(), delim_.end());
+ if (result.first != end && result.second)
+ {
+ // Full match. We're done.
+ search_position_ = result.first - begin + delim_.length();
+ bytes_to_read = 0;
+ }
+
+ // No match yet. Check if buffer is full.
+ else if (streambuf_.size() == streambuf_.max_size())
+ {
+ search_position_ = not_found;
+ bytes_to_read = 0;
+ }
+
+ // Need to read some more data.
+ else
+ {
+ if (result.first != end)
+ {
+ // Partial match. Next search needs to start from beginning of
+ // match.
+ search_position_ = result.first - begin;
+ }
+ else
+ {
+ // Next search can start with the new data.
+ search_position_ = end - begin;
+ }
+
+ bytes_to_read = read_size_helper(streambuf_, 65536);
+ }
+ }
+
+ // Check if we're done.
+ if (!start && bytes_to_read == 0)
+ break;
+
+ // Start a new asynchronous read operation to obtain more data.
+ stream_.async_read_some(streambuf_.prepare(bytes_to_read), *this);
+ return; default:
+ streambuf_.commit(bytes_transferred);
+ if (ec || bytes_transferred == 0)
+ break;
+ }
+
+ const asio::error_code result_ec =
+ (search_position_ == not_found)
+ ? error::not_found : ec;
+
+ const std::size_t result_n =
+ (ec || search_position_ == not_found)
+ ? 0 : search_position_;
+
+ handler_(result_ec, result_n);
+ }
+ }
+
+ //private:
+ AsyncReadStream& stream_;
+ asio::basic_streambuf<Allocator>& streambuf_;
+ std::string delim_;
+ std::size_t search_position_;
+ ReadHandler handler_;
+ };
+
+ template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
+ inline void* asio_handler_allocate(std::size_t size,
+ read_until_delim_string_op<AsyncReadStream,
+ Allocator, ReadHandler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, this_handler->handler_);
+ }
+
+ template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ read_until_delim_string_op<AsyncReadStream,
+ Allocator, ReadHandler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, this_handler->handler_);
+ }
+
+ template <typename Function, typename AsyncReadStream,
+ typename Allocator, typename ReadHandler>
+ inline void asio_handler_invoke(const Function& function,
+ read_until_delim_string_op<AsyncReadStream,
+ Allocator, ReadHandler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
+void async_read_until(AsyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b, const std::string& delim,
+ ReadHandler handler)
+{
+ detail::read_until_delim_string_op<
+ AsyncReadStream, Allocator, ReadHandler>(
+ s, b, delim, handler)(
+ asio::error_code(), 0, 1);
+}
+
+namespace detail
+{
+ template <typename AsyncReadStream, typename Allocator,
+ typename RegEx, typename ReadHandler>
+ class read_until_expr_op
+ {
+ public:
+ read_until_expr_op(AsyncReadStream& stream,
+ asio::basic_streambuf<Allocator>& streambuf,
+ const boost::regex& expr, ReadHandler handler)
+ : stream_(stream),
+ streambuf_(streambuf),
+ expr_(expr),
+ search_position_(0),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ std::size_t bytes_transferred, int start = 0)
+ {
+ const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
+ std::size_t bytes_to_read;
+ switch (start)
+ {
+ case 1:
+ for (;;)
+ {
+ {
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::buffers_iterator<const_buffers_type> iterator;
+ const_buffers_type buffers = streambuf_.data();
+ iterator begin = iterator::begin(buffers);
+ iterator start = begin + search_position_;
+ iterator end = iterator::end(buffers);
+
+ // Look for a match.
+ boost::match_results<iterator,
+ typename std::vector<boost::sub_match<iterator> >::allocator_type>
+ match_results;
+ bool match = regex_search(start, end, match_results, expr_,
+ boost::match_default | boost::match_partial);
+ if (match && match_results[0].matched)
+ {
+ // Full match. We're done.
+ search_position_ = match_results[0].second - begin;
+ bytes_to_read = 0;
+ }
+
+ // No match yet. Check if buffer is full.
+ else if (streambuf_.size() == streambuf_.max_size())
+ {
+ search_position_ = not_found;
+ bytes_to_read = 0;
+ }
+
+ // Need to read some more data.
+ else
+ {
+ if (match)
+ {
+ // Partial match. Next search needs to start from beginning of
+ // match.
+ search_position_ = match_results[0].first - begin;
+ }
+ else
+ {
+ // Next search can start with the new data.
+ search_position_ = end - begin;
+ }
+
+ bytes_to_read = read_size_helper(streambuf_, 65536);
+ }
+ }
+
+ // Check if we're done.
+ if (!start && bytes_to_read == 0)
+ break;
+
+ // Start a new asynchronous read operation to obtain more data.
+ stream_.async_read_some(streambuf_.prepare(bytes_to_read), *this);
+ return; default:
+ streambuf_.commit(bytes_transferred);
+ if (ec || bytes_transferred == 0)
+ break;
+ }
+
+ const asio::error_code result_ec =
+ (search_position_ == not_found)
+ ? error::not_found : ec;
+
+ const std::size_t result_n =
+ (ec || search_position_ == not_found)
+ ? 0 : search_position_;
+
+ handler_(result_ec, result_n);
+ }
+ }
+
+ //private:
+ AsyncReadStream& stream_;
+ asio::basic_streambuf<Allocator>& streambuf_;
+ RegEx expr_;
+ std::size_t search_position_;
+ ReadHandler handler_;
+ };
+
+ template <typename AsyncReadStream, typename Allocator,
+ typename RegEx, typename ReadHandler>
+ inline void* asio_handler_allocate(std::size_t size,
+ read_until_expr_op<AsyncReadStream,
+ Allocator, RegEx, ReadHandler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, this_handler->handler_);
+ }
+
+ template <typename AsyncReadStream, typename Allocator,
+ typename RegEx, typename ReadHandler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ read_until_expr_op<AsyncReadStream,
+ Allocator, RegEx, ReadHandler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, this_handler->handler_);
+ }
+
+ template <typename Function, typename AsyncReadStream, typename Allocator,
+ typename RegEx, typename ReadHandler>
+ inline void asio_handler_invoke(const Function& function,
+ read_until_expr_op<AsyncReadStream,
+ Allocator, RegEx, ReadHandler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename AsyncReadStream, typename Allocator, typename ReadHandler>
+void async_read_until(AsyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b, const boost::regex& expr,
+ ReadHandler handler)
+{
+ detail::read_until_expr_op<AsyncReadStream,
+ Allocator, boost::regex, ReadHandler>(
+ s, b, expr, handler)(
+ asio::error_code(), 0, 1);
+}
+
+namespace detail
+{
+ template <typename AsyncReadStream, typename Allocator,
+ typename MatchCondition, typename ReadHandler>
+ class read_until_match_op
+ {
+ public:
+ read_until_match_op(AsyncReadStream& stream,
+ asio::basic_streambuf<Allocator>& streambuf,
+ MatchCondition match_condition, ReadHandler handler)
+ : stream_(stream),
+ streambuf_(streambuf),
+ match_condition_(match_condition),
+ search_position_(0),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ std::size_t bytes_transferred, int start = 0)
+ {
+ const std::size_t not_found = (std::numeric_limits<std::size_t>::max)();
+ std::size_t bytes_to_read;
+ switch (start)
+ {
+ case 1:
+ for (;;)
+ {
+ {
+ // Determine the range of the data to be searched.
+ typedef typename asio::basic_streambuf<
+ Allocator>::const_buffers_type const_buffers_type;
+ typedef asio::buffers_iterator<const_buffers_type> iterator;
+ const_buffers_type buffers = streambuf_.data();
+ iterator begin = iterator::begin(buffers);
+ iterator start = begin + search_position_;
+ iterator end = iterator::end(buffers);
+
+ // Look for a match.
+ std::pair<iterator, bool> result = match_condition_(start, end);
+ if (result.second)
+ {
+ // Full match. We're done.
+ search_position_ = result.first - begin;
+ bytes_to_read = 0;
+ }
+
+ // No match yet. Check if buffer is full.
+ else if (streambuf_.size() == streambuf_.max_size())
+ {
+ search_position_ = not_found;
+ bytes_to_read = 0;
+ }
+
+ // Need to read some more data.
+ else
+ {
+ if (result.first != end)
+ {
+ // Partial match. Next search needs to start from beginning of
+ // match.
+ search_position_ = result.first - begin;
+ }
+ else
+ {
+ // Next search can start with the new data.
+ search_position_ = end - begin;
+ }
+
+ bytes_to_read = read_size_helper(streambuf_, 65536);
+ }
+ }
+
+ // Check if we're done.
+ if (!start && bytes_to_read == 0)
+ break;
+
+ // Start a new asynchronous read operation to obtain more data.
+ stream_.async_read_some(streambuf_.prepare(bytes_to_read), *this);
+ return; default:
+ streambuf_.commit(bytes_transferred);
+ if (ec || bytes_transferred == 0)
+ break;
+ }
+
+ const asio::error_code result_ec =
+ (search_position_ == not_found)
+ ? error::not_found : ec;
+
+ const std::size_t result_n =
+ (ec || search_position_ == not_found)
+ ? 0 : search_position_;
+
+ handler_(result_ec, result_n);
+ }
+ }
+
+ //private:
+ AsyncReadStream& stream_;
+ asio::basic_streambuf<Allocator>& streambuf_;
+ MatchCondition match_condition_;
+ std::size_t search_position_;
+ ReadHandler handler_;
+ };
+
+ template <typename AsyncReadStream, typename Allocator,
+ typename MatchCondition, typename ReadHandler>
+ inline void* asio_handler_allocate(std::size_t size,
+ read_until_match_op<AsyncReadStream,
+ Allocator, MatchCondition, ReadHandler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, this_handler->handler_);
+ }
+
+ template <typename AsyncReadStream, typename Allocator,
+ typename MatchCondition, typename ReadHandler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ read_until_match_op<AsyncReadStream,
+ Allocator, MatchCondition, ReadHandler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, this_handler->handler_);
+ }
+
+ template <typename Function, typename AsyncReadStream, typename Allocator,
+ typename MatchCondition, typename ReadHandler>
+ inline void asio_handler_invoke(const Function& function,
+ read_until_match_op<AsyncReadStream,
+ Allocator, MatchCondition, ReadHandler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename AsyncReadStream, typename Allocator,
+ typename MatchCondition, typename ReadHandler>
+void async_read_until(AsyncReadStream& s,
+ asio::basic_streambuf<Allocator>& b,
+ MatchCondition match_condition, ReadHandler handler,
+ typename boost::enable_if<is_match_condition<MatchCondition> >::type*)
+{
+ detail::read_until_match_op<
+ AsyncReadStream, Allocator, MatchCondition, ReadHandler>(
+ s, b, match_condition, handler)(
+ asio::error_code(), 0, 1);
+}
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IMPL_READ_UNTIL_HPP
diff --git a/ext/asio/asio/impl/serial_port_base.hpp b/ext/asio/asio/impl/serial_port_base.hpp
new file mode 100644
index 0000000..758e652
--- /dev/null
+++ b/ext/asio/asio/impl/serial_port_base.hpp
@@ -0,0 +1,59 @@
+//
+// impl/serial_port_base.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2008 Rep Invariant Systems, Inc. (info at repinvariant.com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IMPL_SERIAL_PORT_BASE_HPP
+#define ASIO_IMPL_SERIAL_PORT_BASE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+
+inline serial_port_base::baud_rate::baud_rate(unsigned int rate)
+ : value_(rate)
+{
+}
+
+inline unsigned int serial_port_base::baud_rate::value() const
+{
+ return value_;
+}
+
+inline serial_port_base::flow_control::type
+serial_port_base::flow_control::value() const
+{
+ return value_;
+}
+
+inline serial_port_base::parity::type serial_port_base::parity::value() const
+{
+ return value_;
+}
+
+inline serial_port_base::stop_bits::type
+serial_port_base::stop_bits::value() const
+{
+ return value_;
+}
+
+inline unsigned int serial_port_base::character_size::value() const
+{
+ return value_;
+}
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IMPL_SERIAL_PORT_BASE_HPP
diff --git a/ext/asio/asio/impl/serial_port_base.ipp b/ext/asio/asio/impl/serial_port_base.ipp
index 64ab6a4..4d3204d 100644
--- a/ext/asio/asio/impl/serial_port_base.ipp
+++ b/ext/asio/asio/impl/serial_port_base.ipp
@@ -1,40 +1,43 @@
//
-// serial_port_base.ipp
-// ~~~~~~~~~~~~~~~~~~~~
+// impl/serial_port_base.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
// Copyright (c) 2008 Rep Invariant Systems, Inc. (info at repinvariant.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
-#ifndef ASIO_SERIAL_PORT_BASE_IPP
-#define ASIO_SERIAL_PORT_BASE_IPP
+#ifndef ASIO_IMPL_SERIAL_PORT_BASE_IPP
+#define ASIO_IMPL_SERIAL_PORT_BASE_IPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
+#if defined(ASIO_HAS_SERIAL_PORT)
+
+#include <stdexcept>
#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/error.hpp"
+#include "asio/serial_port_base.hpp"
-namespace asio {
+#if defined(GENERATING_DOCUMENTATION)
+# define ASIO_OPTION_STORAGE implementation_defined
+#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# define ASIO_OPTION_STORAGE DCB
+#else
+# define ASIO_OPTION_STORAGE termios
+#endif
-inline serial_port_base::baud_rate::baud_rate(unsigned int rate)
- : value_(rate)
-{
-}
+#include "asio/detail/push_options.hpp"
-inline unsigned int serial_port_base::baud_rate::value() const
-{
- return value_;
-}
+namespace asio {
-inline asio::error_code serial_port_base::baud_rate::store(
+asio::error_code serial_port_base::baud_rate::store(
ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
@@ -122,7 +125,7 @@ inline asio::error_code serial_port_base::baud_rate::store(
return ec;
}
-inline asio::error_code serial_port_base::baud_rate::load(
+asio::error_code serial_port_base::baud_rate::load(
const ASIO_OPTION_STORAGE& storage, asio::error_code& ec)
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
@@ -204,7 +207,7 @@ inline asio::error_code serial_port_base::baud_rate::load(
return ec;
}
-inline serial_port_base::flow_control::flow_control(
+serial_port_base::flow_control::flow_control(
serial_port_base::flow_control::type t)
: value_(t)
{
@@ -215,13 +218,7 @@ inline serial_port_base::flow_control::flow_control(
}
}
-inline serial_port_base::flow_control::type
-serial_port_base::flow_control::value() const
-{
- return value_;
-}
-
-inline asio::error_code serial_port_base::flow_control::store(
+asio::error_code serial_port_base::flow_control::store(
ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
@@ -255,12 +252,16 @@ inline asio::error_code serial_port_base::flow_control::store(
storage.c_iflag &= ~(IXOFF | IXON);
# if defined(_BSD_SOURCE)
storage.c_cflag &= ~CRTSCTS;
+# elif defined(__QNXNTO__)
+ storage.c_cflag &= ~(IHFLOW | OHFLOW);
# endif
break;
case software:
storage.c_iflag |= IXOFF | IXON;
# if defined(_BSD_SOURCE)
storage.c_cflag &= ~CRTSCTS;
+# elif defined(__QNXNTO__)
+ storage.c_cflag &= ~(IHFLOW | OHFLOW);
# endif
break;
case hardware:
@@ -268,6 +269,10 @@ inline asio::error_code serial_port_base::flow_control::store(
storage.c_iflag &= ~(IXOFF | IXON);
storage.c_cflag |= CRTSCTS;
break;
+# elif defined(__QNXNTO__)
+ storage.c_iflag &= ~(IXOFF | IXON);
+ storage.c_cflag |= (IHFLOW | OHFLOW);
+ break;
# else
ec = asio::error::operation_not_supported;
return ec;
@@ -280,7 +285,7 @@ inline asio::error_code serial_port_base::flow_control::store(
return ec;
}
-inline asio::error_code serial_port_base::flow_control::load(
+asio::error_code serial_port_base::flow_control::load(
const ASIO_OPTION_STORAGE& storage, asio::error_code& ec)
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
@@ -306,6 +311,11 @@ inline asio::error_code serial_port_base::flow_control::load(
{
value_ = hardware;
}
+# elif defined(__QNXNTO__)
+ else if (storage.c_cflag & IHFLOW && storage.c_cflag & OHFLOW)
+ {
+ value_ = hardware;
+ }
# endif
else
{
@@ -316,7 +326,7 @@ inline asio::error_code serial_port_base::flow_control::load(
return ec;
}
-inline serial_port_base::parity::parity(serial_port_base::parity::type t)
+serial_port_base::parity::parity(serial_port_base::parity::type t)
: value_(t)
{
if (t != none && t != odd && t != even)
@@ -326,12 +336,7 @@ inline serial_port_base::parity::parity(serial_port_base::parity::type t)
}
}
-inline serial_port_base::parity::type serial_port_base::parity::value() const
-{
- return value_;
-}
-
-inline asio::error_code serial_port_base::parity::store(
+asio::error_code serial_port_base::parity::store(
ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
@@ -378,7 +383,7 @@ inline asio::error_code serial_port_base::parity::store(
return ec;
}
-inline asio::error_code serial_port_base::parity::load(
+asio::error_code serial_port_base::parity::load(
const ASIO_OPTION_STORAGE& storage, asio::error_code& ec)
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
@@ -415,7 +420,7 @@ inline asio::error_code serial_port_base::parity::load(
return ec;
}
-inline serial_port_base::stop_bits::stop_bits(
+serial_port_base::stop_bits::stop_bits(
serial_port_base::stop_bits::type t)
: value_(t)
{
@@ -426,13 +431,7 @@ inline serial_port_base::stop_bits::stop_bits(
}
}
-inline serial_port_base::stop_bits::type
-serial_port_base::stop_bits::value() const
-{
- return value_;
-}
-
-inline asio::error_code serial_port_base::stop_bits::store(
+asio::error_code serial_port_base::stop_bits::store(
ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
@@ -468,7 +467,7 @@ inline asio::error_code serial_port_base::stop_bits::store(
return ec;
}
-inline asio::error_code serial_port_base::stop_bits::load(
+asio::error_code serial_port_base::stop_bits::load(
const ASIO_OPTION_STORAGE& storage, asio::error_code& ec)
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
@@ -495,7 +494,7 @@ inline asio::error_code serial_port_base::stop_bits::load(
return ec;
}
-inline serial_port_base::character_size::character_size(unsigned int t)
+serial_port_base::character_size::character_size(unsigned int t)
: value_(t)
{
if (t < 5 || t > 8)
@@ -505,12 +504,7 @@ inline serial_port_base::character_size::character_size(unsigned int t)
}
}
-inline unsigned int serial_port_base::character_size::value() const
-{
- return value_;
-}
-
-inline asio::error_code serial_port_base::character_size::store(
+asio::error_code serial_port_base::character_size::store(
ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
@@ -530,7 +524,7 @@ inline asio::error_code serial_port_base::character_size::store(
return ec;
}
-inline asio::error_code serial_port_base::character_size::load(
+asio::error_code serial_port_base::character_size::load(
const ASIO_OPTION_STORAGE& storage, asio::error_code& ec)
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
@@ -554,4 +548,8 @@ inline asio::error_code serial_port_base::character_size::load(
#include "asio/detail/pop_options.hpp"
-#endif // ASIO_SERIAL_PORT_BASE_IPP
+#undef ASIO_OPTION_STORAGE
+
+#endif // defined(ASIO_HAS_SERIAL_PORT)
+
+#endif // ASIO_IMPL_SERIAL_PORT_BASE_IPP
diff --git a/ext/asio/asio/impl/src.cpp b/ext/asio/asio/impl/src.cpp
new file mode 100644
index 0000000..1dac400
--- /dev/null
+++ b/ext/asio/asio/impl/src.cpp
@@ -0,0 +1,25 @@
+//
+// impl/src.cpp
+// ~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#if defined(_MSC_VER) \
+ || defined(__BORLANDC__) \
+ || defined(__DMC__)
+# pragma message ( \
+ "This file is deprecated. " \
+ "Please #include <asio/impl/src.hpp> instead.")
+#elif defined(__GNUC__) \
+ || defined(__HP_aCC) \
+ || defined(__SUNPRO_CC) \
+ || defined(__IBMCPP__)
+# warning "This file is deprecated."
+# warning "Please #include <asio/impl/src.hpp> instead."
+#endif
+
+#include "asio/impl/src.hpp"
diff --git a/ext/asio/asio/impl/src.hpp b/ext/asio/asio/impl/src.hpp
new file mode 100644
index 0000000..b37eabd
--- /dev/null
+++ b/ext/asio/asio/impl/src.hpp
@@ -0,0 +1,65 @@
+//
+// impl/src.hpp
+// ~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IMPL_SRC_HPP
+#define ASIO_IMPL_SRC_HPP
+
+#define ASIO_SOURCE
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HEADER_ONLY)
+# error Do not compile Asio library source with ASIO_HEADER_ONLY defined
+#endif
+
+#include "asio/impl/error.ipp"
+#include "asio/impl/error_code.ipp"
+#include "asio/impl/io_service.ipp"
+#include "asio/impl/serial_port_base.ipp"
+#include "asio/detail/impl/descriptor_ops.ipp"
+#include "asio/detail/impl/dev_poll_reactor.ipp"
+#include "asio/detail/impl/epoll_reactor.ipp"
+#include "asio/detail/impl/eventfd_select_interrupter.ipp"
+#include "asio/detail/impl/kqueue_reactor.ipp"
+#include "asio/detail/impl/pipe_select_interrupter.ipp"
+#include "asio/detail/impl/posix_event.ipp"
+#include "asio/detail/impl/posix_mutex.ipp"
+#include "asio/detail/impl/posix_thread.ipp"
+#include "asio/detail/impl/posix_tss_ptr.ipp"
+#include "asio/detail/impl/reactive_descriptor_service.ipp"
+#include "asio/detail/impl/reactive_serial_port_service.ipp"
+#include "asio/detail/impl/reactive_socket_service_base.ipp"
+#include "asio/detail/impl/resolver_service_base.ipp"
+#include "asio/detail/impl/select_reactor.ipp"
+#include "asio/detail/impl/service_registry.ipp"
+#include "asio/detail/impl/socket_ops.ipp"
+#include "asio/detail/impl/socket_select_interrupter.ipp"
+#include "asio/detail/impl/strand_service.ipp"
+#include "asio/detail/impl/task_io_service.ipp"
+#include "asio/detail/impl/throw_error.ipp"
+#include "asio/detail/impl/timer_queue.ipp"
+#include "asio/detail/impl/timer_queue_set.ipp"
+#include "asio/detail/impl/win_iocp_handle_service.ipp"
+#include "asio/detail/impl/win_iocp_io_service.ipp"
+#include "asio/detail/impl/win_iocp_serial_port_service.ipp"
+#include "asio/detail/impl/win_iocp_socket_service_base.ipp"
+#include "asio/detail/impl/win_event.ipp"
+#include "asio/detail/impl/win_mutex.ipp"
+#include "asio/detail/impl/win_thread.ipp"
+#include "asio/detail/impl/win_tss_ptr.ipp"
+#include "asio/detail/impl/winsock_init.ipp"
+#include "asio/ip/impl/address.ipp"
+#include "asio/ip/impl/address_v4.ipp"
+#include "asio/ip/impl/address_v6.ipp"
+#include "asio/ip/impl/host_name.ipp"
+#include "asio/ip/detail/impl/endpoint.ipp"
+#include "asio/local/detail/impl/endpoint.ipp"
+
+#endif // ASIO_IMPL_SRC_HPP
diff --git a/ext/asio/asio/impl/write.hpp b/ext/asio/asio/impl/write.hpp
new file mode 100644
index 0000000..91cb65e
--- /dev/null
+++ b/ext/asio/asio/impl/write.hpp
@@ -0,0 +1,396 @@
+//
+// impl/write.hpp
+// ~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IMPL_WRITE_HPP
+#define ASIO_IMPL_WRITE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/buffer.hpp"
+#include "asio/completion_condition.hpp"
+#include "asio/detail/base_from_completion_cond.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/consuming_buffers.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/throw_error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+
+template <typename SyncWriteStream, typename ConstBufferSequence,
+ typename CompletionCondition>
+std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers,
+ CompletionCondition completion_condition, asio::error_code& ec)
+{
+ ec = asio::error_code();
+ asio::detail::consuming_buffers<
+ const_buffer, ConstBufferSequence> tmp(buffers);
+ std::size_t total_transferred = 0;
+ tmp.prepare(detail::adapt_completion_condition_result(
+ completion_condition(ec, total_transferred)));
+ while (tmp.begin() != tmp.end())
+ {
+ std::size_t bytes_transferred = s.write_some(tmp, ec);
+ tmp.consume(bytes_transferred);
+ total_transferred += bytes_transferred;
+ tmp.prepare(detail::adapt_completion_condition_result(
+ completion_condition(ec, total_transferred)));
+ }
+ return total_transferred;
+}
+
+template <typename SyncWriteStream, typename ConstBufferSequence>
+inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = write(s, buffers, transfer_all(), ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+template <typename SyncWriteStream, typename ConstBufferSequence,
+ typename CompletionCondition>
+inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers,
+ CompletionCondition completion_condition)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = write(s, buffers, completion_condition, ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+#if !defined(BOOST_NO_IOSTREAM)
+
+template <typename SyncWriteStream, typename Allocator,
+ typename CompletionCondition>
+std::size_t write(SyncWriteStream& s,
+ asio::basic_streambuf<Allocator>& b,
+ CompletionCondition completion_condition, asio::error_code& ec)
+{
+ std::size_t bytes_transferred = write(s, b.data(), completion_condition, ec);
+ b.consume(bytes_transferred);
+ return bytes_transferred;
+}
+
+template <typename SyncWriteStream, typename Allocator>
+inline std::size_t write(SyncWriteStream& s,
+ asio::basic_streambuf<Allocator>& b)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = write(s, b, transfer_all(), ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+template <typename SyncWriteStream, typename Allocator,
+ typename CompletionCondition>
+inline std::size_t write(SyncWriteStream& s,
+ asio::basic_streambuf<Allocator>& b,
+ CompletionCondition completion_condition)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = write(s, b, completion_condition, ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+#endif // !defined(BOOST_NO_IOSTREAM)
+
+namespace detail
+{
+ template <typename AsyncWriteStream, typename ConstBufferSequence,
+ typename CompletionCondition, typename WriteHandler>
+ class write_op
+ : detail::base_from_completion_cond<CompletionCondition>
+ {
+ public:
+ write_op(AsyncWriteStream& stream, const ConstBufferSequence& buffers,
+ CompletionCondition completion_condition, WriteHandler handler)
+ : detail::base_from_completion_cond<
+ CompletionCondition>(completion_condition),
+ stream_(stream),
+ buffers_(buffers),
+ total_transferred_(0),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ std::size_t bytes_transferred, int start = 0)
+ {
+ switch (start)
+ {
+ case 1:
+ buffers_.prepare(this->check_for_completion(ec, total_transferred_));
+ for (;;)
+ {
+ stream_.async_write_some(buffers_, *this);
+ return; default:
+ total_transferred_ += bytes_transferred;
+ buffers_.consume(bytes_transferred);
+ buffers_.prepare(this->check_for_completion(ec, total_transferred_));
+ if ((!ec && bytes_transferred == 0)
+ || buffers_.begin() == buffers_.end())
+ break;
+ }
+
+ handler_(ec, static_cast<const std::size_t&>(total_transferred_));
+ }
+ }
+
+ //private:
+ AsyncWriteStream& stream_;
+ asio::detail::consuming_buffers<
+ const_buffer, ConstBufferSequence> buffers_;
+ std::size_t total_transferred_;
+ WriteHandler handler_;
+ };
+
+ template <typename AsyncWriteStream,
+ typename CompletionCondition, typename WriteHandler>
+ class write_op<AsyncWriteStream, asio::mutable_buffers_1,
+ CompletionCondition, WriteHandler>
+ : detail::base_from_completion_cond<CompletionCondition>
+ {
+ public:
+ write_op(AsyncWriteStream& stream,
+ const asio::mutable_buffers_1& buffers,
+ CompletionCondition completion_condition,
+ WriteHandler handler)
+ : detail::base_from_completion_cond<
+ CompletionCondition>(completion_condition),
+ stream_(stream),
+ buffer_(buffers),
+ total_transferred_(0),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ std::size_t bytes_transferred, int start = 0)
+ {
+ std::size_t n = 0;
+ switch (start)
+ {
+ case 1:
+ n = this->check_for_completion(ec, total_transferred_);
+ for (;;)
+ {
+ stream_.async_write_some(asio::buffer(
+ buffer_ + total_transferred_, n), *this);
+ return; default:
+ total_transferred_ += bytes_transferred;
+ if ((!ec && bytes_transferred == 0)
+ || (n = this->check_for_completion(ec, total_transferred_)) == 0
+ || total_transferred_ == asio::buffer_size(buffer_))
+ break;
+ }
+
+ handler_(ec, static_cast<const std::size_t&>(total_transferred_));
+ }
+ }
+
+ //private:
+ AsyncWriteStream& stream_;
+ asio::mutable_buffer buffer_;
+ std::size_t total_transferred_;
+ WriteHandler handler_;
+ };
+
+ template <typename AsyncWriteStream,
+ typename CompletionCondition, typename WriteHandler>
+ class write_op<AsyncWriteStream, asio::const_buffers_1,
+ CompletionCondition, WriteHandler>
+ : detail::base_from_completion_cond<CompletionCondition>
+ {
+ public:
+ write_op(AsyncWriteStream& stream,
+ const asio::const_buffers_1& buffers,
+ CompletionCondition completion_condition,
+ WriteHandler handler)
+ : detail::base_from_completion_cond<
+ CompletionCondition>(completion_condition),
+ stream_(stream),
+ buffer_(buffers),
+ total_transferred_(0),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ std::size_t bytes_transferred, int start = 0)
+ {
+ std::size_t n = 0;
+ switch (start)
+ {
+ case 1:
+ n = this->check_for_completion(ec, total_transferred_);
+ for (;;)
+ {
+ stream_.async_write_some(asio::buffer(
+ buffer_ + total_transferred_, n), *this);
+ return; default:
+ total_transferred_ += bytes_transferred;
+ if ((!ec && bytes_transferred == 0)
+ || (n = this->check_for_completion(ec, total_transferred_)) == 0
+ || total_transferred_ == asio::buffer_size(buffer_))
+ break;
+ }
+
+ handler_(ec, static_cast<const std::size_t&>(total_transferred_));
+ }
+ }
+
+ //private:
+ AsyncWriteStream& stream_;
+ asio::const_buffer buffer_;
+ std::size_t total_transferred_;
+ WriteHandler handler_;
+ };
+
+ template <typename AsyncWriteStream, typename ConstBufferSequence,
+ typename CompletionCondition, typename WriteHandler>
+ inline void* asio_handler_allocate(std::size_t size,
+ write_op<AsyncWriteStream, ConstBufferSequence,
+ CompletionCondition, WriteHandler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, this_handler->handler_);
+ }
+
+ template <typename AsyncWriteStream, typename ConstBufferSequence,
+ typename CompletionCondition, typename WriteHandler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ write_op<AsyncWriteStream, ConstBufferSequence,
+ CompletionCondition, WriteHandler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, this_handler->handler_);
+ }
+
+ template <typename Function, typename AsyncWriteStream,
+ typename ConstBufferSequence, typename CompletionCondition,
+ typename WriteHandler>
+ inline void asio_handler_invoke(const Function& function,
+ write_op<AsyncWriteStream, ConstBufferSequence,
+ CompletionCondition, WriteHandler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename AsyncWriteStream, typename ConstBufferSequence,
+ typename CompletionCondition, typename WriteHandler>
+inline void async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers,
+ CompletionCondition completion_condition, WriteHandler handler)
+{
+ detail::write_op<AsyncWriteStream, ConstBufferSequence,
+ CompletionCondition, WriteHandler>(
+ s, buffers, completion_condition, handler)(
+ asio::error_code(), 0, 1);
+}
+
+template <typename AsyncWriteStream, typename ConstBufferSequence,
+ typename WriteHandler>
+inline void async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers,
+ WriteHandler handler)
+{
+ async_write(s, buffers, transfer_all(), handler);
+}
+
+#if !defined(BOOST_NO_IOSTREAM)
+
+namespace detail
+{
+ template <typename AsyncWriteStream, typename Allocator,
+ typename WriteHandler>
+ class write_streambuf_handler
+ {
+ public:
+ write_streambuf_handler(asio::basic_streambuf<Allocator>& streambuf,
+ WriteHandler handler)
+ : streambuf_(streambuf),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ const std::size_t bytes_transferred)
+ {
+ streambuf_.consume(bytes_transferred);
+ handler_(ec, bytes_transferred);
+ }
+
+ //private:
+ asio::basic_streambuf<Allocator>& streambuf_;
+ WriteHandler handler_;
+ };
+
+ template <typename AsyncWriteStream, typename Allocator,
+ typename WriteHandler>
+ inline void* asio_handler_allocate(std::size_t size,
+ write_streambuf_handler<AsyncWriteStream,
+ Allocator, WriteHandler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, this_handler->handler_);
+ }
+
+ template <typename AsyncWriteStream, typename Allocator,
+ typename WriteHandler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ write_streambuf_handler<AsyncWriteStream,
+ Allocator, WriteHandler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, this_handler->handler_);
+ }
+
+ template <typename Function, typename AsyncWriteStream, typename Allocator,
+ typename WriteHandler>
+ inline void asio_handler_invoke(const Function& function,
+ write_streambuf_handler<AsyncWriteStream,
+ Allocator, WriteHandler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename AsyncWriteStream, typename Allocator,
+ typename CompletionCondition, typename WriteHandler>
+inline void async_write(AsyncWriteStream& s,
+ asio::basic_streambuf<Allocator>& b,
+ CompletionCondition completion_condition, WriteHandler handler)
+{
+ async_write(s, b.data(), completion_condition,
+ detail::write_streambuf_handler<
+ AsyncWriteStream, Allocator, WriteHandler>(b, handler));
+}
+
+template <typename AsyncWriteStream, typename Allocator, typename WriteHandler>
+inline void async_write(AsyncWriteStream& s,
+ asio::basic_streambuf<Allocator>& b, WriteHandler handler)
+{
+ async_write(s, b, transfer_all(), handler);
+}
+
+#endif // !defined(BOOST_NO_IOSTREAM)
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IMPL_WRITE_HPP
diff --git a/ext/asio/asio/impl/write_at.hpp b/ext/asio/asio/impl/write_at.hpp
new file mode 100644
index 0000000..1de6800
--- /dev/null
+++ b/ext/asio/asio/impl/write_at.hpp
@@ -0,0 +1,417 @@
+//
+// impl/write_at.hpp
+// ~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IMPL_WRITE_AT_HPP
+#define ASIO_IMPL_WRITE_AT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/buffer.hpp"
+#include "asio/completion_condition.hpp"
+#include "asio/detail/base_from_completion_cond.hpp"
+#include "asio/detail/bind_handler.hpp"
+#include "asio/detail/consuming_buffers.hpp"
+#include "asio/detail/handler_alloc_helpers.hpp"
+#include "asio/detail/handler_invoke_helpers.hpp"
+#include "asio/detail/throw_error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+
+template <typename SyncRandomAccessWriteDevice, typename ConstBufferSequence,
+ typename CompletionCondition>
+std::size_t write_at(SyncRandomAccessWriteDevice& d,
+ boost::uint64_t offset, const ConstBufferSequence& buffers,
+ CompletionCondition completion_condition, asio::error_code& ec)
+{
+ ec = asio::error_code();
+ asio::detail::consuming_buffers<
+ const_buffer, ConstBufferSequence> tmp(buffers);
+ std::size_t total_transferred = 0;
+ tmp.prepare(detail::adapt_completion_condition_result(
+ completion_condition(ec, total_transferred)));
+ while (tmp.begin() != tmp.end())
+ {
+ std::size_t bytes_transferred = d.write_some_at(
+ offset + total_transferred, tmp, ec);
+ tmp.consume(bytes_transferred);
+ total_transferred += bytes_transferred;
+ tmp.prepare(detail::adapt_completion_condition_result(
+ completion_condition(ec, total_transferred)));
+ }
+ return total_transferred;
+}
+
+template <typename SyncRandomAccessWriteDevice, typename ConstBufferSequence>
+inline std::size_t write_at(SyncRandomAccessWriteDevice& d,
+ boost::uint64_t offset, const ConstBufferSequence& buffers)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = write_at(
+ d, offset, buffers, transfer_all(), ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+template <typename SyncRandomAccessWriteDevice, typename ConstBufferSequence,
+ typename CompletionCondition>
+inline std::size_t write_at(SyncRandomAccessWriteDevice& d,
+ boost::uint64_t offset, const ConstBufferSequence& buffers,
+ CompletionCondition completion_condition)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = write_at(
+ d, offset, buffers, completion_condition, ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+#if !defined(BOOST_NO_IOSTREAM)
+
+template <typename SyncRandomAccessWriteDevice, typename Allocator,
+ typename CompletionCondition>
+std::size_t write_at(SyncRandomAccessWriteDevice& d,
+ boost::uint64_t offset, asio::basic_streambuf<Allocator>& b,
+ CompletionCondition completion_condition, asio::error_code& ec)
+{
+ std::size_t bytes_transferred = write_at(
+ d, offset, b.data(), completion_condition, ec);
+ b.consume(bytes_transferred);
+ return bytes_transferred;
+}
+
+template <typename SyncRandomAccessWriteDevice, typename Allocator>
+inline std::size_t write_at(SyncRandomAccessWriteDevice& d,
+ boost::uint64_t offset, asio::basic_streambuf<Allocator>& b)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = write_at(d, offset, b, transfer_all(), ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+template <typename SyncRandomAccessWriteDevice, typename Allocator,
+ typename CompletionCondition>
+inline std::size_t write_at(SyncRandomAccessWriteDevice& d,
+ boost::uint64_t offset, asio::basic_streambuf<Allocator>& b,
+ CompletionCondition completion_condition)
+{
+ asio::error_code ec;
+ std::size_t bytes_transferred = write_at(
+ d, offset, b, completion_condition, ec);
+ asio::detail::throw_error(ec);
+ return bytes_transferred;
+}
+
+#endif // !defined(BOOST_NO_IOSTREAM)
+
+namespace detail
+{
+ template <typename AsyncRandomAccessWriteDevice, typename ConstBufferSequence,
+ typename CompletionCondition, typename WriteHandler>
+ class write_at_op
+ : detail::base_from_completion_cond<CompletionCondition>
+ {
+ public:
+ write_at_op(AsyncRandomAccessWriteDevice& device,
+ boost::uint64_t offset, const ConstBufferSequence& buffers,
+ CompletionCondition completion_condition, WriteHandler handler)
+ : detail::base_from_completion_cond<
+ CompletionCondition>(completion_condition),
+ device_(device),
+ offset_(offset),
+ buffers_(buffers),
+ total_transferred_(0),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ std::size_t bytes_transferred, int start = 0)
+ {
+ switch (start)
+ {
+ case 1:
+ buffers_.prepare(this->check_for_completion(ec, total_transferred_));
+ for (;;)
+ {
+ device_.async_write_some_at(
+ offset_ + total_transferred_, buffers_, *this);
+ return; default:
+ total_transferred_ += bytes_transferred;
+ buffers_.consume(bytes_transferred);
+ buffers_.prepare(this->check_for_completion(ec, total_transferred_));
+ if ((!ec && bytes_transferred == 0)
+ || buffers_.begin() == buffers_.end())
+ break;
+ }
+
+ handler_(ec, static_cast<const std::size_t&>(total_transferred_));
+ }
+ }
+
+ //private:
+ AsyncRandomAccessWriteDevice& device_;
+ boost::uint64_t offset_;
+ asio::detail::consuming_buffers<
+ const_buffer, ConstBufferSequence> buffers_;
+ std::size_t total_transferred_;
+ WriteHandler handler_;
+ };
+
+ template <typename AsyncRandomAccessWriteDevice,
+ typename CompletionCondition, typename WriteHandler>
+ class write_at_op<AsyncRandomAccessWriteDevice,
+ asio::mutable_buffers_1, CompletionCondition, WriteHandler>
+ : detail::base_from_completion_cond<CompletionCondition>
+ {
+ public:
+ write_at_op(AsyncRandomAccessWriteDevice& device,
+ boost::uint64_t offset, const asio::mutable_buffers_1& buffers,
+ CompletionCondition completion_condition,
+ WriteHandler handler)
+ : detail::base_from_completion_cond<
+ CompletionCondition>(completion_condition),
+ device_(device),
+ offset_(offset),
+ buffer_(buffers),
+ total_transferred_(0),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ std::size_t bytes_transferred, int start = 0)
+ {
+ std::size_t n = 0;
+ switch (start)
+ {
+ case 1:
+ n = this->check_for_completion(ec, total_transferred_);
+ for (;;)
+ {
+ device_.async_write_some_at(offset_ + total_transferred_,
+ asio::buffer(buffer_ + total_transferred_, n), *this);
+ return; default:
+ total_transferred_ += bytes_transferred;
+ if ((!ec && bytes_transferred == 0)
+ || (n = this->check_for_completion(ec, total_transferred_)) == 0
+ || total_transferred_ == asio::buffer_size(buffer_))
+ break;
+ }
+
+ handler_(ec, static_cast<const std::size_t&>(total_transferred_));
+ }
+ }
+
+ //private:
+ AsyncRandomAccessWriteDevice& device_;
+ boost::uint64_t offset_;
+ asio::mutable_buffer buffer_;
+ std::size_t total_transferred_;
+ WriteHandler handler_;
+ };
+
+ template <typename AsyncRandomAccessWriteDevice,
+ typename CompletionCondition, typename WriteHandler>
+ class write_at_op<AsyncRandomAccessWriteDevice, asio::const_buffers_1,
+ CompletionCondition, WriteHandler>
+ : detail::base_from_completion_cond<CompletionCondition>
+ {
+ public:
+ write_at_op(AsyncRandomAccessWriteDevice& device,
+ boost::uint64_t offset, const asio::const_buffers_1& buffers,
+ CompletionCondition completion_condition,
+ WriteHandler handler)
+ : detail::base_from_completion_cond<
+ CompletionCondition>(completion_condition),
+ device_(device),
+ offset_(offset),
+ buffer_(buffers),
+ total_transferred_(0),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ std::size_t bytes_transferred, int start = 0)
+ {
+ std::size_t n = 0;
+ switch (start)
+ {
+ case 1:
+ n = this->check_for_completion(ec, total_transferred_);
+ for (;;)
+ {
+ device_.async_write_some_at(offset_ + total_transferred_,
+ asio::buffer(buffer_ + total_transferred_, n), *this);
+ return; default:
+ total_transferred_ += bytes_transferred;
+ if ((!ec && bytes_transferred == 0)
+ || (n = this->check_for_completion(ec, total_transferred_)) == 0
+ || total_transferred_ == asio::buffer_size(buffer_))
+ break;
+ }
+
+ handler_(ec, static_cast<const std::size_t&>(total_transferred_));
+ }
+ }
+
+ //private:
+ AsyncRandomAccessWriteDevice& device_;
+ boost::uint64_t offset_;
+ asio::const_buffer buffer_;
+ std::size_t total_transferred_;
+ WriteHandler handler_;
+ };
+
+ template <typename AsyncRandomAccessWriteDevice, typename ConstBufferSequence,
+ typename CompletionCondition, typename WriteHandler>
+ inline void* asio_handler_allocate(std::size_t size,
+ write_at_op<AsyncRandomAccessWriteDevice, ConstBufferSequence,
+ CompletionCondition, WriteHandler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, this_handler->handler_);
+ }
+
+ template <typename AsyncRandomAccessWriteDevice, typename ConstBufferSequence,
+ typename CompletionCondition, typename WriteHandler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ write_at_op<AsyncRandomAccessWriteDevice, ConstBufferSequence,
+ CompletionCondition, WriteHandler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, this_handler->handler_);
+ }
+
+ template <typename Function, typename AsyncRandomAccessWriteDevice,
+ typename ConstBufferSequence, typename CompletionCondition,
+ typename WriteHandler>
+ inline void asio_handler_invoke(const Function& function,
+ write_at_op<AsyncRandomAccessWriteDevice, ConstBufferSequence,
+ CompletionCondition, WriteHandler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename AsyncRandomAccessWriteDevice, typename ConstBufferSequence,
+ typename CompletionCondition, typename WriteHandler>
+inline void async_write_at(AsyncRandomAccessWriteDevice& d,
+ boost::uint64_t offset, const ConstBufferSequence& buffers,
+ CompletionCondition completion_condition, WriteHandler handler)
+{
+ detail::write_at_op<AsyncRandomAccessWriteDevice,
+ ConstBufferSequence, CompletionCondition, WriteHandler>(
+ d, offset, buffers, completion_condition, handler)(
+ asio::error_code(), 0, 1);
+}
+
+template <typename AsyncRandomAccessWriteDevice, typename ConstBufferSequence,
+ typename WriteHandler>
+inline void async_write_at(AsyncRandomAccessWriteDevice& d,
+ boost::uint64_t offset, const ConstBufferSequence& buffers,
+ WriteHandler handler)
+{
+ async_write_at(d, offset, buffers, transfer_all(), handler);
+}
+
+#if !defined(BOOST_NO_IOSTREAM)
+
+namespace detail
+{
+ template <typename AsyncRandomAccessWriteDevice,
+ typename Allocator, typename WriteHandler>
+ class write_at_streambuf_op
+ {
+ public:
+ write_at_streambuf_op(
+ asio::basic_streambuf<Allocator>& streambuf,
+ WriteHandler handler)
+ : streambuf_(streambuf),
+ handler_(handler)
+ {
+ }
+
+ void operator()(const asio::error_code& ec,
+ const std::size_t bytes_transferred)
+ {
+ streambuf_.consume(bytes_transferred);
+ handler_(ec, bytes_transferred);
+ }
+
+ //private:
+ asio::basic_streambuf<Allocator>& streambuf_;
+ WriteHandler handler_;
+ };
+
+ template <typename AsyncRandomAccessWriteDevice, typename Allocator,
+ typename WriteHandler>
+ inline void* asio_handler_allocate(std::size_t size,
+ write_at_streambuf_op<AsyncRandomAccessWriteDevice,
+ Allocator, WriteHandler>* this_handler)
+ {
+ return asio_handler_alloc_helpers::allocate(
+ size, this_handler->handler_);
+ }
+
+ template <typename AsyncRandomAccessWriteDevice, typename Allocator,
+ typename WriteHandler>
+ inline void asio_handler_deallocate(void* pointer, std::size_t size,
+ write_at_streambuf_op<AsyncRandomAccessWriteDevice,
+ Allocator, WriteHandler>* this_handler)
+ {
+ asio_handler_alloc_helpers::deallocate(
+ pointer, size, this_handler->handler_);
+ }
+
+ template <typename Function, typename AsyncRandomAccessWriteDevice,
+ typename Allocator, typename WriteHandler>
+ inline void asio_handler_invoke(const Function& function,
+ write_at_streambuf_op<AsyncRandomAccessWriteDevice,
+ Allocator, WriteHandler>* this_handler)
+ {
+ asio_handler_invoke_helpers::invoke(
+ function, this_handler->handler_);
+ }
+} // namespace detail
+
+template <typename AsyncRandomAccessWriteDevice, typename Allocator,
+ typename CompletionCondition, typename WriteHandler>
+inline void async_write_at(AsyncRandomAccessWriteDevice& d,
+ boost::uint64_t offset, asio::basic_streambuf<Allocator>& b,
+ CompletionCondition completion_condition, WriteHandler handler)
+{
+ async_write_at(d, offset, b.data(), completion_condition,
+ detail::write_at_streambuf_op<
+ AsyncRandomAccessWriteDevice, Allocator, WriteHandler>(b, handler));
+}
+
+template <typename AsyncRandomAccessWriteDevice, typename Allocator,
+ typename WriteHandler>
+inline void async_write_at(AsyncRandomAccessWriteDevice& d,
+ boost::uint64_t offset, asio::basic_streambuf<Allocator>& b,
+ WriteHandler handler)
+{
+ async_write_at(d, offset, b, transfer_all(), handler);
+}
+
+#endif // !defined(BOOST_NO_IOSTREAM)
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IMPL_WRITE_AT_HPP
diff --git a/ext/asio/asio/io_service.hpp b/ext/asio/asio/io_service.hpp
index 62d9e54..4fc3e05 100644
--- a/ext/asio/asio/io_service.hpp
+++ b/ext/asio/asio/io_service.hpp
@@ -2,7 +2,7 @@
// io_service.hpp
// ~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,25 +15,29 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
#include <stdexcept>
#include <typeinfo>
-#include <boost/config.hpp>
-#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error_code.hpp"
#include "asio/detail/noncopyable.hpp"
-#include "asio/detail/reactor_fwd.hpp"
#include "asio/detail/service_registry_fwd.hpp"
-#include "asio/detail/signal_init.hpp"
-#include "asio/detail/task_io_service_fwd.hpp"
-#include "asio/detail/win_iocp_io_service_fwd.hpp"
-#include "asio/detail/winsock_init.hpp"
#include "asio/detail/wrapped_handler.hpp"
+#include "asio/error_code.hpp"
+
+#if defined(ASIO_HAS_IOCP)
+# include "asio/detail/win_iocp_io_service_fwd.hpp"
+#else
+# include "asio/detail/task_io_service_fwd.hpp"
+#endif
+
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+# include "asio/detail/winsock_init.hpp"
+#elif defined(__sun) || defined(__QNX__) || defined(__hpux) || defined(_AIX) \
+ || defined(__osf__)
+# include "asio/detail/signal_init.hpp"
+#endif
+
+#include "asio/detail/push_options.hpp"
namespace asio {
@@ -45,7 +49,7 @@ template <typename Service> bool has_service(io_service& ios);
#if defined(ASIO_HAS_IOCP)
namespace detail { typedef win_iocp_io_service io_service_impl; }
#else
-namespace detail { typedef task_io_service<reactor> io_service_impl; }
+namespace detail { typedef task_io_service io_service_impl; }
#endif
/// Provides core I/O functionality.
@@ -196,7 +200,7 @@ public:
class strand;
/// Constructor.
- io_service();
+ ASIO_DECL io_service();
/// Constructor.
/**
@@ -205,7 +209,7 @@ public:
* @param concurrency_hint A suggestion to the implementation on how many
* threads it should allow to run simultaneously.
*/
- explicit io_service(std::size_t concurrency_hint);
+ ASIO_DECL explicit io_service(std::size_t concurrency_hint);
/// Destructor.
/**
@@ -239,7 +243,7 @@ public:
* destructor defined above destroys all handlers, causing all @c shared_ptr
* references to all connection objects to be destroyed.
*/
- ~io_service();
+ ASIO_DECL ~io_service();
/// Run the io_service object's event processing loop.
/**
@@ -265,7 +269,7 @@ public:
* The poll() function may also be used to dispatch ready handlers, but
* without blocking.
*/
- std::size_t run();
+ ASIO_DECL std::size_t run();
/// Run the io_service object's event processing loop.
/**
@@ -291,7 +295,7 @@ public:
* The poll() function may also be used to dispatch ready handlers, but
* without blocking.
*/
- std::size_t run(asio::error_code& ec);
+ ASIO_DECL std::size_t run(asio::error_code& ec);
/// Run the io_service object's event processing loop to execute at most one
/// handler.
@@ -303,7 +307,7 @@ public:
*
* @throws asio::system_error Thrown on failure.
*/
- std::size_t run_one();
+ ASIO_DECL std::size_t run_one();
/// Run the io_service object's event processing loop to execute at most one
/// handler.
@@ -315,7 +319,7 @@ public:
*
* @return The number of handlers that were executed.
*/
- std::size_t run_one(asio::error_code& ec);
+ ASIO_DECL std::size_t run_one(asio::error_code& ec);
/// Run the io_service object's event processing loop to execute ready
/// handlers.
@@ -327,7 +331,7 @@ public:
*
* @throws asio::system_error Thrown on failure.
*/
- std::size_t poll();
+ ASIO_DECL std::size_t poll();
/// Run the io_service object's event processing loop to execute ready
/// handlers.
@@ -339,7 +343,7 @@ public:
*
* @return The number of handlers that were executed.
*/
- std::size_t poll(asio::error_code& ec);
+ ASIO_DECL std::size_t poll(asio::error_code& ec);
/// Run the io_service object's event processing loop to execute one ready
/// handler.
@@ -351,7 +355,7 @@ public:
*
* @throws asio::system_error Thrown on failure.
*/
- std::size_t poll_one();
+ ASIO_DECL std::size_t poll_one();
/// Run the io_service object's event processing loop to execute one ready
/// handler.
@@ -363,7 +367,7 @@ public:
*
* @return The number of handlers that were executed.
*/
- std::size_t poll_one(asio::error_code& ec);
+ ASIO_DECL std::size_t poll_one(asio::error_code& ec);
/// Stop the io_service object's event processing loop.
/**
@@ -372,7 +376,7 @@ public:
* return as soon as possible. Subsequent calls to run(), run_one(), poll()
* or poll_one() will return immediately until reset() is called.
*/
- void stop();
+ ASIO_DECL void stop();
/// Reset the io_service in preparation for a subsequent run() invocation.
/**
@@ -385,7 +389,7 @@ public:
* This function must not be called while there are any unfinished calls to
* the run(), run_one(), poll() or poll_one() functions.
*/
- void reset();
+ ASIO_DECL void reset();
/// Request the io_service to invoke the given handler.
/**
@@ -605,10 +609,10 @@ protected:
/**
* @param owner The io_service object that owns the service.
*/
- service(asio::io_service& owner);
+ ASIO_DECL service(asio::io_service& owner);
/// Destructor.
- virtual ~service();
+ ASIO_DECL virtual ~service();
private:
/// Destroy all user-defined handler objects owned by the service.
@@ -631,10 +635,7 @@ class service_already_exists
: public std::logic_error
{
public:
- service_already_exists()
- : std::logic_error("Service already exists.")
- {
- }
+ ASIO_DECL service_already_exists();
};
/// Exception thrown when trying to add a service object to an io_service where
@@ -643,16 +644,44 @@ class invalid_service_owner
: public std::logic_error
{
public:
- invalid_service_owner()
- : std::logic_error("Invalid service owner.")
+ ASIO_DECL invalid_service_owner();
+};
+
+namespace detail {
+
+// Special derived service id type to keep classes header-file only.
+template <typename Type>
+class service_id
+ : public asio::io_service::id
+{
+};
+
+// Special service base class to keep classes header-file only.
+template <typename Type>
+class service_base
+ : public asio::io_service::service
+{
+public:
+ static asio::detail::service_id<Type> id;
+
+ // Constructor.
+ service_base(asio::io_service& io_service)
+ : asio::io_service::service(io_service)
{
}
};
-} // namespace asio
+template <typename Type>
+asio::detail::service_id<Type> service_base<Type>::id;
-#include "asio/impl/io_service.ipp"
+} // namespace detail
+} // namespace asio
#include "asio/detail/pop_options.hpp"
+#include "asio/impl/io_service.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/impl/io_service.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
#endif // ASIO_IO_SERVICE_HPP
diff --git a/ext/asio/asio/ip/address.hpp b/ext/asio/asio/ip/address.hpp
index 92ee4ae..47683db 100644
--- a/ext/asio/asio/ip/address.hpp
+++ b/ext/asio/asio/ip/address.hpp
@@ -1,8 +1,8 @@
//
-// address.hpp
-// ~~~~~~~~~~~
+// ip/address.hpp
+// ~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,21 +15,17 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+#include <string>
+#include "asio/error_code.hpp"
+#include "asio/ip/address_v4.hpp"
+#include "asio/ip/address_v6.hpp"
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
#if !defined(BOOST_NO_IOSTREAM)
# include <iosfwd>
#endif // !defined(BOOST_NO_IOSTREAM)
-#include <string>
-#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
-#include "asio/error.hpp"
-#include "asio/ip/address_v4.hpp"
-#include "asio/ip/address_v6.hpp"
-#include "asio/detail/throw_error.hpp"
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace ip {
@@ -47,63 +43,27 @@ class address
{
public:
/// Default constructor.
- address()
- : type_(ipv4),
- ipv4_address_(),
- ipv6_address_()
- {
- }
+ ASIO_DECL address();
/// Construct an address from an IPv4 address.
- address(const asio::ip::address_v4& ipv4_address)
- : type_(ipv4),
- ipv4_address_(ipv4_address),
- ipv6_address_()
- {
- }
+ ASIO_DECL address(const asio::ip::address_v4& ipv4_address);
/// Construct an address from an IPv6 address.
- address(const asio::ip::address_v6& ipv6_address)
- : type_(ipv6),
- ipv4_address_(),
- ipv6_address_(ipv6_address)
- {
- }
+ ASIO_DECL address(const asio::ip::address_v6& ipv6_address);
/// Copy constructor.
- address(const address& other)
- : type_(other.type_),
- ipv4_address_(other.ipv4_address_),
- ipv6_address_(other.ipv6_address_)
- {
- }
+ ASIO_DECL address(const address& other);
/// Assign from another address.
- address& operator=(const address& other)
- {
- type_ = other.type_;
- ipv4_address_ = other.ipv4_address_;
- ipv6_address_ = other.ipv6_address_;
- return *this;
- }
+ ASIO_DECL address& operator=(const address& other);
/// Assign from an IPv4 address.
- address& operator=(const asio::ip::address_v4& ipv4_address)
- {
- type_ = ipv4;
- ipv4_address_ = ipv4_address;
- ipv6_address_ = asio::ip::address_v6();
- return *this;
- }
+ ASIO_DECL address& operator=(
+ const asio::ip::address_v4& ipv4_address);
/// Assign from an IPv6 address.
- address& operator=(const asio::ip::address_v6& ipv6_address)
- {
- type_ = ipv6;
- ipv4_address_ = asio::ip::address_v4();
- ipv6_address_ = ipv6_address;
- return *this;
- }
+ ASIO_DECL address& operator=(
+ const asio::ip::address_v6& ipv6_address);
/// Get whether the address is an IP version 4 address.
bool is_v4() const
@@ -118,127 +78,63 @@ public:
}
/// Get the address as an IP version 4 address.
- asio::ip::address_v4 to_v4() const
- {
- if (type_ != ipv4)
- {
- asio::system_error e(
- asio::error::address_family_not_supported);
- boost::throw_exception(e);
- }
- return ipv4_address_;
- }
+ ASIO_DECL asio::ip::address_v4 to_v4() const;
/// Get the address as an IP version 6 address.
- asio::ip::address_v6 to_v6() const
- {
- if (type_ != ipv6)
- {
- asio::system_error e(
- asio::error::address_family_not_supported);
- boost::throw_exception(e);
- }
- return ipv6_address_;
- }
+ ASIO_DECL asio::ip::address_v6 to_v6() const;
/// Get the address as a string in dotted decimal format.
- std::string to_string() const
- {
- if (type_ == ipv6)
- return ipv6_address_.to_string();
- return ipv4_address_.to_string();
- }
+ ASIO_DECL std::string to_string() const;
/// Get the address as a string in dotted decimal format.
- std::string to_string(asio::error_code& ec) const
- {
- if (type_ == ipv6)
- return ipv6_address_.to_string(ec);
- return ipv4_address_.to_string(ec);
- }
+ ASIO_DECL std::string to_string(asio::error_code& ec) const;
/// Create an address from an IPv4 address string in dotted decimal form,
/// or from an IPv6 address in hexadecimal notation.
- static address from_string(const char* str)
- {
- asio::error_code ec;
- address addr = from_string(str, ec);
- asio::detail::throw_error(ec);
- return addr;
- }
+ ASIO_DECL static address from_string(const char* str);
/// Create an address from an IPv4 address string in dotted decimal form,
/// or from an IPv6 address in hexadecimal notation.
- static address from_string(const char* str, asio::error_code& ec)
- {
- asio::ip::address_v6 ipv6_address =
- asio::ip::address_v6::from_string(str, ec);
- if (!ec)
- {
- address tmp;
- tmp.type_ = ipv6;
- tmp.ipv6_address_ = ipv6_address;
- return tmp;
- }
-
- asio::ip::address_v4 ipv4_address =
- asio::ip::address_v4::from_string(str, ec);
- if (!ec)
- {
- address tmp;
- tmp.type_ = ipv4;
- tmp.ipv4_address_ = ipv4_address;
- return tmp;
- }
-
- return address();
- }
+ ASIO_DECL static address from_string(
+ const char* str, asio::error_code& ec);
/// Create an address from an IPv4 address string in dotted decimal form,
/// or from an IPv6 address in hexadecimal notation.
- static address from_string(const std::string& str)
- {
- return from_string(str.c_str());
- }
+ ASIO_DECL static address from_string(const std::string& str);
/// Create an address from an IPv4 address string in dotted decimal form,
/// or from an IPv6 address in hexadecimal notation.
- static address from_string(const std::string& str,
- asio::error_code& ec)
+ ASIO_DECL static address from_string(
+ const std::string& str, asio::error_code& ec);
+
+ /// Compare two addresses for equality.
+ ASIO_DECL friend bool operator==(const address& a1, const address& a2);
+
+ /// Compare two addresses for inequality.
+ friend bool operator!=(const address& a1, const address& a2)
{
- return from_string(str.c_str(), ec);
+ return !(a1 == a2);
}
- /// Compare two addresses for equality.
- friend bool operator==(const address& a1, const address& a2)
+ /// Compare addresses for ordering.
+ ASIO_DECL friend bool operator<(const address& a1, const address& a2);
+
+ /// Compare addresses for ordering.
+ friend bool operator>(const address& a1, const address& a2)
{
- if (a1.type_ != a2.type_)
- return false;
- if (a1.type_ == ipv6)
- return a1.ipv6_address_ == a2.ipv6_address_;
- return a1.ipv4_address_ == a2.ipv4_address_;
+ return a2 < a1;
}
- /// Compare two addresses for inequality.
- friend bool operator!=(const address& a1, const address& a2)
+ /// Compare addresses for ordering.
+ friend bool operator<=(const address& a1, const address& a2)
{
- if (a1.type_ != a2.type_)
- return true;
- if (a1.type_ == ipv6)
- return a1.ipv6_address_ != a2.ipv6_address_;
- return a1.ipv4_address_ != a2.ipv4_address_;
+ return !(a2 < a1);
}
/// Compare addresses for ordering.
- friend bool operator<(const address& a1, const address& a2)
+ friend bool operator>=(const address& a1, const address& a2)
{
- if (a1.type_ < a2.type_)
- return true;
- if (a1.type_ > a2.type_)
- return false;
- if (a1.type_ == ipv6)
- return a1.ipv6_address_ < a2.ipv6_address_;
- return a1.ipv4_address_ < a2.ipv4_address_;
+ return !(a1 < a2);
}
private:
@@ -268,11 +164,7 @@ private:
*/
template <typename Elem, typename Traits>
std::basic_ostream<Elem, Traits>& operator<<(
- std::basic_ostream<Elem, Traits>& os, const address& addr)
-{
- os << addr.to_string();
- return os;
-}
+ std::basic_ostream<Elem, Traits>& os, const address& addr);
#endif // !defined(BOOST_NO_IOSTREAM)
@@ -281,4 +173,9 @@ std::basic_ostream<Elem, Traits>& operator<<(
#include "asio/detail/pop_options.hpp"
+#include "asio/ip/impl/address.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/ip/impl/address.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
#endif // ASIO_IP_ADDRESS_HPP
diff --git a/ext/asio/asio/ip/address_v4.hpp b/ext/asio/asio/ip/address_v4.hpp
index 1b49556..7567ef9 100644
--- a/ext/asio/asio/ip/address_v4.hpp
+++ b/ext/asio/asio/ip/address_v4.hpp
@@ -1,8 +1,8 @@
//
-// address_v4.hpp
-// ~~~~~~~~~~~~~~
+// ip/address_v4.hpp
+// ~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,24 +15,18 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+#include <string>
+#include <boost/array.hpp>
+#include "asio/detail/socket_types.hpp"
+#include "asio/detail/winsock_init.hpp"
+#include "asio/error_code.hpp"
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
#if !defined(BOOST_NO_IOSTREAM)
# include <iosfwd>
#endif // !defined(BOOST_NO_IOSTREAM)
-#include <climits>
-#include <string>
-#include <stdexcept>
-#include <boost/array.hpp>
-#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
-#include "asio/error.hpp"
-#include "asio/detail/socket_ops.hpp"
-#include "asio/detail/socket_types.hpp"
-#include "asio/detail/throw_error.hpp"
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace ip {
@@ -59,34 +53,10 @@ public:
}
/// Construct an address from raw bytes.
- explicit address_v4(const bytes_type& bytes)
- {
-#if UCHAR_MAX > 0xFF
- if (bytes[0] > 0xFF || bytes[1] > 0xFF
- || bytes[2] > 0xFF || bytes[3] > 0xFF)
- {
- std::out_of_range ex("address_v4 from bytes_type");
- boost::throw_exception(ex);
- }
-#endif // UCHAR_MAX > 0xFF
-
- using namespace std; // For memcpy.
- memcpy(&addr_.s_addr, bytes.elems, 4);
- }
+ ASIO_DECL explicit address_v4(const bytes_type& bytes);
/// Construct an address from a unsigned long in host byte order.
- explicit address_v4(unsigned long addr)
- {
-#if ULONG_MAX > 0xFFFFFFFF
- if (addr > 0xFFFFFFFF)
- {
- std::out_of_range ex("address_v4 from unsigned long");
- boost::throw_exception(ex);
- }
-#endif // ULONG_MAX > 0xFFFFFFFF
-
- addr_.s_addr = asio::detail::socket_ops::host_to_network_long(addr);
- }
+ ASIO_DECL explicit address_v4(unsigned long addr);
/// Copy constructor.
address_v4(const address_v4& other)
@@ -102,96 +72,42 @@ public:
}
/// Get the address in bytes, in network byte order.
- bytes_type to_bytes() const
- {
- using namespace std; // For memcpy.
- bytes_type bytes;
- memcpy(bytes.elems, &addr_.s_addr, 4);
- return bytes;
- }
+ ASIO_DECL bytes_type to_bytes() const;
/// Get the address as an unsigned long in host byte order
- unsigned long to_ulong() const
- {
- return asio::detail::socket_ops::network_to_host_long(addr_.s_addr);
- }
+ ASIO_DECL unsigned long to_ulong() const;
/// Get the address as a string in dotted decimal format.
- std::string to_string() const
- {
- asio::error_code ec;
- std::string addr = to_string(ec);
- asio::detail::throw_error(ec);
- return addr;
- }
+ ASIO_DECL std::string to_string() const;
/// Get the address as a string in dotted decimal format.
- std::string to_string(asio::error_code& ec) const
- {
- char addr_str[asio::detail::max_addr_v4_str_len];
- const char* addr =
- asio::detail::socket_ops::inet_ntop(AF_INET, &addr_, addr_str,
- asio::detail::max_addr_v4_str_len, 0, ec);
- if (addr == 0)
- return std::string();
- return addr;
- }
+ ASIO_DECL std::string to_string(asio::error_code& ec) const;
/// Create an address from an IP address string in dotted decimal form.
- static address_v4 from_string(const char* str)
- {
- asio::error_code ec;
- address_v4 addr = from_string(str, ec);
- asio::detail::throw_error(ec);
- return addr;
- }
+ ASIO_DECL static address_v4 from_string(const char* str);
/// Create an address from an IP address string in dotted decimal form.
- static address_v4 from_string(const char* str, asio::error_code& ec)
- {
- address_v4 tmp;
- if (asio::detail::socket_ops::inet_pton(
- AF_INET, str, &tmp.addr_, 0, ec) <= 0)
- return address_v4();
- return tmp;
- }
+ ASIO_DECL static address_v4 from_string(
+ const char* str, asio::error_code& ec);
/// Create an address from an IP address string in dotted decimal form.
- static address_v4 from_string(const std::string& str)
- {
- return from_string(str.c_str());
- }
+ ASIO_DECL static address_v4 from_string(const std::string& str);
/// Create an address from an IP address string in dotted decimal form.
- static address_v4 from_string(const std::string& str,
- asio::error_code& ec)
- {
- return from_string(str.c_str(), ec);
- }
+ ASIO_DECL static address_v4 from_string(
+ const std::string& str, asio::error_code& ec);
/// Determine whether the address is a class A address.
- bool is_class_a() const
- {
- return IN_CLASSA(to_ulong());
- }
+ ASIO_DECL bool is_class_a() const;
/// Determine whether the address is a class B address.
- bool is_class_b() const
- {
- return IN_CLASSB(to_ulong());
- }
+ ASIO_DECL bool is_class_b() const;
/// Determine whether the address is a class C address.
- bool is_class_c() const
- {
- return IN_CLASSC(to_ulong());
- }
+ ASIO_DECL bool is_class_c() const;
/// Determine whether the address is a multicast address.
- bool is_multicast() const
- {
- return IN_MULTICAST(to_ulong());
- }
+ ASIO_DECL bool is_multicast() const;
/// Compare two addresses for equality.
friend bool operator==(const address_v4& a1, const address_v4& a2)
@@ -249,23 +165,12 @@ public:
/// Obtain an address object that represents the broadcast address that
/// corresponds to the specified address and netmask.
- static address_v4 broadcast(const address_v4& addr, const address_v4& mask)
- {
- return address_v4(addr.to_ulong() | ~mask.to_ulong());
- }
+ ASIO_DECL static address_v4 broadcast(
+ const address_v4& addr, const address_v4& mask);
/// Obtain the netmask that corresponds to the address, based on its address
/// class.
- static address_v4 netmask(const address_v4& addr)
- {
- if (addr.is_class_a())
- return address_v4(0xFF000000);
- if (addr.is_class_b())
- return address_v4(0xFFFF0000);
- if (addr.is_class_c())
- return address_v4(0xFFFFFF00);
- return address_v4(0xFFFFFFFF);
- }
+ ASIO_DECL static address_v4 netmask(const address_v4& addr);
private:
// The underlying IPv4 address.
@@ -288,22 +193,7 @@ private:
*/
template <typename Elem, typename Traits>
std::basic_ostream<Elem, Traits>& operator<<(
- std::basic_ostream<Elem, Traits>& os, const address_v4& addr)
-{
- asio::error_code ec;
- std::string s = addr.to_string(ec);
- if (ec)
- {
- if (os.exceptions() & std::ios::failbit)
- asio::detail::throw_error(ec);
- else
- os.setstate(std::ios_base::failbit);
- }
- else
- for (std::string::iterator i = s.begin(); i != s.end(); ++i)
- os << os.widen(*i);
- return os;
-}
+ std::basic_ostream<Elem, Traits>& os, const address_v4& addr);
#endif // !defined(BOOST_NO_IOSTREAM)
@@ -312,4 +202,9 @@ std::basic_ostream<Elem, Traits>& operator<<(
#include "asio/detail/pop_options.hpp"
+#include "asio/ip/impl/address_v4.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/ip/impl/address_v4.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
#endif // ASIO_IP_ADDRESS_V4_HPP
diff --git a/ext/asio/asio/ip/address_v6.hpp b/ext/asio/asio/ip/address_v6.hpp
index 8d2c083..261c651 100644
--- a/ext/asio/asio/ip/address_v6.hpp
+++ b/ext/asio/asio/ip/address_v6.hpp
@@ -1,8 +1,8 @@
//
-// address_v6.hpp
-// ~~~~~~~~~~~~~~
+// ip/address_v6.hpp
+// ~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,26 +15,19 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+#include <string>
+#include <boost/array.hpp>
+#include "asio/detail/socket_types.hpp"
+#include "asio/detail/winsock_init.hpp"
+#include "asio/error_code.hpp"
+#include "asio/ip/address_v4.hpp"
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
#if !defined(BOOST_NO_IOSTREAM)
# include <iosfwd>
#endif // !defined(BOOST_NO_IOSTREAM)
-#include <cstring>
-#include <string>
-#include <stdexcept>
-#include <typeinfo>
-#include <boost/array.hpp>
-#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
-#include "asio/error.hpp"
-#include "asio/detail/socket_ops.hpp"
-#include "asio/detail/socket_types.hpp"
-#include "asio/detail/throw_error.hpp"
-#include "asio/ip/address_v4.hpp"
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace ip {
@@ -55,46 +48,17 @@ public:
typedef boost::array<unsigned char, 16> bytes_type;
/// Default constructor.
- address_v6()
- : scope_id_(0)
- {
- asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT;
- addr_ = tmp_addr;
- }
+ ASIO_DECL address_v6();
/// Construct an address from raw bytes and scope ID.
- explicit address_v6(const bytes_type& bytes, unsigned long scope_id = 0)
- : scope_id_(scope_id)
- {
-#if UCHAR_MAX > 0xFF
- for (std::size_t i = 0; i < bytes.size(); ++i)
- {
- if (bytes[i] > 0xFF)
- {
- std::out_of_range ex("address_v6 from bytes_type");
- boost::throw_exception(ex);
- }
- }
-#endif // UCHAR_MAX > 0xFF
-
- using namespace std; // For memcpy.
- memcpy(addr_.s6_addr, bytes.elems, 16);
- }
+ ASIO_DECL explicit address_v6(const bytes_type& bytes,
+ unsigned long scope_id = 0);
/// Copy constructor.
- address_v6(const address_v6& other)
- : addr_(other.addr_),
- scope_id_(other.scope_id_)
- {
- }
+ ASIO_DECL address_v6(const address_v6& other);
/// Assign from another address.
- address_v6& operator=(const address_v6& other)
- {
- addr_ = other.addr_;
- scope_id_ = other.scope_id_;
- return *this;
- }
+ ASIO_DECL address_v6& operator=(const address_v6& other);
/// The scope ID of the address.
/**
@@ -115,217 +79,80 @@ public:
}
/// Get the address in bytes, in network byte order.
- bytes_type to_bytes() const
- {
- using namespace std; // For memcpy.
- bytes_type bytes;
- memcpy(bytes.elems, addr_.s6_addr, 16);
- return bytes;
- }
+ ASIO_DECL bytes_type to_bytes() const;
/// Get the address as a string.
- std::string to_string() const
- {
- asio::error_code ec;
- std::string addr = to_string(ec);
- asio::detail::throw_error(ec);
- return addr;
- }
+ ASIO_DECL std::string to_string() const;
/// Get the address as a string.
- std::string to_string(asio::error_code& ec) const
- {
- char addr_str[asio::detail::max_addr_v6_str_len];
- const char* addr =
- asio::detail::socket_ops::inet_ntop(AF_INET6, &addr_, addr_str,
- asio::detail::max_addr_v6_str_len, scope_id_, ec);
- if (addr == 0)
- return std::string();
- return addr;
- }
+ ASIO_DECL std::string to_string(asio::error_code& ec) const;
/// Create an address from an IP address string.
- static address_v6 from_string(const char* str)
- {
- asio::error_code ec;
- address_v6 addr = from_string(str, ec);
- asio::detail::throw_error(ec);
- return addr;
- }
+ ASIO_DECL static address_v6 from_string(const char* str);
/// Create an address from an IP address string.
- static address_v6 from_string(const char* str, asio::error_code& ec)
- {
- address_v6 tmp;
- if (asio::detail::socket_ops::inet_pton(
- AF_INET6, str, &tmp.addr_, &tmp.scope_id_, ec) <= 0)
- return address_v6();
- return tmp;
- }
+ ASIO_DECL static address_v6 from_string(
+ const char* str, asio::error_code& ec);
/// Create an address from an IP address string.
- static address_v6 from_string(const std::string& str)
- {
- return from_string(str.c_str());
- }
+ ASIO_DECL static address_v6 from_string(const std::string& str);
/// Create an address from an IP address string.
- static address_v6 from_string(const std::string& str,
- asio::error_code& ec)
- {
- return from_string(str.c_str(), ec);
- }
+ ASIO_DECL static address_v6 from_string(
+ const std::string& str, asio::error_code& ec);
/// Converts an IPv4-mapped or IPv4-compatible address to an IPv4 address.
- address_v4 to_v4() const
- {
- if (!is_v4_mapped() && !is_v4_compatible())
- {
- std::bad_cast ex;
- boost::throw_exception(ex);
- }
-
- address_v4::bytes_type v4_bytes = { { addr_.s6_addr[12],
- addr_.s6_addr[13], addr_.s6_addr[14], addr_.s6_addr[15] } };
- return address_v4(v4_bytes);
- }
+ ASIO_DECL address_v4 to_v4() const;
/// Determine whether the address is a loopback address.
- bool is_loopback() const
- {
-#if defined(__BORLANDC__)
- return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0)
- && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0)
- && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0)
- && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0)
- && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0)
- && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0)
- && (addr_.s6_addr[12] == 0) && (addr_.s6_addr[13] == 0)
- && (addr_.s6_addr[14] == 0) && (addr_.s6_addr[15] == 1));
-#else
- using namespace asio::detail;
- return IN6_IS_ADDR_LOOPBACK(&addr_) != 0;
-#endif
- }
+ ASIO_DECL bool is_loopback() const;
/// Determine whether the address is unspecified.
- bool is_unspecified() const
- {
-#if defined(__BORLANDC__)
- return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0)
- && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0)
- && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0)
- && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0)
- && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0)
- && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0)
- && (addr_.s6_addr[12] == 0) && (addr_.s6_addr[13] == 0)
- && (addr_.s6_addr[14] == 0) && (addr_.s6_addr[15] == 0));
-#else
- using namespace asio::detail;
- return IN6_IS_ADDR_UNSPECIFIED(&addr_) != 0;
-#endif
- }
+ ASIO_DECL bool is_unspecified() const;
/// Determine whether the address is link local.
- bool is_link_local() const
- {
- using namespace asio::detail;
- return IN6_IS_ADDR_LINKLOCAL(&addr_) != 0;
- }
+ ASIO_DECL bool is_link_local() const;
/// Determine whether the address is site local.
- bool is_site_local() const
- {
- using namespace asio::detail;
- return IN6_IS_ADDR_SITELOCAL(&addr_) != 0;
- }
+ ASIO_DECL bool is_site_local() const;
/// Determine whether the address is a mapped IPv4 address.
- bool is_v4_mapped() const
- {
- using namespace asio::detail;
- return IN6_IS_ADDR_V4MAPPED(&addr_) != 0;
- }
+ ASIO_DECL bool is_v4_mapped() const;
/// Determine whether the address is an IPv4-compatible address.
- bool is_v4_compatible() const
- {
- using namespace asio::detail;
- return IN6_IS_ADDR_V4COMPAT(&addr_) != 0;
- }
+ ASIO_DECL bool is_v4_compatible() const;
/// Determine whether the address is a multicast address.
- bool is_multicast() const
- {
- using namespace asio::detail;
- return IN6_IS_ADDR_MULTICAST(&addr_) != 0;
- }
+ ASIO_DECL bool is_multicast() const;
/// Determine whether the address is a global multicast address.
- bool is_multicast_global() const
- {
- using namespace asio::detail;
- return IN6_IS_ADDR_MC_GLOBAL(&addr_) != 0;
- }
+ ASIO_DECL bool is_multicast_global() const;
/// Determine whether the address is a link-local multicast address.
- bool is_multicast_link_local() const
- {
- using namespace asio::detail;
- return IN6_IS_ADDR_MC_LINKLOCAL(&addr_) != 0;
- }
+ ASIO_DECL bool is_multicast_link_local() const;
/// Determine whether the address is a node-local multicast address.
- bool is_multicast_node_local() const
- {
- using namespace asio::detail;
- return IN6_IS_ADDR_MC_NODELOCAL(&addr_) != 0;
- }
+ ASIO_DECL bool is_multicast_node_local() const;
/// Determine whether the address is a org-local multicast address.
- bool is_multicast_org_local() const
- {
- using namespace asio::detail;
- return IN6_IS_ADDR_MC_ORGLOCAL(&addr_) != 0;
- }
+ ASIO_DECL bool is_multicast_org_local() const;
/// Determine whether the address is a site-local multicast address.
- bool is_multicast_site_local() const
- {
- using namespace asio::detail;
- return IN6_IS_ADDR_MC_SITELOCAL(&addr_) != 0;
- }
+ ASIO_DECL bool is_multicast_site_local() const;
/// Compare two addresses for equality.
- friend bool operator==(const address_v6& a1, const address_v6& a2)
- {
- using namespace std; // For memcmp.
- return memcmp(&a1.addr_, &a2.addr_,
- sizeof(asio::detail::in6_addr_type)) == 0
- && a1.scope_id_ == a2.scope_id_;
- }
+ ASIO_DECL friend bool operator==(
+ const address_v6& a1, const address_v6& a2);
/// Compare two addresses for inequality.
friend bool operator!=(const address_v6& a1, const address_v6& a2)
{
- using namespace std; // For memcmp.
- return memcmp(&a1.addr_, &a2.addr_,
- sizeof(asio::detail::in6_addr_type)) != 0
- || a1.scope_id_ != a2.scope_id_;
+ return !(a1 == a2);
}
/// Compare addresses for ordering.
- friend bool operator<(const address_v6& a1, const address_v6& a2)
- {
- using namespace std; // For memcmp.
- int memcmp_result = memcmp(&a1.addr_, &a2.addr_,
- sizeof(asio::detail::in6_addr_type));
- if (memcmp_result < 0)
- return true;
- if (memcmp_result > 0)
- return false;
- return a1.scope_id_ < a2.scope_id_;
- }
+ ASIO_DECL friend bool operator<(
+ const address_v6& a1, const address_v6& a2);
/// Compare addresses for ordering.
friend bool operator>(const address_v6& a1, const address_v6& a2)
@@ -352,31 +179,13 @@ public:
}
/// Obtain an address object that represents the loopback address.
- static address_v6 loopback()
- {
- address_v6 tmp;
- asio::detail::in6_addr_type tmp_addr = IN6ADDR_LOOPBACK_INIT;
- tmp.addr_ = tmp_addr;
- return tmp;
- }
+ ASIO_DECL static address_v6 loopback();
/// Create an IPv4-mapped IPv6 address.
- static address_v6 v4_mapped(const address_v4& addr)
- {
- address_v4::bytes_type v4_bytes = addr.to_bytes();
- bytes_type v6_bytes = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF,
- v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] } };
- return address_v6(v6_bytes);
- }
+ ASIO_DECL static address_v6 v4_mapped(const address_v4& addr);
/// Create an IPv4-compatible IPv6 address.
- static address_v6 v4_compatible(const address_v4& addr)
- {
- address_v4::bytes_type v4_bytes = addr.to_bytes();
- bytes_type v6_bytes = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] } };
- return address_v6(v6_bytes);
- }
+ ASIO_DECL static address_v6 v4_compatible(const address_v4& addr);
private:
// The underlying IPv6 address.
@@ -402,22 +211,7 @@ private:
*/
template <typename Elem, typename Traits>
std::basic_ostream<Elem, Traits>& operator<<(
- std::basic_ostream<Elem, Traits>& os, const address_v6& addr)
-{
- asio::error_code ec;
- std::string s = addr.to_string(ec);
- if (ec)
- {
- if (os.exceptions() & std::ios::failbit)
- asio::detail::throw_error(ec);
- else
- os.setstate(std::ios_base::failbit);
- }
- else
- for (std::string::iterator i = s.begin(); i != s.end(); ++i)
- os << os.widen(*i);
- return os;
-}
+ std::basic_ostream<Elem, Traits>& os, const address_v6& addr);
#endif // !defined(BOOST_NO_IOSTREAM)
@@ -426,4 +220,9 @@ std::basic_ostream<Elem, Traits>& operator<<(
#include "asio/detail/pop_options.hpp"
+#include "asio/ip/impl/address_v6.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/ip/impl/address_v6.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
#endif // ASIO_IP_ADDRESS_V6_HPP
diff --git a/ext/asio/asio/ip/basic_endpoint.hpp b/ext/asio/asio/ip/basic_endpoint.hpp
index 4ad3f68..5e5ef0b 100644
--- a/ext/asio/asio/ip/basic_endpoint.hpp
+++ b/ext/asio/asio/ip/basic_endpoint.hpp
@@ -1,8 +1,8 @@
//
-// basic_endpoint.hpp
-// ~~~~~~~~~~~~~~~~~~
+// ip/basic_endpoint.hpp
+// ~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,25 +15,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+#include "asio/ip/address.hpp"
+#include "asio/ip/detail/endpoint.hpp"
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include <boost/throw_exception.hpp>
-#include <boost/detail/workaround.hpp>
-#include <cstring>
#if !defined(BOOST_NO_IOSTREAM)
-# if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
-# include <ostream>
-# endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
-# include <sstream>
+# include <iosfwd>
#endif // !defined(BOOST_NO_IOSTREAM)
-#include "asio/detail/pop_options.hpp"
-#include "asio/error.hpp"
-#include "asio/ip/address.hpp"
-#include "asio/detail/socket_ops.hpp"
-#include "asio/detail/socket_types.hpp"
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace ip {
@@ -67,11 +57,8 @@ public:
/// Default constructor.
basic_endpoint()
- : data_()
+ : impl_()
{
- data_.v4.sin_family = AF_INET;
- data_.v4.sin_port = 0;
- data_.v4.sin_addr.s_addr = INADDR_ANY;
}
/// Construct an endpoint using a port number, specified in the host's byte
@@ -91,74 +78,35 @@ public:
* @endcode
*/
basic_endpoint(const InternetProtocol& protocol, unsigned short port_num)
- : data_()
+ : impl_(protocol.family(), port_num)
{
- using namespace std; // For memcpy.
- if (protocol.family() == PF_INET)
- {
- data_.v4.sin_family = AF_INET;
- data_.v4.sin_port =
- asio::detail::socket_ops::host_to_network_short(port_num);
- data_.v4.sin_addr.s_addr = INADDR_ANY;
- }
- else
- {
- data_.v6.sin6_family = AF_INET6;
- data_.v6.sin6_port =
- asio::detail::socket_ops::host_to_network_short(port_num);
- data_.v6.sin6_flowinfo = 0;
- asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT;
- data_.v6.sin6_addr = tmp_addr;
- data_.v6.sin6_scope_id = 0;
- }
}
/// Construct an endpoint using a port number and an IP address. This
/// constructor may be used for accepting connections on a specific interface
/// or for making a connection to a remote endpoint.
basic_endpoint(const asio::ip::address& addr, unsigned short port_num)
- : data_()
+ : impl_(addr, port_num)
{
- using namespace std; // For memcpy.
- if (addr.is_v4())
- {
- data_.v4.sin_family = AF_INET;
- data_.v4.sin_port =
- asio::detail::socket_ops::host_to_network_short(port_num);
- data_.v4.sin_addr.s_addr =
- asio::detail::socket_ops::host_to_network_long(
- addr.to_v4().to_ulong());
- }
- else
- {
- data_.v6.sin6_family = AF_INET6;
- data_.v6.sin6_port =
- asio::detail::socket_ops::host_to_network_short(port_num);
- data_.v6.sin6_flowinfo = 0;
- asio::ip::address_v6 v6_addr = addr.to_v6();
- asio::ip::address_v6::bytes_type bytes = v6_addr.to_bytes();
- memcpy(data_.v6.sin6_addr.s6_addr, bytes.elems, 16);
- data_.v6.sin6_scope_id = v6_addr.scope_id();
- }
}
/// Copy constructor.
basic_endpoint(const basic_endpoint& other)
- : data_(other.data_)
+ : impl_(other.impl_)
{
}
/// Assign from another endpoint.
basic_endpoint& operator=(const basic_endpoint& other)
{
- data_ = other.data_;
+ impl_ = other.impl_;
return *this;
}
/// The protocol associated with the endpoint.
protocol_type protocol() const
{
- if (is_v4())
+ if (impl_.is_v4())
return InternetProtocol::v4();
return InternetProtocol::v6();
}
@@ -166,137 +114,104 @@ public:
/// Get the underlying endpoint in the native type.
data_type* data()
{
- return &data_.base;
+ return impl_.data();
}
/// Get the underlying endpoint in the native type.
const data_type* data() const
{
- return &data_.base;
+ return impl_.data();
}
/// Get the underlying size of the endpoint in the native type.
std::size_t size() const
{
- if (is_v4())
- return sizeof(asio::detail::sockaddr_in4_type);
- else
- return sizeof(asio::detail::sockaddr_in6_type);
+ return impl_.size();
}
/// Set the underlying size of the endpoint in the native type.
void resize(std::size_t size)
{
- if (size > sizeof(asio::detail::sockaddr_storage_type))
- {
- asio::system_error e(asio::error::invalid_argument);
- boost::throw_exception(e);
- }
+ impl_.resize(size);
}
/// Get the capacity of the endpoint in the native type.
std::size_t capacity() const
{
- return sizeof(asio::detail::sockaddr_storage_type);
+ return impl_.capacity();
}
/// Get the port associated with the endpoint. The port number is always in
/// the host's byte order.
unsigned short port() const
{
- if (is_v4())
- {
- return asio::detail::socket_ops::network_to_host_short(
- data_.v4.sin_port);
- }
- else
- {
- return asio::detail::socket_ops::network_to_host_short(
- data_.v6.sin6_port);
- }
+ return impl_.port();
}
/// Set the port associated with the endpoint. The port number is always in
/// the host's byte order.
void port(unsigned short port_num)
{
- if (is_v4())
- {
- data_.v4.sin_port
- = asio::detail::socket_ops::host_to_network_short(port_num);
- }
- else
- {
- data_.v6.sin6_port
- = asio::detail::socket_ops::host_to_network_short(port_num);
- }
+ impl_.port(port_num);
}
/// Get the IP address associated with the endpoint.
asio::ip::address address() const
{
- using namespace std; // For memcpy.
- if (is_v4())
- {
- return asio::ip::address_v4(
- asio::detail::socket_ops::network_to_host_long(
- data_.v4.sin_addr.s_addr));
- }
- else
- {
- asio::ip::address_v6::bytes_type bytes;
- memcpy(bytes.elems, data_.v6.sin6_addr.s6_addr, 16);
- return asio::ip::address_v6(bytes, data_.v6.sin6_scope_id);
- }
+ return impl_.address();
}
/// Set the IP address associated with the endpoint.
void address(const asio::ip::address& addr)
{
- basic_endpoint<InternetProtocol> tmp_endpoint(addr, port());
- data_ = tmp_endpoint.data_;
+ impl_.address(addr);
}
/// Compare two endpoints for equality.
friend bool operator==(const basic_endpoint<InternetProtocol>& e1,
const basic_endpoint<InternetProtocol>& e2)
{
- return e1.address() == e2.address() && e1.port() == e2.port();
+ return e1.impl_ == e2.impl_;
}
/// Compare two endpoints for inequality.
friend bool operator!=(const basic_endpoint<InternetProtocol>& e1,
const basic_endpoint<InternetProtocol>& e2)
{
- return e1.address() != e2.address() || e1.port() != e2.port();
+ return !(e1 == e2);
}
/// Compare endpoints for ordering.
friend bool operator<(const basic_endpoint<InternetProtocol>& e1,
const basic_endpoint<InternetProtocol>& e2)
{
- if (e1.address() < e2.address())
- return true;
- if (e1.address() != e2.address())
- return false;
- return e1.port() < e2.port();
+ return e1.impl_ < e2.impl_;
}
-private:
- // Helper function to determine whether the endpoint is IPv4.
- bool is_v4() const
+ /// Compare endpoints for ordering.
+ friend bool operator>(const basic_endpoint<InternetProtocol>& e1,
+ const basic_endpoint<InternetProtocol>& e2)
+ {
+ return e2.impl_ < e1.impl_;
+ }
+
+ /// Compare endpoints for ordering.
+ friend bool operator<=(const basic_endpoint<InternetProtocol>& e1,
+ const basic_endpoint<InternetProtocol>& e2)
{
- return data_.base.sa_family == AF_INET;
+ return !(e2 < e1);
}
- // The underlying IP socket address.
- union data_union
+ /// Compare endpoints for ordering.
+ friend bool operator>=(const basic_endpoint<InternetProtocol>& e1,
+ const basic_endpoint<InternetProtocol>& e2)
{
- asio::detail::socket_addr_type base;
- asio::detail::sockaddr_storage_type storage;
- asio::detail::sockaddr_in4_type v4;
- asio::detail::sockaddr_in6_type v6;
- } data_;
+ return !(e1 < e2);
+ }
+
+private:
+ // The underlying IP endpoint.
+ asio::ip::detail::endpoint impl_;
};
#if !defined(BOOST_NO_IOSTREAM)
@@ -313,64 +228,10 @@ private:
*
* @relates asio::ip::basic_endpoint
*/
-#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
-template <typename InternetProtocol>
-std::ostream& operator<<(std::ostream& os,
- const basic_endpoint<InternetProtocol>& endpoint)
-{
- const address& addr = endpoint.address();
- asio::error_code ec;
- std::string a = addr.to_string(ec);
- if (ec)
- {
- if (os.exceptions() & std::ios::failbit)
- asio::detail::throw_error(ec);
- else
- os.setstate(std::ios_base::failbit);
- }
- else
- {
- std::ostringstream tmp_os;
- tmp_os.imbue(std::locale::classic());
- if (addr.is_v4())
- tmp_os << a;
- else
- tmp_os << '[' << a << ']';
- tmp_os << ':' << endpoint.port();
- os << tmp_os.str();
- }
- return os;
-}
-#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
template <typename Elem, typename Traits, typename InternetProtocol>
std::basic_ostream<Elem, Traits>& operator<<(
std::basic_ostream<Elem, Traits>& os,
- const basic_endpoint<InternetProtocol>& endpoint)
-{
- const address& addr = endpoint.address();
- asio::error_code ec;
- std::string a = addr.to_string(ec);
- if (ec)
- {
- if (os.exceptions() & std::ios::failbit)
- asio::detail::throw_error(ec);
- else
- os.setstate(std::ios_base::failbit);
- }
- else
- {
- std::ostringstream tmp_os;
- tmp_os.imbue(std::locale::classic());
- if (addr.is_v4())
- tmp_os << a;
- else
- tmp_os << '[' << a << ']';
- tmp_os << ':' << endpoint.port();
- os << tmp_os.str();
- }
- return os;
-}
-#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
+ const basic_endpoint<InternetProtocol>& endpoint);
#endif // !defined(BOOST_NO_IOSTREAM)
@@ -379,4 +240,6 @@ std::basic_ostream<Elem, Traits>& operator<<(
#include "asio/detail/pop_options.hpp"
+#include "asio/ip/impl/basic_endpoint.hpp"
+
#endif // ASIO_IP_BASIC_ENDPOINT_HPP
diff --git a/ext/asio/asio/ip/basic_resolver.hpp b/ext/asio/asio/ip/basic_resolver.hpp
index 43a37af..c8847e1 100644
--- a/ext/asio/asio/ip/basic_resolver.hpp
+++ b/ext/asio/asio/ip/basic_resolver.hpp
@@ -1,8 +1,8 @@
//
-// basic_resolver.hpp
-// ~~~~~~~~~~~~~~~~~~
+// ip/basic_resolver.hpp
+// ~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,14 +15,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/basic_io_object.hpp"
+#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/ip/basic_resolver_iterator.hpp"
#include "asio/ip/basic_resolver_query.hpp"
#include "asio/ip/resolver_service.hpp"
-#include "asio/detail/throw_error.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace ip {
diff --git a/ext/asio/asio/ip/basic_resolver_entry.hpp b/ext/asio/asio/ip/basic_resolver_entry.hpp
index 09b144b..4117b7b 100644
--- a/ext/asio/asio/ip/basic_resolver_entry.hpp
+++ b/ext/asio/asio/ip/basic_resolver_entry.hpp
@@ -1,8 +1,8 @@
//
-// basic_resolver_entry.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~
+// ip/basic_resolver_entry.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,11 +15,10 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+#include <string>
#include "asio/detail/push_options.hpp"
-#include <string>
-#include "asio/detail/pop_options.hpp"
namespace asio {
namespace ip {
diff --git a/ext/asio/asio/ip/basic_resolver_iterator.hpp b/ext/asio/asio/ip/basic_resolver_iterator.hpp
index 1982d62..033b7e0 100644
--- a/ext/asio/asio/ip/basic_resolver_iterator.hpp
+++ b/ext/asio/asio/ip/basic_resolver_iterator.hpp
@@ -1,8 +1,8 @@
//
-// basic_resolver_iterator.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// ip/basic_resolver_iterator.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,20 +15,18 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <boost/iterator.hpp>
-#include <boost/shared_ptr.hpp>
#include <cstring>
#include <string>
#include <vector>
-#include "asio/detail/pop_options.hpp"
-
+#include "asio/detail/shared_ptr.hpp"
#include "asio/detail/socket_ops.hpp"
#include "asio/detail/socket_types.hpp"
#include "asio/ip/basic_resolver_entry.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace ip {
@@ -176,7 +174,7 @@ private:
}
typedef std::vector<basic_resolver_entry<InternetProtocol> > values_type;
- boost::shared_ptr<values_type> values_;
+ asio::detail::shared_ptr<values_type> values_;
std::size_t index_;
};
diff --git a/ext/asio/asio/ip/basic_resolver_query.hpp b/ext/asio/asio/ip/basic_resolver_query.hpp
index 3cbb335..22ab643 100644
--- a/ext/asio/asio/ip/basic_resolver_query.hpp
+++ b/ext/asio/asio/ip/basic_resolver_query.hpp
@@ -1,8 +1,8 @@
//
-// basic_resolver_query.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~
+// ip/basic_resolver_query.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
+#include "asio/detail/config.hpp"
#include <string>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/detail/socket_ops.hpp"
#include "asio/ip/resolver_query_base.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace ip {
diff --git a/ext/asio/asio/ip/detail/endpoint.hpp b/ext/asio/asio/ip/detail/endpoint.hpp
new file mode 100644
index 0000000..fbea71e
--- /dev/null
+++ b/ext/asio/asio/ip/detail/endpoint.hpp
@@ -0,0 +1,140 @@
+//
+// ip/detail/endpoint.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_DETAIL_ENDPOINT_HPP
+#define ASIO_IP_DETAIL_ENDPOINT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <string>
+#include "asio/detail/socket_types.hpp"
+#include "asio/detail/winsock_init.hpp"
+#include "asio/error_code.hpp"
+#include "asio/ip/address.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace ip {
+namespace detail {
+
+// Helper class for implementating an IP endpoint.
+class endpoint
+{
+public:
+ // Default constructor.
+ ASIO_DECL endpoint();
+
+ // Construct an endpoint using a family and port number.
+ ASIO_DECL endpoint(int family, unsigned short port_num);
+
+ // Construct an endpoint using an address and port number.
+ ASIO_DECL endpoint(const asio::ip::address& addr,
+ unsigned short port_num);
+
+ // Copy constructor.
+ endpoint(const endpoint& other)
+ : data_(other.data_)
+ {
+ }
+
+ // Assign from another endpoint.
+ endpoint& operator=(const endpoint& other)
+ {
+ data_ = other.data_;
+ return *this;
+ }
+
+ // Get the underlying endpoint in the native type.
+ asio::detail::socket_addr_type* data()
+ {
+ return &data_.base;
+ }
+
+ // Get the underlying endpoint in the native type.
+ const asio::detail::socket_addr_type* data() const
+ {
+ return &data_.base;
+ }
+
+ // Get the underlying size of the endpoint in the native type.
+ std::size_t size() const
+ {
+ if (is_v4())
+ return sizeof(asio::detail::sockaddr_in4_type);
+ else
+ return sizeof(asio::detail::sockaddr_in6_type);
+ }
+
+ // Set the underlying size of the endpoint in the native type.
+ ASIO_DECL void resize(std::size_t size);
+
+ // Get the capacity of the endpoint in the native type.
+ std::size_t capacity() const
+ {
+ return sizeof(asio::detail::sockaddr_storage_type);
+ }
+
+ // Get the port associated with the endpoint.
+ ASIO_DECL unsigned short port() const;
+
+ // Set the port associated with the endpoint.
+ ASIO_DECL void port(unsigned short port_num);
+
+ // Get the IP address associated with the endpoint.
+ ASIO_DECL asio::ip::address address() const;
+
+ // Set the IP address associated with the endpoint.
+ ASIO_DECL void address(const asio::ip::address& addr);
+
+ // Compare two endpoints for equality.
+ ASIO_DECL friend bool operator==(
+ const endpoint& e1, const endpoint& e2);
+
+ // Compare endpoints for ordering.
+ ASIO_DECL friend bool operator<(
+ const endpoint& e1, const endpoint& e2);
+
+ // Determine whether the endpoint is IPv4.
+ bool is_v4() const
+ {
+ return data_.base.sa_family == AF_INET;
+ }
+
+#if !defined(BOOST_NO_IOSTREAM)
+ // Convert to a string.
+ ASIO_DECL std::string to_string(asio::error_code& ec) const;
+#endif // !defined(BOOST_NO_IOSTREAM)
+
+private:
+ // The underlying IP socket address.
+ union data_union
+ {
+ asio::detail::socket_addr_type base;
+ asio::detail::sockaddr_storage_type storage;
+ asio::detail::sockaddr_in4_type v4;
+ asio::detail::sockaddr_in6_type v6;
+ } data_;
+};
+
+} // namespace detail
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/ip/detail/impl/endpoint.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // ASIO_IP_DETAIL_ENDPOINT_HPP
diff --git a/ext/asio/asio/ip/detail/impl/endpoint.ipp b/ext/asio/asio/ip/detail/impl/endpoint.ipp
new file mode 100644
index 0000000..b1469c8
--- /dev/null
+++ b/ext/asio/asio/ip/detail/impl/endpoint.ipp
@@ -0,0 +1,202 @@
+//
+// ip/detail/impl/endpoint.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_DETAIL_IMPL_ENDPOINT_IPP
+#define ASIO_IP_DETAIL_IMPL_ENDPOINT_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <cstring>
+#if !defined(BOOST_NO_IOSTREAM)
+# include <sstream>
+#endif // !defined(BOOST_NO_IOSTREAM)
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+#include "asio/ip/detail/endpoint.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace ip {
+namespace detail {
+
+ASIO_DECL
+endpoint::endpoint()
+ : data_()
+{
+ data_.v4.sin_family = AF_INET;
+ data_.v4.sin_port = 0;
+ data_.v4.sin_addr.s_addr = INADDR_ANY;
+}
+
+ASIO_DECL
+endpoint::endpoint(int family, unsigned short port_num)
+ : data_()
+{
+ using namespace std; // For memcpy.
+ if (family == PF_INET)
+ {
+ data_.v4.sin_family = AF_INET;
+ data_.v4.sin_port =
+ asio::detail::socket_ops::host_to_network_short(port_num);
+ data_.v4.sin_addr.s_addr = INADDR_ANY;
+ }
+ else
+ {
+ data_.v6.sin6_family = AF_INET6;
+ data_.v6.sin6_port =
+ asio::detail::socket_ops::host_to_network_short(port_num);
+ data_.v6.sin6_flowinfo = 0;
+ asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT;
+ data_.v6.sin6_addr = tmp_addr;
+ data_.v6.sin6_scope_id = 0;
+ }
+}
+
+ASIO_DECL
+endpoint::endpoint(const asio::ip::address& addr,
+ unsigned short port_num)
+ : data_()
+{
+ using namespace std; // For memcpy.
+ if (addr.is_v4())
+ {
+ data_.v4.sin_family = AF_INET;
+ data_.v4.sin_port =
+ asio::detail::socket_ops::host_to_network_short(port_num);
+ data_.v4.sin_addr.s_addr =
+ asio::detail::socket_ops::host_to_network_long(
+ addr.to_v4().to_ulong());
+ }
+ else
+ {
+ data_.v6.sin6_family = AF_INET6;
+ data_.v6.sin6_port =
+ asio::detail::socket_ops::host_to_network_short(port_num);
+ data_.v6.sin6_flowinfo = 0;
+ asio::ip::address_v6 v6_addr = addr.to_v6();
+ asio::ip::address_v6::bytes_type bytes = v6_addr.to_bytes();
+ memcpy(data_.v6.sin6_addr.s6_addr, bytes.elems, 16);
+ data_.v6.sin6_scope_id = v6_addr.scope_id();
+ }
+}
+
+ASIO_DECL
+void endpoint::resize(std::size_t size)
+{
+ if (size > sizeof(asio::detail::sockaddr_storage_type))
+ {
+ asio::error_code ec(asio::error::invalid_argument);
+ asio::detail::throw_error(ec);
+ }
+}
+
+ASIO_DECL
+unsigned short endpoint::port() const
+{
+ if (is_v4())
+ {
+ return asio::detail::socket_ops::network_to_host_short(
+ data_.v4.sin_port);
+ }
+ else
+ {
+ return asio::detail::socket_ops::network_to_host_short(
+ data_.v6.sin6_port);
+ }
+}
+
+ASIO_DECL
+void endpoint::port(unsigned short port_num)
+{
+ if (is_v4())
+ {
+ data_.v4.sin_port
+ = asio::detail::socket_ops::host_to_network_short(port_num);
+ }
+ else
+ {
+ data_.v6.sin6_port
+ = asio::detail::socket_ops::host_to_network_short(port_num);
+ }
+}
+
+ASIO_DECL
+asio::ip::address endpoint::address() const
+{
+ using namespace std; // For memcpy.
+ if (is_v4())
+ {
+ return asio::ip::address_v4(
+ asio::detail::socket_ops::network_to_host_long(
+ data_.v4.sin_addr.s_addr));
+ }
+ else
+ {
+ asio::ip::address_v6::bytes_type bytes;
+ memcpy(bytes.elems, data_.v6.sin6_addr.s6_addr, 16);
+ return asio::ip::address_v6(bytes, data_.v6.sin6_scope_id);
+ }
+}
+
+ASIO_DECL
+void endpoint::address(const asio::ip::address& addr)
+{
+ endpoint tmp_endpoint(addr, port());
+ data_ = tmp_endpoint.data_;
+}
+
+ASIO_DECL
+bool operator==(const endpoint& e1, const endpoint& e2)
+{
+ return e1.address() == e2.address() && e1.port() == e2.port();
+}
+
+ASIO_DECL
+bool operator<(const endpoint& e1, const endpoint& e2)
+{
+ if (e1.address() < e2.address())
+ return true;
+ if (e1.address() != e2.address())
+ return false;
+ return e1.port() < e2.port();
+}
+
+#if !defined(BOOST_NO_IOSTREAM)
+ASIO_DECL
+std::string endpoint::to_string(asio::error_code& ec) const
+{
+ std::string a = address().to_string(ec);
+ if (ec)
+ return std::string();
+
+ std::ostringstream tmp_os;
+ tmp_os.imbue(std::locale::classic());
+ if (is_v4())
+ tmp_os << a;
+ else
+ tmp_os << '[' << a << ']';
+ tmp_os << ':' << port();
+
+ return tmp_os.str();
+}
+#endif // !defined(BOOST_NO_IOSTREAM)
+
+} // namespace detail
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_DETAIL_IMPL_ENDPOINT_IPP
diff --git a/ext/asio/asio/ip/detail/socket_option.hpp b/ext/asio/asio/ip/detail/socket_option.hpp
index 00045f8..e370b11 100644
--- a/ext/asio/asio/ip/detail/socket_option.hpp
+++ b/ext/asio/asio/ip/detail/socket_option.hpp
@@ -1,8 +1,8 @@
//
-// socket_option.hpp
-// ~~~~~~~~~~~~~~~~~
+// detail/socket_option.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,18 +15,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
#include <cstring>
-#include <boost/config.hpp>
#include <boost/throw_exception.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/ip/address.hpp"
#include "asio/detail/socket_ops.hpp"
#include "asio/detail/socket_types.hpp"
+#include "asio/ip/address.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace ip {
diff --git a/ext/asio/asio/ip/host_name.hpp b/ext/asio/asio/ip/host_name.hpp
index f24ce1a..8bfaae9 100644
--- a/ext/asio/asio/ip/host_name.hpp
+++ b/ext/asio/asio/ip/host_name.hpp
@@ -1,8 +1,8 @@
//
-// host_name.hpp
-// ~~~~~~~~~~~~~
+// ip/host_name.hpp
+// ~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,48 +15,28 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <string>
-#include "asio/detail/pop_options.hpp"
+#include "asio/error_code.hpp"
-#include "asio/error.hpp"
-#include "asio/detail/socket_ops.hpp"
-#include "asio/detail/throw_error.hpp"
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace ip {
/// Get the current host name.
-std::string host_name();
+ASIO_DECL std::string host_name();
/// Get the current host name.
-std::string host_name(asio::error_code& ec);
-
-inline std::string host_name()
-{
- char name[1024];
- asio::error_code ec;
- if (asio::detail::socket_ops::gethostname(name, sizeof(name), ec) != 0)
- {
- asio::detail::throw_error(ec);
- return std::string();
- }
- return std::string(name);
-}
-
-inline std::string host_name(asio::error_code& ec)
-{
- char name[1024];
- if (asio::detail::socket_ops::gethostname(name, sizeof(name), ec) != 0)
- return std::string();
- return std::string(name);
-}
+ASIO_DECL std::string host_name(asio::error_code& ec);
} // namespace ip
} // namespace asio
#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/ip/impl/host_name.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
#endif // ASIO_IP_HOST_NAME_HPP
diff --git a/ext/asio/asio/ip/icmp.hpp b/ext/asio/asio/ip/icmp.hpp
index d76b4d1..59df5b0 100644
--- a/ext/asio/asio/ip/icmp.hpp
+++ b/ext/asio/asio/ip/icmp.hpp
@@ -1,8 +1,8 @@
//
-// icmp.hpp
-// ~~~~~~~~
+// ip/icmp.hpp
+// ~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,14 +15,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
+#include "asio/detail/socket_types.hpp"
#include "asio/basic_raw_socket.hpp"
#include "asio/ip/basic_endpoint.hpp"
#include "asio/ip/basic_resolver.hpp"
#include "asio/ip/basic_resolver_iterator.hpp"
#include "asio/ip/basic_resolver_query.hpp"
-#include "asio/detail/socket_types.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace ip {
diff --git a/ext/asio/asio/ip/impl/address.hpp b/ext/asio/asio/ip/impl/address.hpp
new file mode 100644
index 0000000..0983d24
--- /dev/null
+++ b/ext/asio/asio/ip/impl/address.hpp
@@ -0,0 +1,53 @@
+//
+// ip/impl/address.hpp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_IMPL_ADDRESS_HPP
+#define ASIO_IP_IMPL_ADDRESS_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#if !defined(BOOST_NO_IOSTREAM)
+
+#include "asio/detail/throw_error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace ip {
+
+template <typename Elem, typename Traits>
+std::basic_ostream<Elem, Traits>& operator<<(
+ std::basic_ostream<Elem, Traits>& os, const address& addr)
+{
+ asio::error_code ec;
+ std::string s = addr.to_string(ec);
+ if (ec)
+ {
+ if (os.exceptions() & std::basic_ostream<Elem, Traits>::failbit)
+ asio::detail::throw_error(ec);
+ else
+ os.setstate(std::basic_ostream<Elem, Traits>::failbit);
+ }
+ else
+ for (std::string::iterator i = s.begin(); i != s.end(); ++i)
+ os << os.widen(*i);
+ return os;
+}
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // !defined(BOOST_NO_IOSTREAM)
+
+#endif // ASIO_IP_IMPL_ADDRESS_HPP
diff --git a/ext/asio/asio/ip/impl/address.ipp b/ext/asio/asio/ip/impl/address.ipp
new file mode 100644
index 0000000..24916ab
--- /dev/null
+++ b/ext/asio/asio/ip/impl/address.ipp
@@ -0,0 +1,203 @@
+//
+// ip/impl/address.ipp
+// ~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_IMPL_ADDRESS_IPP
+#define ASIO_IP_IMPL_ADDRESS_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <typeinfo>
+#include <boost/throw_exception.hpp>
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+#include "asio/ip/address.hpp"
+#include "asio/system_error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace ip {
+
+ASIO_DECL
+address::address()
+ : type_(ipv4),
+ ipv4_address_(),
+ ipv6_address_()
+{
+}
+
+ASIO_DECL
+address::address(const asio::ip::address_v4& ipv4_address)
+ : type_(ipv4),
+ ipv4_address_(ipv4_address),
+ ipv6_address_()
+{
+}
+
+ASIO_DECL
+address::address(const asio::ip::address_v6& ipv6_address)
+ : type_(ipv6),
+ ipv4_address_(),
+ ipv6_address_(ipv6_address)
+{
+}
+
+ASIO_DECL
+address::address(const address& other)
+ : type_(other.type_),
+ ipv4_address_(other.ipv4_address_),
+ ipv6_address_(other.ipv6_address_)
+{
+}
+
+ASIO_DECL
+address& address::operator=(const address& other)
+{
+ type_ = other.type_;
+ ipv4_address_ = other.ipv4_address_;
+ ipv6_address_ = other.ipv6_address_;
+ return *this;
+}
+
+ASIO_DECL
+address& address::operator=(const asio::ip::address_v4& ipv4_address)
+{
+ type_ = ipv4;
+ ipv4_address_ = ipv4_address;
+ ipv6_address_ = asio::ip::address_v6();
+ return *this;
+}
+
+ASIO_DECL
+address& address::operator=(const asio::ip::address_v6& ipv6_address)
+{
+ type_ = ipv6;
+ ipv4_address_ = asio::ip::address_v4();
+ ipv6_address_ = ipv6_address;
+ return *this;
+}
+
+ASIO_DECL
+asio::ip::address_v4 address::to_v4() const
+{
+ if (type_ != ipv4)
+ {
+ std::bad_cast ex;
+ boost::throw_exception(ex);
+ }
+ return ipv4_address_;
+}
+
+ASIO_DECL
+asio::ip::address_v6 address::to_v6() const
+{
+ if (type_ != ipv6)
+ {
+ std::bad_cast ex;
+ boost::throw_exception(ex);
+ }
+ return ipv6_address_;
+}
+
+ASIO_DECL
+std::string address::to_string() const
+{
+ if (type_ == ipv6)
+ return ipv6_address_.to_string();
+ return ipv4_address_.to_string();
+}
+
+ASIO_DECL
+std::string address::to_string(asio::error_code& ec) const
+{
+ if (type_ == ipv6)
+ return ipv6_address_.to_string(ec);
+ return ipv4_address_.to_string(ec);
+}
+
+ASIO_DECL
+address address::from_string(const char* str)
+{
+ asio::error_code ec;
+ address addr = from_string(str, ec);
+ asio::detail::throw_error(ec);
+ return addr;
+}
+
+ASIO_DECL
+address address::from_string(const char* str, asio::error_code& ec)
+{
+ asio::ip::address_v6 ipv6_address =
+ asio::ip::address_v6::from_string(str, ec);
+ if (!ec)
+ {
+ address tmp;
+ tmp.type_ = ipv6;
+ tmp.ipv6_address_ = ipv6_address;
+ return tmp;
+ }
+
+ asio::ip::address_v4 ipv4_address =
+ asio::ip::address_v4::from_string(str, ec);
+ if (!ec)
+ {
+ address tmp;
+ tmp.type_ = ipv4;
+ tmp.ipv4_address_ = ipv4_address;
+ return tmp;
+ }
+
+ return address();
+}
+
+ASIO_DECL
+address address::from_string(const std::string& str)
+{
+ return from_string(str.c_str());
+}
+
+ASIO_DECL
+address address::from_string(const std::string& str,
+ asio::error_code& ec)
+{
+ return from_string(str.c_str(), ec);
+}
+
+ASIO_DECL
+bool operator==(const address& a1, const address& a2)
+{
+ if (a1.type_ != a2.type_)
+ return false;
+ if (a1.type_ == address::ipv6)
+ return a1.ipv6_address_ == a2.ipv6_address_;
+ return a1.ipv4_address_ == a2.ipv4_address_;
+}
+
+ASIO_DECL
+bool operator<(const address& a1, const address& a2)
+{
+ if (a1.type_ < a2.type_)
+ return true;
+ if (a1.type_ > a2.type_)
+ return false;
+ if (a1.type_ == address::ipv6)
+ return a1.ipv6_address_ < a2.ipv6_address_;
+ return a1.ipv4_address_ < a2.ipv4_address_;
+}
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_IMPL_ADDRESS_IPP
diff --git a/ext/asio/asio/ip/impl/address_v4.hpp b/ext/asio/asio/ip/impl/address_v4.hpp
new file mode 100644
index 0000000..bbe0882
--- /dev/null
+++ b/ext/asio/asio/ip/impl/address_v4.hpp
@@ -0,0 +1,53 @@
+//
+// ip/impl/address_v4.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_IMPL_ADDRESS_V4_HPP
+#define ASIO_IP_IMPL_ADDRESS_V4_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#if !defined(BOOST_NO_IOSTREAM)
+
+#include "asio/detail/throw_error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace ip {
+
+template <typename Elem, typename Traits>
+std::basic_ostream<Elem, Traits>& operator<<(
+ std::basic_ostream<Elem, Traits>& os, const address_v4& addr)
+{
+ asio::error_code ec;
+ std::string s = addr.to_string(ec);
+ if (ec)
+ {
+ if (os.exceptions() & std::basic_ostream<Elem, Traits>::failbit)
+ asio::detail::throw_error(ec);
+ else
+ os.setstate(std::basic_ostream<Elem, Traits>::failbit);
+ }
+ else
+ for (std::string::iterator i = s.begin(); i != s.end(); ++i)
+ os << os.widen(*i);
+ return os;
+}
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // !defined(BOOST_NO_IOSTREAM)
+
+#endif // ASIO_IP_IMPL_ADDRESS_V4_HPP
diff --git a/ext/asio/asio/ip/impl/address_v4.ipp b/ext/asio/asio/ip/impl/address_v4.ipp
new file mode 100644
index 0000000..2c8e9aa
--- /dev/null
+++ b/ext/asio/asio/ip/impl/address_v4.ipp
@@ -0,0 +1,178 @@
+//
+// ip/impl/address_v4.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_IMPL_ADDRESS_V4_IPP
+#define ASIO_IP_IMPL_ADDRESS_V4_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <climits>
+#include <stdexcept>
+#include <boost/throw_exception.hpp>
+#include "asio/error.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/ip/address_v4.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace ip {
+
+ASIO_DECL
+address_v4::address_v4(const address_v4::bytes_type& bytes)
+{
+#if UCHAR_MAX > 0xFF
+ if (bytes[0] > 0xFF || bytes[1] > 0xFF
+ || bytes[2] > 0xFF || bytes[3] > 0xFF)
+ {
+ std::out_of_range ex("address_v4 from bytes_type");
+ boost::throw_exception(ex);
+ }
+#endif // UCHAR_MAX > 0xFF
+
+ using namespace std; // For memcpy.
+ memcpy(&addr_.s_addr, bytes.elems, 4);
+}
+
+ASIO_DECL
+address_v4::address_v4(unsigned long addr)
+{
+#if ULONG_MAX > 0xFFFFFFFF
+ if (addr > 0xFFFFFFFF)
+ {
+ std::out_of_range ex("address_v4 from unsigned long");
+ boost::throw_exception(ex);
+ }
+#endif // ULONG_MAX > 0xFFFFFFFF
+
+ addr_.s_addr = asio::detail::socket_ops::host_to_network_long(addr);
+}
+
+ASIO_DECL
+address_v4::bytes_type address_v4::to_bytes() const
+{
+ using namespace std; // For memcpy.
+ bytes_type bytes;
+ memcpy(bytes.elems, &addr_.s_addr, 4);
+ return bytes;
+}
+
+ASIO_DECL
+unsigned long address_v4::to_ulong() const
+{
+ return asio::detail::socket_ops::network_to_host_long(addr_.s_addr);
+}
+
+ASIO_DECL
+std::string address_v4::to_string() const
+{
+ asio::error_code ec;
+ std::string addr = to_string(ec);
+ asio::detail::throw_error(ec);
+ return addr;
+}
+
+ASIO_DECL
+std::string address_v4::to_string(asio::error_code& ec) const
+{
+ char addr_str[asio::detail::max_addr_v4_str_len];
+ const char* addr =
+ asio::detail::socket_ops::inet_ntop(AF_INET, &addr_, addr_str,
+ asio::detail::max_addr_v4_str_len, 0, ec);
+ if (addr == 0)
+ return std::string();
+ return addr;
+}
+
+ASIO_DECL
+address_v4 address_v4::from_string(const char* str)
+{
+ asio::error_code ec;
+ address_v4 addr = from_string(str, ec);
+ asio::detail::throw_error(ec);
+ return addr;
+}
+
+ASIO_DECL
+address_v4 address_v4::from_string(
+ const char* str, asio::error_code& ec)
+{
+ address_v4 tmp;
+ if (asio::detail::socket_ops::inet_pton(
+ AF_INET, str, &tmp.addr_, 0, ec) <= 0)
+ return address_v4();
+ return tmp;
+}
+
+ASIO_DECL
+address_v4 address_v4::from_string(const std::string& str)
+{
+ return from_string(str.c_str());
+}
+
+ASIO_DECL
+address_v4 address_v4::from_string(
+ const std::string& str, asio::error_code& ec)
+{
+ return from_string(str.c_str(), ec);
+}
+
+ASIO_DECL
+bool address_v4::is_class_a() const
+{
+ return IN_CLASSA(to_ulong());
+}
+
+ASIO_DECL
+bool address_v4::is_class_b() const
+{
+ return IN_CLASSB(to_ulong());
+}
+
+ASIO_DECL
+bool address_v4::is_class_c() const
+{
+ return IN_CLASSC(to_ulong());
+}
+
+ASIO_DECL
+bool address_v4::is_multicast() const
+{
+ return IN_MULTICAST(to_ulong());
+}
+
+ASIO_DECL
+address_v4 address_v4::broadcast(const address_v4& addr, const address_v4& mask)
+{
+ return address_v4(addr.to_ulong() | (mask.to_ulong() ^ 0xFFFFFFFF));
+}
+
+ASIO_DECL
+address_v4 address_v4::netmask(const address_v4& addr)
+{
+ if (addr.is_class_a())
+ return address_v4(0xFF000000);
+ if (addr.is_class_b())
+ return address_v4(0xFFFF0000);
+ if (addr.is_class_c())
+ return address_v4(0xFFFFFF00);
+ return address_v4(0xFFFFFFFF);
+}
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_IMPL_ADDRESS_V4_IPP
diff --git a/ext/asio/asio/ip/impl/address_v6.hpp b/ext/asio/asio/ip/impl/address_v6.hpp
new file mode 100644
index 0000000..796a505
--- /dev/null
+++ b/ext/asio/asio/ip/impl/address_v6.hpp
@@ -0,0 +1,53 @@
+//
+// ip/impl/address_v6.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_IMPL_ADDRESS_V6_HPP
+#define ASIO_IP_IMPL_ADDRESS_V6_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#if !defined(BOOST_NO_IOSTREAM)
+
+#include "asio/detail/throw_error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace ip {
+
+template <typename Elem, typename Traits>
+std::basic_ostream<Elem, Traits>& operator<<(
+ std::basic_ostream<Elem, Traits>& os, const address_v6& addr)
+{
+ asio::error_code ec;
+ std::string s = addr.to_string(ec);
+ if (ec)
+ {
+ if (os.exceptions() & std::basic_ostream<Elem, Traits>::failbit)
+ asio::detail::throw_error(ec);
+ else
+ os.setstate(std::basic_ostream<Elem, Traits>::failbit);
+ }
+ else
+ for (std::string::iterator i = s.begin(); i != s.end(); ++i)
+ os << os.widen(*i);
+ return os;
+}
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // !defined(BOOST_NO_IOSTREAM)
+
+#endif // ASIO_IP_IMPL_ADDRESS_V6_HPP
diff --git a/ext/asio/asio/ip/impl/address_v6.ipp b/ext/asio/asio/ip/impl/address_v6.ipp
new file mode 100644
index 0000000..17b6382
--- /dev/null
+++ b/ext/asio/asio/ip/impl/address_v6.ipp
@@ -0,0 +1,313 @@
+//
+// ip/impl/address_v6.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_IMPL_ADDRESS_V6_IPP
+#define ASIO_IP_IMPL_ADDRESS_V6_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <cstring>
+#include <stdexcept>
+#include <typeinfo>
+#include <boost/throw_exception.hpp>
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+#include "asio/ip/address_v6.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace ip {
+
+ASIO_DECL
+address_v6::address_v6()
+ : scope_id_(0)
+{
+ asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT;
+ addr_ = tmp_addr;
+}
+
+ASIO_DECL
+address_v6::address_v6(const address_v6::bytes_type& bytes,
+ unsigned long scope_id)
+ : scope_id_(scope_id)
+{
+#if UCHAR_MAX > 0xFF
+ for (std::size_t i = 0; i < bytes.size(); ++i)
+ {
+ if (bytes[i] > 0xFF)
+ {
+ std::out_of_range ex("address_v6 from bytes_type");
+ boost::throw_exception(ex);
+ }
+ }
+#endif // UCHAR_MAX > 0xFF
+
+ using namespace std; // For memcpy.
+ memcpy(addr_.s6_addr, bytes.elems, 16);
+}
+
+ASIO_DECL
+address_v6::address_v6(const address_v6& other)
+ : addr_(other.addr_),
+ scope_id_(other.scope_id_)
+{
+}
+
+ASIO_DECL
+address_v6& address_v6::operator=(const address_v6& other)
+{
+ addr_ = other.addr_;
+ scope_id_ = other.scope_id_;
+ return *this;
+}
+
+ASIO_DECL
+address_v6::bytes_type address_v6::to_bytes() const
+{
+ using namespace std; // For memcpy.
+ bytes_type bytes;
+ memcpy(bytes.elems, addr_.s6_addr, 16);
+ return bytes;
+}
+
+ASIO_DECL
+std::string address_v6::to_string() const
+{
+ asio::error_code ec;
+ std::string addr = to_string(ec);
+ asio::detail::throw_error(ec);
+ return addr;
+}
+
+ASIO_DECL
+std::string address_v6::to_string(asio::error_code& ec) const
+{
+ char addr_str[asio::detail::max_addr_v6_str_len];
+ const char* addr =
+ asio::detail::socket_ops::inet_ntop(AF_INET6, &addr_, addr_str,
+ asio::detail::max_addr_v6_str_len, scope_id_, ec);
+ if (addr == 0)
+ return std::string();
+ return addr;
+}
+
+ASIO_DECL
+address_v6 address_v6::from_string(const char* str)
+{
+ asio::error_code ec;
+ address_v6 addr = from_string(str, ec);
+ asio::detail::throw_error(ec);
+ return addr;
+}
+
+ASIO_DECL
+address_v6 address_v6::from_string(
+ const char* str, asio::error_code& ec)
+{
+ address_v6 tmp;
+ if (asio::detail::socket_ops::inet_pton(
+ AF_INET6, str, &tmp.addr_, &tmp.scope_id_, ec) <= 0)
+ return address_v6();
+ return tmp;
+}
+
+ASIO_DECL
+address_v6 address_v6::from_string(const std::string& str)
+{
+ return from_string(str.c_str());
+}
+
+ASIO_DECL
+address_v6 address_v6::from_string(
+ const std::string& str, asio::error_code& ec)
+{
+ return from_string(str.c_str(), ec);
+}
+
+ASIO_DECL
+address_v4 address_v6::to_v4() const
+{
+ if (!is_v4_mapped() && !is_v4_compatible())
+ {
+ std::bad_cast ex;
+ boost::throw_exception(ex);
+ }
+
+ address_v4::bytes_type v4_bytes = { { addr_.s6_addr[12],
+ addr_.s6_addr[13], addr_.s6_addr[14], addr_.s6_addr[15] } };
+ return address_v4(v4_bytes);
+}
+
+ASIO_DECL
+bool address_v6::is_loopback() const
+{
+#if defined(__BORLANDC__)
+ return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0)
+ && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0)
+ && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0)
+ && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0)
+ && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0)
+ && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0)
+ && (addr_.s6_addr[12] == 0) && (addr_.s6_addr[13] == 0)
+ && (addr_.s6_addr[14] == 0) && (addr_.s6_addr[15] == 1));
+#else
+ using namespace asio::detail;
+ return IN6_IS_ADDR_LOOPBACK(&addr_) != 0;
+#endif
+}
+
+ASIO_DECL
+bool address_v6::is_unspecified() const
+{
+#if defined(__BORLANDC__)
+ return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0)
+ && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0)
+ && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0)
+ && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0)
+ && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0)
+ && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0)
+ && (addr_.s6_addr[12] == 0) && (addr_.s6_addr[13] == 0)
+ && (addr_.s6_addr[14] == 0) && (addr_.s6_addr[15] == 0));
+#else
+ using namespace asio::detail;
+ return IN6_IS_ADDR_UNSPECIFIED(&addr_) != 0;
+#endif
+}
+
+ASIO_DECL
+bool address_v6::is_link_local() const
+{
+ using namespace asio::detail;
+ return IN6_IS_ADDR_LINKLOCAL(&addr_) != 0;
+}
+
+ASIO_DECL
+bool address_v6::is_site_local() const
+{
+ using namespace asio::detail;
+ return IN6_IS_ADDR_SITELOCAL(&addr_) != 0;
+}
+
+ASIO_DECL
+bool address_v6::is_v4_mapped() const
+{
+ using namespace asio::detail;
+ return IN6_IS_ADDR_V4MAPPED(&addr_) != 0;
+}
+
+ASIO_DECL
+bool address_v6::is_v4_compatible() const
+{
+ using namespace asio::detail;
+ return IN6_IS_ADDR_V4COMPAT(&addr_) != 0;
+}
+
+ASIO_DECL
+bool address_v6::is_multicast() const
+{
+ using namespace asio::detail;
+ return IN6_IS_ADDR_MULTICAST(&addr_) != 0;
+}
+
+ASIO_DECL
+bool address_v6::is_multicast_global() const
+{
+ using namespace asio::detail;
+ return IN6_IS_ADDR_MC_GLOBAL(&addr_) != 0;
+}
+
+ASIO_DECL
+bool address_v6::is_multicast_link_local() const
+{
+ using namespace asio::detail;
+ return IN6_IS_ADDR_MC_LINKLOCAL(&addr_) != 0;
+}
+
+ASIO_DECL
+bool address_v6::is_multicast_node_local() const
+{
+ using namespace asio::detail;
+ return IN6_IS_ADDR_MC_NODELOCAL(&addr_) != 0;
+}
+
+ASIO_DECL
+bool address_v6::is_multicast_org_local() const
+{
+ using namespace asio::detail;
+ return IN6_IS_ADDR_MC_ORGLOCAL(&addr_) != 0;
+}
+
+ASIO_DECL
+bool address_v6::is_multicast_site_local() const
+{
+ using namespace asio::detail;
+ return IN6_IS_ADDR_MC_SITELOCAL(&addr_) != 0;
+}
+
+ASIO_DECL
+bool operator==(const address_v6& a1, const address_v6& a2)
+{
+ using namespace std; // For memcmp.
+ return memcmp(&a1.addr_, &a2.addr_,
+ sizeof(asio::detail::in6_addr_type)) == 0
+ && a1.scope_id_ == a2.scope_id_;
+}
+
+ASIO_DECL
+bool operator<(const address_v6& a1, const address_v6& a2)
+{
+ using namespace std; // For memcmp.
+ int memcmp_result = memcmp(&a1.addr_, &a2.addr_,
+ sizeof(asio::detail::in6_addr_type));
+ if (memcmp_result < 0)
+ return true;
+ if (memcmp_result > 0)
+ return false;
+ return a1.scope_id_ < a2.scope_id_;
+}
+
+ASIO_DECL
+address_v6 address_v6::loopback()
+{
+ address_v6 tmp;
+ asio::detail::in6_addr_type tmp_addr = IN6ADDR_LOOPBACK_INIT;
+ tmp.addr_ = tmp_addr;
+ return tmp;
+}
+
+ASIO_DECL
+address_v6 address_v6::v4_mapped(const address_v4& addr)
+{
+ address_v4::bytes_type v4_bytes = addr.to_bytes();
+ bytes_type v6_bytes = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF,
+ v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] } };
+ return address_v6(v6_bytes);
+}
+
+ASIO_DECL
+address_v6 address_v6::v4_compatible(const address_v4& addr)
+{
+ address_v4::bytes_type v4_bytes = addr.to_bytes();
+ bytes_type v6_bytes = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] } };
+ return address_v6(v6_bytes);
+}
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_IMPL_ADDRESS_V6_IPP
diff --git a/ext/asio/asio/ip/impl/basic_endpoint.hpp b/ext/asio/asio/ip/impl/basic_endpoint.hpp
new file mode 100644
index 0000000..db7d5fb
--- /dev/null
+++ b/ext/asio/asio/ip/impl/basic_endpoint.hpp
@@ -0,0 +1,55 @@
+//
+// ip/impl/basic_endpoint.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_IMPL_BASIC_ENDPOINT_HPP
+#define ASIO_IP_IMPL_BASIC_ENDPOINT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#if !defined(BOOST_NO_IOSTREAM)
+
+#include "asio/detail/throw_error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace ip {
+
+template <typename Elem, typename Traits, typename InternetProtocol>
+std::basic_ostream<Elem, Traits>& operator<<(
+ std::basic_ostream<Elem, Traits>& os,
+ const basic_endpoint<InternetProtocol>& endpoint)
+{
+ asio::ip::detail::endpoint tmp_ep(endpoint.address(), endpoint.port());
+ asio::error_code ec;
+ std::string s = tmp_ep.to_string(ec);
+ if (ec)
+ {
+ if (os.exceptions() & std::basic_ostream<Elem, Traits>::failbit)
+ asio::detail::throw_error(ec);
+ else
+ os.setstate(std::basic_ostream<Elem, Traits>::failbit);
+ }
+ else
+ for (std::string::iterator i = s.begin(); i != s.end(); ++i)
+ os << os.widen(*i);
+ return os;
+}
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // !defined(BOOST_NO_IOSTREAM)
+
+#endif // ASIO_IP_IMPL_BASIC_ENDPOINT_HPP
diff --git a/ext/asio/asio/ip/impl/host_name.ipp b/ext/asio/asio/ip/impl/host_name.ipp
new file mode 100644
index 0000000..5bf5cca
--- /dev/null
+++ b/ext/asio/asio/ip/impl/host_name.ipp
@@ -0,0 +1,56 @@
+//
+// ip/impl/host_name.ipp
+// ~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_IP_IMPL_HOST_NAME_IPP
+#define ASIO_IP_IMPL_HOST_NAME_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/detail/winsock_init.hpp"
+#include "asio/ip/host_name.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace ip {
+
+ASIO_DECL
+std::string host_name()
+{
+ char name[1024];
+ asio::error_code ec;
+ if (asio::detail::socket_ops::gethostname(name, sizeof(name), ec) != 0)
+ {
+ asio::detail::throw_error(ec);
+ return std::string();
+ }
+ return std::string(name);
+}
+
+ASIO_DECL
+std::string host_name(asio::error_code& ec)
+{
+ char name[1024];
+ if (asio::detail::socket_ops::gethostname(name, sizeof(name), ec) != 0)
+ return std::string();
+ return std::string(name);
+}
+
+} // namespace ip
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_IP_IMPL_HOST_NAME_IPP
diff --git a/ext/asio/asio/ip/multicast.hpp b/ext/asio/asio/ip/multicast.hpp
index eeedc6c..f2193c0 100644
--- a/ext/asio/asio/ip/multicast.hpp
+++ b/ext/asio/asio/ip/multicast.hpp
@@ -1,8 +1,8 @@
//
-// multicast.hpp
-// ~~~~~~~~~~~~~
+// ip/multicast.hpp
+// ~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,15 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/ip/detail/socket_option.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace ip {
namespace multicast {
diff --git a/ext/asio/asio/ip/resolver_query_base.hpp b/ext/asio/asio/ip/resolver_query_base.hpp
index 5a0acea..8963f41 100644
--- a/ext/asio/asio/ip/resolver_query_base.hpp
+++ b/ext/asio/asio/ip/resolver_query_base.hpp
@@ -1,8 +1,8 @@
//
-// resolver_query_base.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~
+// ip/resolver_query_base.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,15 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
+#include "asio/detail/config.hpp"
#include <boost/detail/workaround.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/detail/socket_types.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace ip {
diff --git a/ext/asio/asio/ip/resolver_service.hpp b/ext/asio/asio/ip/resolver_service.hpp
index 75f6b2c..0d866d6 100644
--- a/ext/asio/asio/ip/resolver_service.hpp
+++ b/ext/asio/asio/ip/resolver_service.hpp
@@ -1,8 +1,8 @@
//
-// resolver_service.hpp
-// ~~~~~~~~~~~~~~~~~~~~
+// ip/resolver_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,14 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/error.hpp"
+#include "asio/detail/config.hpp"
+#include "asio/error_code.hpp"
+#include "asio/detail/resolver_service.hpp"
#include "asio/io_service.hpp"
#include "asio/ip/basic_resolver_iterator.hpp"
#include "asio/ip/basic_resolver_query.hpp"
-#include "asio/detail/resolver_service.hpp"
-#include "asio/detail/service_base.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace ip {
@@ -72,13 +72,14 @@ public:
explicit resolver_service(asio::io_service& io_service)
: asio::detail::service_base<
resolver_service<InternetProtocol> >(io_service),
- service_impl_(asio::use_service<service_impl_type>(io_service))
+ service_impl_(io_service)
{
}
/// Destroy all user-defined handler objects owned by the service.
void shutdown_service()
{
+ service_impl_.shutdown_service();
}
/// Construct a new resolver implementation.
@@ -130,8 +131,8 @@ public:
}
private:
- // The service that provides the platform-specific implementation.
- service_impl_type& service_impl_;
+ // The platform-specific implementation.
+ service_impl_type service_impl_;
};
} // namespace ip
diff --git a/ext/asio/asio/ip/tcp.hpp b/ext/asio/asio/ip/tcp.hpp
index a2e9ac9..67fb09f 100644
--- a/ext/asio/asio/ip/tcp.hpp
+++ b/ext/asio/asio/ip/tcp.hpp
@@ -1,8 +1,8 @@
//
-// tcp.hpp
-// ~~~~~~~
+// ip/tcp.hpp
+// ~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,18 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/basic_socket_acceptor.hpp"
#include "asio/basic_socket_iostream.hpp"
#include "asio/basic_stream_socket.hpp"
+#include "asio/detail/socket_option.hpp"
+#include "asio/detail/socket_types.hpp"
#include "asio/ip/basic_endpoint.hpp"
#include "asio/ip/basic_resolver.hpp"
#include "asio/ip/basic_resolver_iterator.hpp"
#include "asio/ip/basic_resolver_query.hpp"
-#include "asio/detail/socket_option.hpp"
-#include "asio/detail/socket_types.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace ip {
diff --git a/ext/asio/asio/ip/udp.hpp b/ext/asio/asio/ip/udp.hpp
index fb26118..ca3ac78 100644
--- a/ext/asio/asio/ip/udp.hpp
+++ b/ext/asio/asio/ip/udp.hpp
@@ -1,8 +1,8 @@
//
-// udp.hpp
-// ~~~~~~~
+// ip/udp.hpp
+// ~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,14 +15,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/basic_datagram_socket.hpp"
+#include "asio/detail/socket_types.hpp"
#include "asio/ip/basic_endpoint.hpp"
#include "asio/ip/basic_resolver.hpp"
#include "asio/ip/basic_resolver_iterator.hpp"
#include "asio/ip/basic_resolver_query.hpp"
-#include "asio/detail/socket_types.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace ip {
diff --git a/ext/asio/asio/ip/unicast.hpp b/ext/asio/asio/ip/unicast.hpp
index 46d7239..e3d04e5 100644
--- a/ext/asio/asio/ip/unicast.hpp
+++ b/ext/asio/asio/ip/unicast.hpp
@@ -1,8 +1,8 @@
//
-// unicast.hpp
-// ~~~~~~~~~~~
+// ip/unicast.hpp
+// ~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,15 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/ip/detail/socket_option.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace ip {
namespace unicast {
diff --git a/ext/asio/asio/ip/v6_only.hpp b/ext/asio/asio/ip/v6_only.hpp
index 928caff..5ac6a37 100644
--- a/ext/asio/asio/ip/v6_only.hpp
+++ b/ext/asio/asio/ip/v6_only.hpp
@@ -1,8 +1,8 @@
//
-// v6_only.hpp
-// ~~~~~~~~~~~
+// ip/v6_only.hpp
+// ~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,10 +15,11 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/detail/socket_option.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace ip {
diff --git a/ext/asio/asio/is_read_buffered.hpp b/ext/asio/asio/is_read_buffered.hpp
index 8d97174..e48de55 100644
--- a/ext/asio/asio/is_read_buffered.hpp
+++ b/ext/asio/asio/is_read_buffered.hpp
@@ -2,7 +2,7 @@
// is_read_buffered.hpp
// ~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,15 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/buffered_read_stream_fwd.hpp"
#include "asio/buffered_stream_fwd.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
diff --git a/ext/asio/asio/is_write_buffered.hpp b/ext/asio/asio/is_write_buffered.hpp
index 5d16b1c..c681a2b 100644
--- a/ext/asio/asio/is_write_buffered.hpp
+++ b/ext/asio/asio/is_write_buffered.hpp
@@ -2,7 +2,7 @@
// is_write_buffered.hpp
// ~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,15 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/buffered_stream_fwd.hpp"
#include "asio/buffered_write_stream_fwd.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail {
diff --git a/ext/asio/asio/local/basic_endpoint.hpp b/ext/asio/asio/local/basic_endpoint.hpp
index 81e6a7e..d2d0a02 100644
--- a/ext/asio/asio/local/basic_endpoint.hpp
+++ b/ext/asio/asio/local/basic_endpoint.hpp
@@ -1,8 +1,8 @@
//
-// basic_endpoint.hpp
-// ~~~~~~~~~~~~~~~~~~
+// local/basic_endpoint.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
// Derived from a public domain implementation written by Daniel Casimiro.
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
@@ -16,30 +16,18 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/throw_exception.hpp>
-#include <cstddef>
-#include <cstring>
-#include <ostream>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error.hpp"
-#include "asio/system_error.hpp"
-#include "asio/detail/socket_ops.hpp"
-#include "asio/detail/socket_types.hpp"
-#include "asio/detail/throw_error.hpp"
-
-#if !defined(ASIO_DISABLE_LOCAL_SOCKETS)
-# if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
-# define ASIO_HAS_LOCAL_SOCKETS 1
-# endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
-#endif // !defined(ASIO_DISABLE_LOCAL_SOCKETS)
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_LOCAL_SOCKETS) \
|| defined(GENERATING_DOCUMENTATION)
+#include "asio/local/detail/endpoint.hpp"
+
+#if !defined(BOOST_NO_IOSTREAM)
+# include <iosfwd>
+#endif // !defined(BOOST_NO_IOSTREAM)
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace local {
@@ -74,34 +62,30 @@ public:
/// Default constructor.
basic_endpoint()
{
- init("", 0);
}
/// Construct an endpoint using the specified path name.
basic_endpoint(const char* path)
+ : impl_(path)
{
- using namespace std; // For strlen.
- init(path, strlen(path));
}
/// Construct an endpoint using the specified path name.
basic_endpoint(const std::string& path)
+ : impl_(path)
{
- init(path.data(), path.length());
}
/// Copy constructor.
basic_endpoint(const basic_endpoint& other)
- : data_(other.data_),
- path_length_(other.path_length_)
+ : impl_(other.impl_)
{
}
/// Assign from another endpoint.
basic_endpoint& operator=(const basic_endpoint& other)
{
- data_ = other.data_;
- path_length_ = other.path_length_;
+ impl_ = other.impl_;
return *this;
}
@@ -114,123 +98,96 @@ public:
/// Get the underlying endpoint in the native type.
data_type* data()
{
- return &data_.base;
+ return impl_.data();
}
/// Get the underlying endpoint in the native type.
const data_type* data() const
{
- return &data_.base;
+ return impl_.data();
}
/// Get the underlying size of the endpoint in the native type.
std::size_t size() const
{
- return path_length_
- + offsetof(asio::detail::sockaddr_un_type, sun_path);
+ return impl_.size();
}
/// Set the underlying size of the endpoint in the native type.
void resize(std::size_t size)
{
- if (size > sizeof(asio::detail::sockaddr_un_type))
- {
- asio::system_error e(asio::error::invalid_argument);
- boost::throw_exception(e);
- }
- else if (size == 0)
- {
- path_length_ = 0;
- }
- else
- {
- path_length_ = size
- - offsetof(asio::detail::sockaddr_un_type, sun_path);
-
- // The path returned by the operating system may be NUL-terminated.
- if (path_length_ > 0 && data_.local.sun_path[path_length_ - 1] == 0)
- --path_length_;
- }
+ impl_.resize(size);
}
/// Get the capacity of the endpoint in the native type.
std::size_t capacity() const
{
- return sizeof(asio::detail::sockaddr_un_type);
+ return impl_.capacity();
}
/// Get the path associated with the endpoint.
std::string path() const
{
- return std::string(data_.local.sun_path, path_length_);
+ return impl_.path();
}
/// Set the path associated with the endpoint.
void path(const char* p)
{
- using namespace std; // For strlen.
- init(p, strlen(p));
+ impl_.path(p);
}
/// Set the path associated with the endpoint.
void path(const std::string& p)
{
- init(p.data(), p.length());
+ impl_.path(p);
}
/// Compare two endpoints for equality.
friend bool operator==(const basic_endpoint<Protocol>& e1,
const basic_endpoint<Protocol>& e2)
{
- return e1.path() == e2.path();
+ return e1.impl_ == e2.impl_;
}
/// Compare two endpoints for inequality.
friend bool operator!=(const basic_endpoint<Protocol>& e1,
const basic_endpoint<Protocol>& e2)
{
- return e1.path() != e2.path();
+ return !(e1.impl_ == e2.impl_);
}
/// Compare endpoints for ordering.
friend bool operator<(const basic_endpoint<Protocol>& e1,
const basic_endpoint<Protocol>& e2)
{
- return e1.path() < e2.path();
+ return e1.impl_ < e2.impl_;
}
-private:
- // The underlying UNIX socket address.
- union data_union
+ /// Compare endpoints for ordering.
+ friend bool operator>(const basic_endpoint<Protocol>& e1,
+ const basic_endpoint<Protocol>& e2)
{
- asio::detail::socket_addr_type base;
- asio::detail::sockaddr_un_type local;
- } data_;
+ return e2.impl_ < e1.impl_;
+ }
- // The length of the path associated with the endpoint.
- std::size_t path_length_;
+ /// Compare endpoints for ordering.
+ friend bool operator<=(const basic_endpoint<Protocol>& e1,
+ const basic_endpoint<Protocol>& e2)
+ {
+ return !(e2 < e1);
+ }
- // Initialise with a specified path.
- void init(const char* path, std::size_t path_length)
+ /// Compare endpoints for ordering.
+ friend bool operator>=(const basic_endpoint<Protocol>& e1,
+ const basic_endpoint<Protocol>& e2)
{
- if (path_length > sizeof(data_.local.sun_path) - 1)
- {
- // The buffer is not large enough to store this address.
- asio::error_code ec(asio::error::name_too_long);
- asio::detail::throw_error(ec);
- }
-
- using namespace std; // For memcpy.
- data_.local = asio::detail::sockaddr_un_type();
- data_.local.sun_family = AF_UNIX;
- memcpy(data_.local.sun_path, path, path_length);
- path_length_ = path_length;
-
- // NUL-terminate normal path names. Names that start with a NUL are in the
- // UNIX domain protocol's "abstract namespace" and are not NUL-terminated.
- if (path_length > 0 && data_.local.sun_path[0] == 0)
- data_.local.sun_path[path_length] = 0;
+ return !(e1 < e2);
}
+
+private:
+ // The underlying UNIX domain endpoint.
+ asio::local::detail::endpoint impl_;
};
/// Output an endpoint as a string.
@@ -257,9 +214,9 @@ std::basic_ostream<Elem, Traits>& operator<<(
} // namespace local
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#endif // defined(ASIO_HAS_LOCAL_SOCKETS)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_LOCAL_BASIC_ENDPOINT_HPP
diff --git a/ext/asio/asio/local/connect_pair.hpp b/ext/asio/asio/local/connect_pair.hpp
index da1d4fc..4a58247 100644
--- a/ext/asio/asio/local/connect_pair.hpp
+++ b/ext/asio/asio/local/connect_pair.hpp
@@ -1,8 +1,8 @@
//
-// connect_pair.hpp
-// ~~~~~~~~~~~~~~~~
+// local/connect_pair.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,18 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_LOCAL_SOCKETS) \
+ || defined(GENERATING_DOCUMENTATION)
#include "asio/basic_socket.hpp"
-#include "asio/error.hpp"
-#include "asio/local/basic_endpoint.hpp"
#include "asio/detail/socket_ops.hpp"
#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+#include "asio/local/basic_endpoint.hpp"
-#if defined(ASIO_HAS_LOCAL_SOCKETS) \
- || defined(GENERATING_DOCUMENTATION)
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace local {
@@ -73,8 +75,9 @@ inline asio::error_code connect_pair(
if (socket1.assign(protocol, sv[0], ec))
{
asio::error_code temp_ec;
- asio::detail::socket_ops::close(sv[0], temp_ec);
- asio::detail::socket_ops::close(sv[1], temp_ec);
+ asio::detail::socket_ops::state_type state[2] = { 0, 0 };
+ asio::detail::socket_ops::close(sv[0], state[0], true, temp_ec);
+ asio::detail::socket_ops::close(sv[1], state[1], true, temp_ec);
return ec;
}
@@ -82,7 +85,8 @@ inline asio::error_code connect_pair(
{
asio::error_code temp_ec;
socket1.close(temp_ec);
- asio::detail::socket_ops::close(sv[1], temp_ec);
+ asio::detail::socket_ops::state_type state = 0;
+ asio::detail::socket_ops::close(sv[1], state, true, temp_ec);
return ec;
}
@@ -92,9 +96,9 @@ inline asio::error_code connect_pair(
} // namespace local
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#endif // defined(ASIO_HAS_LOCAL_SOCKETS)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_LOCAL_CONNECT_PAIR_HPP
diff --git a/ext/asio/asio/local/datagram_protocol.hpp b/ext/asio/asio/local/datagram_protocol.hpp
index 0340180..39f47ce 100644
--- a/ext/asio/asio/local/datagram_protocol.hpp
+++ b/ext/asio/asio/local/datagram_protocol.hpp
@@ -1,8 +1,8 @@
//
-// datagram_protocol.hpp
-// ~~~~~~~~~~~~~~~~~~~~~
+// local/datagram_protocol.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,14 +15,16 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_LOCAL_SOCKETS) \
+ || defined(GENERATING_DOCUMENTATION)
#include "asio/basic_datagram_socket.hpp"
-#include "asio/local/basic_endpoint.hpp"
#include "asio/detail/socket_types.hpp"
+#include "asio/local/basic_endpoint.hpp"
-#if defined(ASIO_HAS_LOCAL_SOCKETS) \
- || defined(GENERATING_DOCUMENTATION)
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace local {
@@ -70,9 +72,9 @@ public:
} // namespace local
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#endif // defined(ASIO_HAS_LOCAL_SOCKETS)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_LOCAL_DATAGRAM_PROTOCOL_HPP
diff --git a/ext/asio/asio/local/detail/endpoint.hpp b/ext/asio/asio/local/detail/endpoint.hpp
new file mode 100644
index 0000000..05fc992
--- /dev/null
+++ b/ext/asio/asio/local/detail/endpoint.hpp
@@ -0,0 +1,133 @@
+//
+// local/detail/endpoint.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Derived from a public domain implementation written by Daniel Casimiro.
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_LOCAL_DETAIL_ENDPOINT_HPP
+#define ASIO_LOCAL_DETAIL_ENDPOINT_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_LOCAL_SOCKETS)
+
+#include <cstddef>
+#include <string>
+#include "asio/detail/socket_types.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace local {
+namespace detail {
+
+// Helper class for implementing a UNIX domain endpoint.
+class endpoint
+{
+public:
+ // Default constructor.
+ ASIO_DECL endpoint();
+
+ // Construct an endpoint using the specified path name.
+ ASIO_DECL endpoint(const char* path);
+
+ // Construct an endpoint using the specified path name.
+ ASIO_DECL endpoint(const std::string& path);
+
+ // Copy constructor.
+ endpoint(const endpoint& other)
+ : data_(other.data_),
+ path_length_(other.path_length_)
+ {
+ }
+
+ // Assign from another endpoint.
+ endpoint& operator=(const endpoint& other)
+ {
+ data_ = other.data_;
+ path_length_ = other.path_length_;
+ return *this;
+ }
+
+ // Get the underlying endpoint in the native type.
+ asio::detail::socket_addr_type* data()
+ {
+ return &data_.base;
+ }
+
+ // Get the underlying endpoint in the native type.
+ const asio::detail::socket_addr_type* data() const
+ {
+ return &data_.base;
+ }
+
+ // Get the underlying size of the endpoint in the native type.
+ std::size_t size() const
+ {
+ return path_length_
+ + offsetof(asio::detail::sockaddr_un_type, sun_path);
+ }
+
+ // Set the underlying size of the endpoint in the native type.
+ ASIO_DECL void resize(std::size_t size);
+
+ // Get the capacity of the endpoint in the native type.
+ std::size_t capacity() const
+ {
+ return sizeof(asio::detail::sockaddr_un_type);
+ }
+
+ // Get the path associated with the endpoint.
+ ASIO_DECL std::string path() const;
+
+ // Set the path associated with the endpoint.
+ ASIO_DECL void path(const char* p);
+
+ // Set the path associated with the endpoint.
+ ASIO_DECL void path(const std::string& p);
+
+ // Compare two endpoints for equality.
+ ASIO_DECL friend bool operator==(
+ const endpoint& e1, const endpoint& e2);
+
+ // Compare endpoints for ordering.
+ ASIO_DECL friend bool operator<(
+ const endpoint& e1, const endpoint& e2);
+
+private:
+ // The underlying UNIX socket address.
+ union data_union
+ {
+ asio::detail::socket_addr_type base;
+ asio::detail::sockaddr_un_type local;
+ } data_;
+
+ // The length of the path associated with the endpoint.
+ std::size_t path_length_;
+
+ // Initialise with a specified path.
+ ASIO_DECL void init(const char* path, std::size_t path_length);
+};
+
+} // namespace detail
+} // namespace local
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/local/detail/impl/endpoint.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // defined(ASIO_HAS_LOCAL_SOCKETS)
+
+#endif // ASIO_LOCAL_DETAIL_ENDPOINT_HPP
diff --git a/ext/asio/asio/local/detail/impl/endpoint.ipp b/ext/asio/asio/local/detail/impl/endpoint.ipp
new file mode 100644
index 0000000..1bc2c94
--- /dev/null
+++ b/ext/asio/asio/local/detail/impl/endpoint.ipp
@@ -0,0 +1,138 @@
+//
+// local/detail/impl/endpoint.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Derived from a public domain implementation written by Daniel Casimiro.
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ASIO_LOCAL_DETAIL_IMPL_ENDPOINT_IPP
+#define ASIO_LOCAL_DETAIL_IMPL_ENDPOINT_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_LOCAL_SOCKETS)
+
+#include <cstring>
+#include "asio/detail/socket_ops.hpp"
+#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+#include "asio/local/detail/endpoint.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace local {
+namespace detail {
+
+ASIO_DECL
+endpoint::endpoint()
+{
+ init("", 0);
+}
+
+ASIO_DECL
+endpoint::endpoint(const char* path)
+{
+ using namespace std; // For strlen.
+ init(path, strlen(path));
+}
+
+ASIO_DECL
+endpoint::endpoint(const std::string& path)
+{
+ init(path.data(), path.length());
+}
+
+ASIO_DECL
+void endpoint::resize(std::size_t size)
+{
+ if (size > sizeof(asio::detail::sockaddr_un_type))
+ {
+ asio::error_code ec(asio::error::invalid_argument);
+ asio::detail::throw_error(ec);
+ }
+ else if (size == 0)
+ {
+ path_length_ = 0;
+ }
+ else
+ {
+ path_length_ = size
+ - offsetof(asio::detail::sockaddr_un_type, sun_path);
+
+ // The path returned by the operating system may be NUL-terminated.
+ if (path_length_ > 0 && data_.local.sun_path[path_length_ - 1] == 0)
+ --path_length_;
+ }
+}
+
+ASIO_DECL
+std::string endpoint::path() const
+{
+ return std::string(data_.local.sun_path, path_length_);
+}
+
+ASIO_DECL
+void endpoint::path(const char* p)
+{
+ using namespace std; // For strlen.
+ init(p, strlen(p));
+}
+
+ASIO_DECL
+void endpoint::path(const std::string& p)
+{
+ init(p.data(), p.length());
+}
+
+ASIO_DECL
+bool operator==(const endpoint& e1, const endpoint& e2)
+{
+ return e1.path() == e2.path();
+}
+
+ASIO_DECL
+bool operator<(const endpoint& e1, const endpoint& e2)
+{
+ return e1.path() < e2.path();
+}
+
+ASIO_DECL
+void endpoint::init(const char* path, std::size_t path_length)
+{
+ if (path_length > sizeof(data_.local.sun_path) - 1)
+ {
+ // The buffer is not large enough to store this address.
+ asio::error_code ec(asio::error::name_too_long);
+ asio::detail::throw_error(ec);
+ }
+
+ using namespace std; // For memcpy.
+ data_.local = asio::detail::sockaddr_un_type();
+ data_.local.sun_family = AF_UNIX;
+ memcpy(data_.local.sun_path, path, path_length);
+ path_length_ = path_length;
+
+ // NUL-terminate normal path names. Names that start with a NUL are in the
+ // UNIX domain protocol's "abstract namespace" and are not NUL-terminated.
+ if (path_length > 0 && data_.local.sun_path[0] == 0)
+ data_.local.sun_path[path_length] = 0;
+}
+
+} // namespace detail
+} // namespace local
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // defined(ASIO_HAS_LOCAL_SOCKETS)
+
+#endif // ASIO_LOCAL_DETAIL_IMPL_ENDPOINT_IPP
diff --git a/ext/asio/asio/local/stream_protocol.hpp b/ext/asio/asio/local/stream_protocol.hpp
index 47fe42f..e65a2f6 100644
--- a/ext/asio/asio/local/stream_protocol.hpp
+++ b/ext/asio/asio/local/stream_protocol.hpp
@@ -1,8 +1,8 @@
//
-// stream_protocol.hpp
-// ~~~~~~~~~~~~~~~~~~~
+// local/stream_protocol.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,18 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_LOCAL_SOCKETS) \
+ || defined(GENERATING_DOCUMENTATION)
#include "asio/basic_socket_acceptor.hpp"
#include "asio/basic_socket_iostream.hpp"
#include "asio/basic_stream_socket.hpp"
-#include "asio/local/basic_endpoint.hpp"
#include "asio/detail/socket_types.hpp"
+#include "asio/local/basic_endpoint.hpp"
-#if defined(ASIO_HAS_LOCAL_SOCKETS) \
- || defined(GENERATING_DOCUMENTATION)
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace local {
@@ -80,9 +82,9 @@ public:
} // namespace local
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#endif // defined(ASIO_HAS_LOCAL_SOCKETS)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_LOCAL_STREAM_PROTOCOL_HPP
diff --git a/ext/asio/asio/placeholders.hpp b/ext/asio/asio/placeholders.hpp
index 70e69fc..6fda487 100644
--- a/ext/asio/asio/placeholders.hpp
+++ b/ext/asio/asio/placeholders.hpp
@@ -2,7 +2,7 @@
// placeholders.hpp
// ~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,12 +15,11 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <boost/bind/arg.hpp>
#include <boost/detail/workaround.hpp>
-#include "asio/detail/pop_options.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace placeholders {
diff --git a/ext/asio/asio/posix/basic_descriptor.hpp b/ext/asio/asio/posix/basic_descriptor.hpp
index 37bcc94..591e65a 100644
--- a/ext/asio/asio/posix/basic_descriptor.hpp
+++ b/ext/asio/asio/posix/basic_descriptor.hpp
@@ -1,8 +1,8 @@
//
-// basic_descriptor.hpp
-// ~~~~~~~~~~~~~~~~~~~~
+// posix/basic_descriptor.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,17 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \
+ || defined(GENERATING_DOCUMENTATION)
#include "asio/basic_io_object.hpp"
+#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/posix/descriptor_base.hpp"
-#include "asio/detail/throw_error.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace posix {
@@ -291,4 +292,7 @@ protected:
#include "asio/detail/pop_options.hpp"
+#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR)
+ // || defined(GENERATING_DOCUMENTATION)
+
#endif // ASIO_POSIX_BASIC_DESCRIPTOR_HPP
diff --git a/ext/asio/asio/posix/basic_stream_descriptor.hpp b/ext/asio/asio/posix/basic_stream_descriptor.hpp
index 21e2287..8211c58 100644
--- a/ext/asio/asio/posix/basic_stream_descriptor.hpp
+++ b/ext/asio/asio/posix/basic_stream_descriptor.hpp
@@ -1,8 +1,8 @@
//
-// basic_stream_descriptor.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// posix/basic_stream_descriptor.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,20 +15,18 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
-#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \
+ || defined(GENERATING_DOCUMENTATION)
+#include <cstddef>
+#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/posix/basic_descriptor.hpp"
#include "asio/posix/stream_descriptor_service.hpp"
-#include "asio/detail/throw_error.hpp"
-#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \
- || defined(GENERATING_DOCUMENTATION)
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace posix {
@@ -296,9 +294,9 @@ public:
} // namespace posix
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_POSIX_BASIC_STREAM_DESCRIPTOR_HPP
diff --git a/ext/asio/asio/posix/descriptor_base.hpp b/ext/asio/asio/posix/descriptor_base.hpp
index 29e1746..2222915 100644
--- a/ext/asio/asio/posix/descriptor_base.hpp
+++ b/ext/asio/asio/posix/descriptor_base.hpp
@@ -1,8 +1,8 @@
//
-// descriptor_base.hpp
-// ~~~~~~~~~~~~~~~~~~~
+// posix/descriptor_base.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,16 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include <boost/detail/workaround.hpp>
-#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \
+ || defined(GENERATING_DOCUMENTATION)
#include "asio/detail/io_control.hpp"
#include "asio/detail/socket_option.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace posix {
@@ -90,4 +90,7 @@ protected:
#include "asio/detail/pop_options.hpp"
+#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR)
+ // || defined(GENERATING_DOCUMENTATION)
+
#endif // ASIO_POSIX_DESCRIPTOR_BASE_HPP
diff --git a/ext/asio/asio/posix/stream_descriptor.hpp b/ext/asio/asio/posix/stream_descriptor.hpp
index 72fbbed..48c20a3 100644
--- a/ext/asio/asio/posix/stream_descriptor.hpp
+++ b/ext/asio/asio/posix/stream_descriptor.hpp
@@ -1,8 +1,8 @@
//
-// stream_descriptor.hpp
-// ~~~~~~~~~~~~~~~~~~~~~
+// posix/stream_descriptor.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,13 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/posix/basic_stream_descriptor.hpp"
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \
|| defined(GENERATING_DOCUMENTATION)
+#include "asio/posix/basic_stream_descriptor.hpp"
+
namespace asio {
namespace posix {
@@ -34,6 +34,4 @@ typedef basic_stream_descriptor<> stream_descriptor;
#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_POSIX_STREAM_DESCRIPTOR_HPP
diff --git a/ext/asio/asio/posix/stream_descriptor_service.hpp b/ext/asio/asio/posix/stream_descriptor_service.hpp
index 61cee1b..4660e79 100644
--- a/ext/asio/asio/posix/stream_descriptor_service.hpp
+++ b/ext/asio/asio/posix/stream_descriptor_service.hpp
@@ -1,8 +1,8 @@
//
-// stream_descriptor_service.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// posix/stream_descriptor_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,28 +15,18 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/error.hpp"
-#include "asio/io_service.hpp"
-#include "asio/detail/service_base.hpp"
-
-#if !defined(ASIO_DISABLE_POSIX_STREAM_DESCRIPTOR)
-# if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
-# define ASIO_HAS_POSIX_STREAM_DESCRIPTOR 1
-# endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
-#endif // !defined(ASIO_DISABLE_POSIX_STREAM_DESCRIPTOR)
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \
|| defined(GENERATING_DOCUMENTATION)
+#include <cstddef>
+#include "asio/error.hpp"
+#include "asio/io_service.hpp"
#include "asio/detail/reactive_descriptor_service.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace posix {
@@ -179,9 +169,9 @@ private:
} // namespace posix
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_POSIX_STREAM_DESCRIPTOR_SERVICE_HPP
diff --git a/ext/asio/asio/raw_socket_service.hpp b/ext/asio/asio/raw_socket_service.hpp
index a8973d3..3d0016d 100644
--- a/ext/asio/asio/raw_socket_service.hpp
+++ b/ext/asio/asio/raw_socket_service.hpp
@@ -2,7 +2,7 @@
// raw_socket_service.hpp
// ~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,10 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/error.hpp"
#include "asio/io_service.hpp"
-#include "asio/detail/service_base.hpp"
#if defined(ASIO_HAS_IOCP)
# include "asio/detail/win_iocp_socket_service.hpp"
@@ -32,6 +26,8 @@
# include "asio/detail/reactive_socket_service.hpp"
#endif
+#include "asio/detail/push_options.hpp"
+
namespace asio {
/// Default service implementation for a raw socket.
diff --git a/ext/asio/asio/read.hpp b/ext/asio/asio/read.hpp
index 859c05a..7f04b07 100644
--- a/ext/asio/asio/read.hpp
+++ b/ext/asio/asio/read.hpp
@@ -2,7 +2,7 @@
// read.hpp
// ~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/basic_streambuf.hpp"
+#include "asio/basic_streambuf_fwd.hpp"
#include "asio/error.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
/**
@@ -536,8 +533,8 @@ void async_read(AsyncReadStream& s, basic_streambuf<Allocator>& b,
} // namespace asio
-#include "asio/impl/read.ipp"
-
#include "asio/detail/pop_options.hpp"
+#include "asio/impl/read.hpp"
+
#endif // ASIO_READ_HPP
diff --git a/ext/asio/asio/read_at.hpp b/ext/asio/asio/read_at.hpp
index 6bb3fe1..4be1f96 100644
--- a/ext/asio/asio/read_at.hpp
+++ b/ext/asio/asio/read_at.hpp
@@ -2,7 +2,7 @@
// read_at.hpp
// ~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
#include <boost/cstdint.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/basic_streambuf.hpp"
+#include "asio/basic_streambuf_fwd.hpp"
#include "asio/error.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
/**
@@ -569,8 +566,8 @@ void async_read_at(AsyncRandomAccessReadDevice& d,
} // namespace asio
-#include "asio/impl/read_at.ipp"
-
#include "asio/detail/pop_options.hpp"
+#include "asio/impl/read_at.hpp"
+
#endif // ASIO_READ_AT_HPP
diff --git a/ext/asio/asio/read_until.hpp b/ext/asio/asio/read_until.hpp
index 5df71ce..67fa066 100644
--- a/ext/asio/asio/read_until.hpp
+++ b/ext/asio/asio/read_until.hpp
@@ -2,7 +2,7 @@
// read_until.hpp
// ~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,27 +15,22 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/config.hpp"
#if !defined(BOOST_NO_IOSTREAM)
-#include "asio/detail/push_options.hpp"
#include <cstddef>
-#include <boost/regex.hpp>
#include <boost/type_traits/is_function.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/detail/workaround.hpp>
#include <string>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/basic_streambuf.hpp"
+#include "asio/detail/regex_fwd.hpp"
#include "asio/error.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace detail
@@ -914,10 +909,10 @@ void async_read_until(AsyncReadStream& s,
} // namespace asio
-#include "asio/impl/read_until.ipp"
+#include "asio/detail/pop_options.hpp"
+
+#include "asio/impl/read_until.hpp"
#endif // !defined(BOOST_NO_IOSTREAM)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_READ_UNTIL_HPP
diff --git a/ext/asio/asio/serial_port.hpp b/ext/asio/asio/serial_port.hpp
index a55a03a..4cc6fd4 100644
--- a/ext/asio/asio/serial_port.hpp
+++ b/ext/asio/asio/serial_port.hpp
@@ -2,7 +2,7 @@
// serial_port.hpp
// ~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
// Copyright (c) 2008 Rep Invariant Systems, Inc. (info at repinvariant.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
@@ -16,13 +16,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/basic_serial_port.hpp"
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_SERIAL_PORT) \
|| defined(GENERATING_DOCUMENTATION)
+#include "asio/basic_serial_port.hpp"
+
namespace asio {
/// Typedef for the typical usage of a serial port.
@@ -33,6 +33,4 @@ typedef basic_serial_port<> serial_port;
#endif // defined(ASIO_HAS_SERIAL_PORT)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_SERIAL_PORT_HPP
diff --git a/ext/asio/asio/serial_port_base.hpp b/ext/asio/asio/serial_port_base.hpp
index 28e51a0..9c7a773 100644
--- a/ext/asio/asio/serial_port_base.hpp
+++ b/ext/asio/asio/serial_port_base.hpp
@@ -2,7 +2,7 @@
// serial_port_base.hpp
// ~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
// Copyright (c) 2008 Rep Invariant Systems, Inc. (info at repinvariant.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
@@ -16,32 +16,18 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <stdexcept>
-#include <boost/config.hpp>
-#include <boost/detail/workaround.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#if !defined(ASIO_DISABLE_SERIAL_PORT)
-# if defined(ASIO_HAS_IOCP) \
- || !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
-# define ASIO_HAS_SERIAL_PORT 1
-# endif // defined(ASIO_HAS_IOCP)
-#endif // !defined(ASIO_DISABLE_STREAM_HANDLE)
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_SERIAL_PORT) \
|| defined(GENERATING_DOCUMENTATION)
#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
-# include "asio/detail/push_options.hpp"
# include <termios.h>
-# include "asio/detail/pop_options.hpp"
#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
-#include "asio/error_code.hpp"
+#include <boost/detail/workaround.hpp>
#include "asio/detail/socket_types.hpp"
+#include "asio/error_code.hpp"
#if defined(GENERATING_DOCUMENTATION)
# define ASIO_OPTION_STORAGE implementation_defined
@@ -51,6 +37,8 @@
# define ASIO_OPTION_STORAGE termios
#endif
+#include "asio/detail/push_options.hpp"
+
namespace asio {
/// The serial_port_base class is used as a base for the basic_serial_port class
@@ -67,9 +55,11 @@ public:
public:
explicit baud_rate(unsigned int rate = 0);
unsigned int value() const;
- asio::error_code store(ASIO_OPTION_STORAGE& storage,
+ ASIO_DECL asio::error_code store(
+ ASIO_OPTION_STORAGE& storage,
asio::error_code& ec) const;
- asio::error_code load(const ASIO_OPTION_STORAGE& storage,
+ ASIO_DECL asio::error_code load(
+ const ASIO_OPTION_STORAGE& storage,
asio::error_code& ec);
private:
unsigned int value_;
@@ -83,11 +73,13 @@ public:
{
public:
enum type { none, software, hardware };
- explicit flow_control(type t = none);
+ ASIO_DECL explicit flow_control(type t = none);
type value() const;
- asio::error_code store(ASIO_OPTION_STORAGE& storage,
+ ASIO_DECL asio::error_code store(
+ ASIO_OPTION_STORAGE& storage,
asio::error_code& ec) const;
- asio::error_code load(const ASIO_OPTION_STORAGE& storage,
+ ASIO_DECL asio::error_code load(
+ const ASIO_OPTION_STORAGE& storage,
asio::error_code& ec);
private:
type value_;
@@ -101,11 +93,13 @@ public:
{
public:
enum type { none, odd, even };
- explicit parity(type t = none);
+ ASIO_DECL explicit parity(type t = none);
type value() const;
- asio::error_code store(ASIO_OPTION_STORAGE& storage,
+ ASIO_DECL asio::error_code store(
+ ASIO_OPTION_STORAGE& storage,
asio::error_code& ec) const;
- asio::error_code load(const ASIO_OPTION_STORAGE& storage,
+ ASIO_DECL asio::error_code load(
+ const ASIO_OPTION_STORAGE& storage,
asio::error_code& ec);
private:
type value_;
@@ -119,11 +113,13 @@ public:
{
public:
enum type { one, onepointfive, two };
- explicit stop_bits(type t = one);
+ ASIO_DECL explicit stop_bits(type t = one);
type value() const;
- asio::error_code store(ASIO_OPTION_STORAGE& storage,
+ ASIO_DECL asio::error_code store(
+ ASIO_OPTION_STORAGE& storage,
asio::error_code& ec) const;
- asio::error_code load(const ASIO_OPTION_STORAGE& storage,
+ ASIO_DECL asio::error_code load(
+ const ASIO_OPTION_STORAGE& storage,
asio::error_code& ec);
private:
type value_;
@@ -136,11 +132,13 @@ public:
class character_size
{
public:
- explicit character_size(unsigned int t = 8);
+ ASIO_DECL explicit character_size(unsigned int t = 8);
unsigned int value() const;
- asio::error_code store(ASIO_OPTION_STORAGE& storage,
+ ASIO_DECL asio::error_code store(
+ ASIO_OPTION_STORAGE& storage,
asio::error_code& ec) const;
- asio::error_code load(const ASIO_OPTION_STORAGE& storage,
+ ASIO_DECL asio::error_code load(
+ const ASIO_OPTION_STORAGE& storage,
asio::error_code& ec);
private:
unsigned int value_;
@@ -161,13 +159,16 @@ private:
} // namespace asio
-#include "asio/impl/serial_port_base.ipp"
+#include "asio/detail/pop_options.hpp"
#undef ASIO_OPTION_STORAGE
+#include "asio/impl/serial_port_base.hpp"
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/impl/serial_port_base.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
#endif // defined(ASIO_HAS_SERIAL_PORT)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_SERIAL_PORT_BASE_HPP
diff --git a/ext/asio/asio/serial_port_service.hpp b/ext/asio/asio/serial_port_service.hpp
index 5847c29..37142d5 100644
--- a/ext/asio/asio/serial_port_service.hpp
+++ b/ext/asio/asio/serial_port_service.hpp
@@ -2,7 +2,7 @@
// serial_port_service.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,23 +15,20 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_SERIAL_PORT) \
+ || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/push_options.hpp"
#include <cstddef>
-#include <boost/config.hpp>
#include <string>
-#include "asio/detail/pop_options.hpp"
-
+#include "asio/detail/reactive_serial_port_service.hpp"
+#include "asio/detail/win_iocp_serial_port_service.hpp"
#include "asio/error.hpp"
#include "asio/io_service.hpp"
#include "asio/serial_port_base.hpp"
-#include "asio/detail/service_base.hpp"
-#include "asio/detail/reactive_serial_port_service.hpp"
-#include "asio/detail/win_iocp_serial_port_service.hpp"
-#if defined(ASIO_HAS_SERIAL_PORT) \
- || defined(GENERATING_DOCUMENTATION)
+#include "asio/detail/push_options.hpp"
namespace asio {
@@ -199,9 +196,9 @@ private:
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#endif // defined(ASIO_HAS_SERIAL_PORT)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_SERIAL_PORT_SERVICE_HPP
diff --git a/ext/asio/asio/socket_acceptor_service.hpp b/ext/asio/asio/socket_acceptor_service.hpp
index b2e2c6d..a1e4874 100644
--- a/ext/asio/asio/socket_acceptor_service.hpp
+++ b/ext/asio/asio/socket_acceptor_service.hpp
@@ -2,7 +2,7 @@
// socket_acceptor_service.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,12 +15,10 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/basic_socket.hpp"
#include "asio/error.hpp"
#include "asio/io_service.hpp"
-#include "asio/detail/service_base.hpp"
#if defined(ASIO_HAS_IOCP)
# include "asio/detail/win_iocp_socket_service.hpp"
@@ -28,6 +26,8 @@
# include "asio/detail/reactive_socket_service.hpp"
#endif
+#include "asio/detail/push_options.hpp"
+
namespace asio {
/// Default service implementation for a socket acceptor.
diff --git a/ext/asio/asio/socket_base.hpp b/ext/asio/asio/socket_base.hpp
index d82cd22..56585e1 100644
--- a/ext/asio/asio/socket_base.hpp
+++ b/ext/asio/asio/socket_base.hpp
@@ -2,7 +2,7 @@
// socket_base.hpp
// ~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
+#include "asio/detail/config.hpp"
#include <boost/detail/workaround.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/detail/io_control.hpp"
#include "asio/detail/socket_option.hpp"
#include "asio/detail/socket_types.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
/// The socket_base class is used as a base for the basic_stream_socket and
diff --git a/ext/asio/asio/ssl.hpp b/ext/asio/asio/ssl.hpp
index a9fff5e..48aff5a 100644
--- a/ext/asio/asio/ssl.hpp
+++ b/ext/asio/asio/ssl.hpp
@@ -2,7 +2,7 @@
// ssl.hpp
// ~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/ext/asio/asio/ssl/basic_context.hpp b/ext/asio/asio/ssl/basic_context.hpp
index ea3893e..20ed57d 100755
--- a/ext/asio/asio/ssl/basic_context.hpp
+++ b/ext/asio/asio/ssl/basic_context.hpp
@@ -1,9 +1,9 @@
//
-// basic_context.hpp
-// ~~~~~~~~~~~~~~~~~
+// ssl/basic_context.hpp
+// ~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
-// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -16,17 +16,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <string>
#include <boost/noncopyable.hpp>
-#include "asio/detail/pop_options.hpp"
-
+#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/io_service.hpp"
#include "asio/ssl/context_base.hpp"
-#include "asio/detail/throw_error.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace ssl {
diff --git a/ext/asio/asio/ssl/context.hpp b/ext/asio/asio/ssl/context.hpp
index d53882a..7aedc4d 100644
--- a/ext/asio/asio/ssl/context.hpp
+++ b/ext/asio/asio/ssl/context.hpp
@@ -1,9 +1,9 @@
//
-// context.hpp
-// ~~~~~~~~~~~
+// ssl/context.hpp
+// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
-// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -16,8 +16,7 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/ssl/basic_context.hpp"
#include "asio/ssl/context_service.hpp"
@@ -30,6 +29,4 @@ typedef basic_context<context_service> context;
} // namespace ssl
} // namespace asio
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_SSL_CONTEXT_HPP
diff --git a/ext/asio/asio/ssl/context_base.hpp b/ext/asio/asio/ssl/context_base.hpp
index a0700ca..7636bed 100755
--- a/ext/asio/asio/ssl/context_base.hpp
+++ b/ext/asio/asio/ssl/context_base.hpp
@@ -1,8 +1,8 @@
//
-// context_base.hpp
-// ~~~~~~~~~~~~~~~~
+// ssl/context_base.hpp
+// ~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,15 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
+#include "asio/detail/config.hpp"
#include <boost/detail/workaround.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/ssl/detail/openssl_types.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace ssl {
diff --git a/ext/asio/asio/ssl/context_service.hpp b/ext/asio/asio/ssl/context_service.hpp
index e9cfef7..766bbde 100755
--- a/ext/asio/asio/ssl/context_service.hpp
+++ b/ext/asio/asio/ssl/context_service.hpp
@@ -1,9 +1,9 @@
//
-// context_service.hpp
-// ~~~~~~~~~~~~~~~~~~~
+// ssl/context_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
-// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -16,19 +16,16 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <string>
#include <boost/noncopyable.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/error.hpp"
#include "asio/io_service.hpp"
-#include "asio/detail/service_base.hpp"
#include "asio/ssl/context_base.hpp"
#include "asio/ssl/detail/openssl_context_service.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace ssl {
diff --git a/ext/asio/asio/ssl/detail/openssl_context_service.hpp b/ext/asio/asio/ssl/detail/openssl_context_service.hpp
index a3d4fdb..7019542 100755
--- a/ext/asio/asio/ssl/detail/openssl_context_service.hpp
+++ b/ext/asio/asio/ssl/detail/openssl_context_service.hpp
@@ -1,9 +1,9 @@
//
-// openssl_context_service.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// ssl/detail/openssl_context_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
-// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -16,21 +16,18 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstring>
#include <string>
#include <boost/function.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/error.hpp"
#include "asio/io_service.hpp"
-#include "asio/detail/service_base.hpp"
#include "asio/ssl/context_base.hpp"
#include "asio/ssl/detail/openssl_init.hpp"
#include "asio/ssl/detail/openssl_types.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace ssl {
namespace detail {
diff --git a/ext/asio/asio/ssl/detail/openssl_init.hpp b/ext/asio/asio/ssl/detail/openssl_init.hpp
index 9ecbb78..dcda50f 100755
--- a/ext/asio/asio/ssl/detail/openssl_init.hpp
+++ b/ext/asio/asio/ssl/detail/openssl_init.hpp
@@ -1,9 +1,9 @@
//
-// openssl_init.hpp
-// ~~~~~~~~~~~~~~~~
+// ssl/detail/openssl_init.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
-// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -16,20 +16,17 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstring>
#include <vector>
#include <boost/assert.hpp>
-#include <boost/config.hpp>
#include <boost/shared_ptr.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/detail/mutex.hpp"
#include "asio/detail/tss_ptr.hpp"
#include "asio/ssl/detail/openssl_types.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
namespace ssl {
namespace detail {
diff --git a/ext/asio/asio/ssl/detail/openssl_operation.hpp b/ext/asio/asio/ssl/detail/openssl_operation.hpp
index 8d237e3..c5119a5 100755
--- a/ext/asio/asio/ssl/detail/openssl_operation.hpp
+++ b/ext/asio/asio/ssl/detail/openssl_operation.hpp
@@ -1,6 +1,6 @@
//
-// openssl_operation.hpp
-// ~~~~~~~~~~~~~~~~~~~~~
+// ssl/detail/openssl_operation.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
//
@@ -15,19 +15,19 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <boost/function.hpp>
#include <boost/assert.hpp>
#include <boost/bind.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/buffer.hpp"
-#include "asio/placeholders.hpp"
-#include "asio/write.hpp"
#include "asio/detail/socket_ops.hpp"
+#include "asio/placeholders.hpp"
#include "asio/ssl/detail/openssl_types.hpp"
+#include "asio/strand.hpp"
+#include "asio/system_error.hpp"
+#include "asio/write.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace ssl {
@@ -160,7 +160,7 @@ public:
if (error_code == SSL_ERROR_SSL)
return handler_(asio::error_code(
- error_code, asio::error::get_ssl_category()), rc);
+ sys_error_code, asio::error::get_ssl_category()), rc);
bool is_read_needed = (error_code == SSL_ERROR_WANT_READ);
bool is_write_needed = (error_code == SSL_ERROR_WANT_WRITE ||
@@ -195,7 +195,7 @@ public:
else
{
return handler_(asio::error_code(
- error_code, asio::error::get_ssl_category()), rc);
+ sys_error_code, asio::error::get_ssl_category()), rc);
}
}
diff --git a/ext/asio/asio/ssl/detail/openssl_stream_service.hpp b/ext/asio/asio/ssl/detail/openssl_stream_service.hpp
index d7bb457..aef9e7e 100644
--- a/ext/asio/asio/ssl/detail/openssl_stream_service.hpp
+++ b/ext/asio/asio/ssl/detail/openssl_stream_service.hpp
@@ -1,9 +1,9 @@
//
-// stream_service.hpp
-// ~~~~~~~~~~~~~~~~~~
+// ssl/detail/stream_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
-// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -16,9 +16,7 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
#include <climits>
#include <memory>
@@ -26,17 +24,17 @@
#include <boost/noncopyable.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
-#include "asio/detail/pop_options.hpp"
-
+#include "asio/detail/buffer_sequence_adapter.hpp"
#include "asio/error.hpp"
#include "asio/io_service.hpp"
-#include "asio/strand.hpp"
-#include "asio/detail/buffer_sequence_adapter.hpp"
-#include "asio/detail/service_base.hpp"
#include "asio/ssl/basic_context.hpp"
#include "asio/ssl/stream_base.hpp"
#include "asio/ssl/detail/openssl_operation.hpp"
#include "asio/ssl/detail/openssl_types.hpp"
+#include "asio/strand.hpp"
+#include "asio/system_error.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace ssl {
@@ -92,7 +90,7 @@ private:
: base_handler<Stream>(io_service)
, handler_(handler)
{
- set_func(boost::bind(
+ this->set_func(boost::bind(
&io_handler<Stream, Handler>::handler_impl,
this, boost::arg<1>(), boost::arg<2>() ));
}
@@ -116,7 +114,7 @@ private:
: base_handler<Stream>(io_service)
, handler_(handler)
{
- set_func(boost::bind(
+ this->set_func(boost::bind(
&handshake_handler<Stream, Handler>::handler_impl,
this, boost::arg<1>(), boost::arg<2>() ));
}
@@ -141,7 +139,7 @@ private:
: base_handler<Stream>(io_service),
handler_(handler)
{
- set_func(boost::bind(
+ this->set_func(boost::bind(
&shutdown_handler<Stream, Handler>::handler_impl,
this, boost::arg<1>(), boost::arg<2>() ));
}
diff --git a/ext/asio/asio/ssl/detail/openssl_types.hpp b/ext/asio/asio/ssl/detail/openssl_types.hpp
index c697d74..fbe1b74 100755
--- a/ext/asio/asio/ssl/detail/openssl_types.hpp
+++ b/ext/asio/asio/ssl/detail/openssl_types.hpp
@@ -1,8 +1,8 @@
//
-// openssl_types.hpp
-// ~~~~~~~~~~~~~~~~~
+// ssl/detail/openssl_types.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,11 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/socket_types.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <openssl/conf.h>
#include <openssl/ssl.h>
#include <openssl/engine.h>
#include <openssl/err.h>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/detail/pop_options.hpp"
+#include "asio/detail/socket_types.hpp"
#endif // ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP
diff --git a/ext/asio/asio/ssl/stream.hpp b/ext/asio/asio/ssl/stream.hpp
index e800e62..e16cd9a 100644
--- a/ext/asio/asio/ssl/stream.hpp
+++ b/ext/asio/asio/ssl/stream.hpp
@@ -1,9 +1,9 @@
//
-// stream.hpp
-// ~~~~~~~~~~
+// ssl/stream.hpp
+// ~~~~~~~~~~~~~~
//
// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
-// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -16,20 +16,17 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
#include <boost/noncopyable.hpp>
#include <boost/type_traits/remove_reference.hpp>
-#include "asio/detail/pop_options.hpp"
-
+#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/ssl/basic_context.hpp"
#include "asio/ssl/stream_base.hpp"
#include "asio/ssl/stream_service.hpp"
-#include "asio/detail/throw_error.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace ssl {
diff --git a/ext/asio/asio/ssl/stream_base.hpp b/ext/asio/asio/ssl/stream_base.hpp
index d62d386..e1cc0d7 100755
--- a/ext/asio/asio/ssl/stream_base.hpp
+++ b/ext/asio/asio/ssl/stream_base.hpp
@@ -1,8 +1,8 @@
//
-// stream_base.hpp
-// ~~~~~~~~~~~~~~~
+// ssl/stream_base.hpp
+// ~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,11 +15,10 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+#include <boost/detail/workaround.hpp>
#include "asio/detail/push_options.hpp"
-#include <boost/detail/workaround.hpp>
-#include "asio/detail/pop_options.hpp"
namespace asio {
namespace ssl {
diff --git a/ext/asio/asio/ssl/stream_service.hpp b/ext/asio/asio/ssl/stream_service.hpp
index 1f1e6ad..d99548e 100644
--- a/ext/asio/asio/ssl/stream_service.hpp
+++ b/ext/asio/asio/ssl/stream_service.hpp
@@ -1,9 +1,9 @@
//
-// stream_service.hpp
-// ~~~~~~~~~~~~~~~~~~
+// ssl/stream_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
-// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -16,19 +16,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
#include <boost/noncopyable.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/io_service.hpp"
-#include "asio/detail/service_base.hpp"
#include "asio/ssl/basic_context.hpp"
-#include "asio/ssl/stream_base.hpp"
#include "asio/ssl/detail/openssl_stream_service.hpp"
+#include "asio/ssl/stream_base.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace ssl {
diff --git a/ext/asio/asio/strand.hpp b/ext/asio/asio/strand.hpp
index 6b32151..5a526f2 100644
--- a/ext/asio/asio/strand.hpp
+++ b/ext/asio/asio/strand.hpp
@@ -2,7 +2,7 @@
// strand.hpp
// ~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,11 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/io_service.hpp"
+#include "asio/detail/config.hpp"
#include "asio/detail/strand_service.hpp"
#include "asio/detail/wrapped_handler.hpp"
+#include "asio/io_service.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
diff --git a/ext/asio/asio/stream_socket_service.hpp b/ext/asio/asio/stream_socket_service.hpp
index 1c4935c..553719e 100644
--- a/ext/asio/asio/stream_socket_service.hpp
+++ b/ext/asio/asio/stream_socket_service.hpp
@@ -2,7 +2,7 @@
// stream_socket_service.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,10 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/error.hpp"
#include "asio/io_service.hpp"
-#include "asio/detail/service_base.hpp"
#if defined(ASIO_HAS_IOCP)
# include "asio/detail/win_iocp_socket_service.hpp"
@@ -32,6 +26,8 @@
# include "asio/detail/reactive_socket_service.hpp"
#endif
+#include "asio/detail/push_options.hpp"
+
namespace asio {
/// Default service implementation for a stream socket.
diff --git a/ext/asio/asio/streambuf.hpp b/ext/asio/asio/streambuf.hpp
index 665155b..553da3e 100644
--- a/ext/asio/asio/streambuf.hpp
+++ b/ext/asio/asio/streambuf.hpp
@@ -2,7 +2,7 @@
// streambuf.hpp
// ~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,12 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/basic_streambuf.hpp"
+#include "asio/detail/config.hpp"
#if !defined(BOOST_NO_IOSTREAM)
+#include "asio/basic_streambuf.hpp"
+
namespace asio {
/// Typedef for the typical usage of basic_streambuf.
@@ -30,6 +30,4 @@ typedef basic_streambuf<> streambuf;
#endif // !defined(BOOST_NO_IOSTREAM)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_STREAMBUF_HPP
diff --git a/ext/asio/asio/system_error.hpp b/ext/asio/asio/system_error.hpp
index e704b3f..60580a1 100644
--- a/ext/asio/asio/system_error.hpp
+++ b/ext/asio/asio/system_error.hpp
@@ -2,7 +2,7 @@
// system_error.hpp
// ~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,18 +15,15 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
+#include "asio/detail/config.hpp"
#include <boost/scoped_ptr.hpp>
#include <cerrno>
#include <exception>
#include <string>
-#include "asio/detail/pop_options.hpp"
-
#include "asio/error_code.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
/// The system_error class is used to represent system conditions that
diff --git a/ext/asio/asio/thread.hpp b/ext/asio/asio/thread.hpp
index 9e578bc..070483e 100644
--- a/ext/asio/asio/thread.hpp
+++ b/ext/asio/asio/thread.hpp
@@ -2,7 +2,7 @@
// thread.hpp
// ~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,11 +15,12 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
+#include "asio/detail/config.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/thread.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
/// A simple abstraction for starting threads.
diff --git a/ext/asio/asio/time_traits.hpp b/ext/asio/asio/time_traits.hpp
index 0371373..3b2899b 100644
--- a/ext/asio/asio/time_traits.hpp
+++ b/ext/asio/asio/time_traits.hpp
@@ -2,7 +2,7 @@
// time_traits.hpp
// ~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,14 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
#include "asio/detail/socket_types.hpp" // Must come before posix_time.
#include "asio/detail/push_options.hpp"
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include "asio/detail/pop_options.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
/// Time traits suitable for use with the deadline timer.
diff --git a/ext/asio/asio/version.hpp b/ext/asio/asio/version.hpp
index 198f508..0402d0e 100644
--- a/ext/asio/asio/version.hpp
+++ b/ext/asio/asio/version.hpp
@@ -2,7 +2,7 @@
// version.hpp
// ~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -18,6 +18,6 @@
// ASIO_VERSION % 100 is the sub-minor version
// ASIO_VERSION / 100 % 1000 is the minor version
// ASIO_VERSION / 100000 is the major version
-#define ASIO_VERSION 100405 // 1.4.5
+#define ASIO_VERSION 100408 // 1.4.8
#endif // ASIO_VERSION_HPP
diff --git a/ext/asio/asio/windows/basic_handle.hpp b/ext/asio/asio/windows/basic_handle.hpp
index 8c2ee60..f87b3db 100644
--- a/ext/asio/asio/windows/basic_handle.hpp
+++ b/ext/asio/asio/windows/basic_handle.hpp
@@ -1,8 +1,8 @@
//
-// basic_handle.hpp
-// ~~~~~~~~~~~~~~~~
+// windows/basic_handle.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,15 +15,17 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \
+ || defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \
+ || defined(GENERATING_DOCUMENTATION)
#include "asio/basic_io_object.hpp"
-#include "asio/error.hpp"
#include "asio/detail/throw_error.hpp"
+#include "asio/error.hpp"
+
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace windows {
@@ -222,4 +224,8 @@ protected:
#include "asio/detail/pop_options.hpp"
+#endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE)
+ // || defined(ASIO_HAS_WINDOWS_STREAM_HANDLE)
+ // || defined(GENERATING_DOCUMENTATION)
+
#endif // ASIO_WINDOWS_BASIC_HANDLE_HPP
diff --git a/ext/asio/asio/windows/basic_random_access_handle.hpp b/ext/asio/asio/windows/basic_random_access_handle.hpp
index 2e6b994..056b175 100644
--- a/ext/asio/asio/windows/basic_random_access_handle.hpp
+++ b/ext/asio/asio/windows/basic_random_access_handle.hpp
@@ -1,8 +1,8 @@
//
-// basic_random_access_handle.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// windows/basic_random_access_handle.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,20 +15,18 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
-#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \
+ || defined(GENERATING_DOCUMENTATION)
+#include <cstddef>
+#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/windows/basic_handle.hpp"
#include "asio/windows/random_access_handle_service.hpp"
-#include "asio/detail/throw_error.hpp"
-#if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \
- || defined(GENERATING_DOCUMENTATION)
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace windows {
@@ -312,9 +310,9 @@ public:
} // namespace windows
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_WINDOWS_BASIC_RANDOM_ACCESS_HANDLE_HPP
diff --git a/ext/asio/asio/windows/basic_stream_handle.hpp b/ext/asio/asio/windows/basic_stream_handle.hpp
index 48e7153..28149f4 100644
--- a/ext/asio/asio/windows/basic_stream_handle.hpp
+++ b/ext/asio/asio/windows/basic_stream_handle.hpp
@@ -1,8 +1,8 @@
//
-// basic_stream_handle.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~
+// windows/basic_stream_handle.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,20 +15,18 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
-#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \
+ || defined(GENERATING_DOCUMENTATION)
+#include <cstddef>
#include "asio/error.hpp"
#include "asio/windows/basic_handle.hpp"
#include "asio/windows/stream_handle_service.hpp"
#include "asio/detail/throw_error.hpp"
-#if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \
- || defined(GENERATING_DOCUMENTATION)
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace windows {
@@ -294,9 +292,9 @@ public:
} // namespace windows
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#endif // defined(ASIO_HAS_WINDOWS_STREAM_HANDLE)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_WINDOWS_BASIC_STREAM_HANDLE_HPP
diff --git a/ext/asio/asio/windows/overlapped_ptr.hpp b/ext/asio/asio/windows/overlapped_ptr.hpp
index 087170b..8f4d628 100644
--- a/ext/asio/asio/windows/overlapped_ptr.hpp
+++ b/ext/asio/asio/windows/overlapped_ptr.hpp
@@ -1,8 +1,8 @@
//
-// overlapped_ptr.hpp
-// ~~~~~~~~~~~~~~~~~~
+// windows/overlapped_ptr.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,20 +15,16 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_WINDOWS_OVERLAPPED_PTR) \
+ || defined(GENERATING_DOCUMENTATION)
-#include "asio/io_service.hpp"
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/win_iocp_overlapped_ptr.hpp"
+#include "asio/io_service.hpp"
-#if !defined(ASIO_DISABLE_WINDOWS_OVERLAPPED_PTR)
-# if defined(ASIO_HAS_IOCP)
-# define ASIO_HAS_WINDOWS_OVERLAPPED_PTR 1
-# endif // defined(ASIO_HAS_IOCP)
-#endif // !defined(ASIO_DISABLE_WINDOWS_OVERLAPPED_PTR)
-
-#if defined(ASIO_HAS_WINDOWS_OVERLAPPED_PTR) \
- || defined(GENERATING_DOCUMENTATION)
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace windows {
@@ -110,9 +106,9 @@ private:
} // namespace windows
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#endif // defined(ASIO_HAS_WINDOWS_OVERLAPPED_PTR)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_WINDOWS_OVERLAPPED_PTR_HPP
diff --git a/ext/asio/asio/windows/random_access_handle.hpp b/ext/asio/asio/windows/random_access_handle.hpp
index 6e5dd7b..727b084 100644
--- a/ext/asio/asio/windows/random_access_handle.hpp
+++ b/ext/asio/asio/windows/random_access_handle.hpp
@@ -1,8 +1,8 @@
//
-// random_access_handle.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~
+// windows/random_access_handle.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,13 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/windows/basic_random_access_handle.hpp"
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \
|| defined(GENERATING_DOCUMENTATION)
+#include "asio/windows/basic_random_access_handle.hpp"
+
namespace asio {
namespace windows {
@@ -34,6 +34,4 @@ typedef basic_random_access_handle<> random_access_handle;
#endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_HPP
diff --git a/ext/asio/asio/windows/random_access_handle_service.hpp b/ext/asio/asio/windows/random_access_handle_service.hpp
index 2e579f8..b2c05ef 100644
--- a/ext/asio/asio/windows/random_access_handle_service.hpp
+++ b/ext/asio/asio/windows/random_access_handle_service.hpp
@@ -1,8 +1,8 @@
//
-// random_access_handle_service.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// windows/random_access_handle_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,27 +15,19 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
+
+#if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \
+ || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/push_options.hpp"
#include <cstddef>
#include <boost/config.hpp>
#include <boost/cstdint.hpp>
-#include "asio/detail/pop_options.hpp"
-
+#include "asio/detail/win_iocp_handle_service.hpp"
#include "asio/error.hpp"
#include "asio/io_service.hpp"
-#include "asio/detail/service_base.hpp"
-#include "asio/detail/win_iocp_handle_service.hpp"
-#if !defined(ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE)
-# if defined(ASIO_HAS_IOCP)
-# define ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE 1
-# endif // defined(ASIO_HAS_IOCP)
-#endif // !defined(ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE)
-
-#if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \
- || defined(GENERATING_DOCUMENTATION)
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace windows {
@@ -172,9 +164,9 @@ private:
} // namespace windows
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_SERVICE_HPP
diff --git a/ext/asio/asio/windows/stream_handle.hpp b/ext/asio/asio/windows/stream_handle.hpp
index d55dc31..802d157 100644
--- a/ext/asio/asio/windows/stream_handle.hpp
+++ b/ext/asio/asio/windows/stream_handle.hpp
@@ -1,8 +1,8 @@
//
-// stream_handle.hpp
-// ~~~~~~~~~~~~~~~~~~
+// windows/stream_handle.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,13 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/windows/basic_stream_handle.hpp"
+#include "asio/detail/config.hpp"
#if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \
|| defined(GENERATING_DOCUMENTATION)
+#include "asio/windows/basic_stream_handle.hpp"
+
namespace asio {
namespace windows {
@@ -34,6 +34,4 @@ typedef basic_stream_handle<> stream_handle;
#endif // defined(ASIO_HAS_WINDOWS_STREAM_HANDLE)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_WINDOWS_STREAM_HANDLE_HPP
diff --git a/ext/asio/asio/windows/stream_handle_service.hpp b/ext/asio/asio/windows/stream_handle_service.hpp
index 75eff60..816cfe5 100644
--- a/ext/asio/asio/windows/stream_handle_service.hpp
+++ b/ext/asio/asio/windows/stream_handle_service.hpp
@@ -1,8 +1,8 @@
//
-// stream_handle_service.hpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~
+// windows/stream_handle_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,26 +15,17 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
-#include "asio/detail/push_options.hpp"
-#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
+#if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \
+ || defined(GENERATING_DOCUMENTATION)
+#include <cstddef>
+#include "asio/detail/win_iocp_handle_service.hpp"
#include "asio/error.hpp"
#include "asio/io_service.hpp"
-#include "asio/detail/service_base.hpp"
-#include "asio/detail/win_iocp_handle_service.hpp"
-#if !defined(ASIO_DISABLE_WINDOWS_STREAM_HANDLE)
-# if defined(ASIO_HAS_IOCP)
-# define ASIO_HAS_WINDOWS_STREAM_HANDLE 1
-# endif // defined(ASIO_HAS_IOCP)
-#endif // !defined(ASIO_DISABLE_WINDOWS_STREAM_HANDLE)
-
-#if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \
- || defined(GENERATING_DOCUMENTATION)
+#include "asio/detail/push_options.hpp"
namespace asio {
namespace windows {
@@ -170,9 +161,9 @@ private:
} // namespace windows
} // namespace asio
+#include "asio/detail/pop_options.hpp"
+
#endif // defined(ASIO_HAS_WINDOWS_STREAM_HANDLE)
// || defined(GENERATING_DOCUMENTATION)
-#include "asio/detail/pop_options.hpp"
-
#endif // ASIO_WINDOWS_STREAM_HANDLE_SERVICE_HPP
diff --git a/ext/asio/asio/write.hpp b/ext/asio/asio/write.hpp
index 2b188cd..bd90ecb 100644
--- a/ext/asio/asio/write.hpp
+++ b/ext/asio/asio/write.hpp
@@ -2,7 +2,7 @@
// write.hpp
// ~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,16 +15,13 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/basic_streambuf.hpp"
+#include "asio/basic_streambuf_fwd.hpp"
#include "asio/error.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
/**
@@ -533,8 +530,8 @@ void async_write(AsyncWriteStream& s, basic_streambuf<Allocator>& b,
} // namespace asio
-#include "asio/impl/write.ipp"
-
#include "asio/detail/pop_options.hpp"
+#include "asio/impl/write.hpp"
+
#endif // ASIO_WRITE_HPP
diff --git a/ext/asio/asio/write_at.hpp b/ext/asio/asio/write_at.hpp
index 5690bcf..37d0cbb 100644
--- a/ext/asio/asio/write_at.hpp
+++ b/ext/asio/asio/write_at.hpp
@@ -2,7 +2,7 @@
// write_at.hpp
// ~~~~~~~~~~~~
//
-// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,17 +15,14 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
-#include "asio/detail/push_options.hpp"
-
-#include "asio/detail/push_options.hpp"
+#include "asio/detail/config.hpp"
#include <cstddef>
-#include <boost/config.hpp>
#include <boost/cstdint.hpp>
-#include "asio/detail/pop_options.hpp"
-
-#include "asio/basic_streambuf.hpp"
+#include "asio/basic_streambuf_fwd.hpp"
#include "asio/error.hpp"
+#include "asio/detail/push_options.hpp"
+
namespace asio {
/**
@@ -556,8 +553,8 @@ void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset,
} // namespace asio
-#include "asio/impl/write_at.ipp"
-
#include "asio/detail/pop_options.hpp"
+#include "asio/impl/write_at.hpp"
+
#endif // ASIO_WRITE_AT_HPP
diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am
index 1768ce7..06d8df2 100644
--- a/src/bin/Makefile.am
+++ b/src/bin/Makefile.am
@@ -1,4 +1,4 @@
SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout \
- usermgr zonemgr stats tests resolver
+ usermgr zonemgr stats tests resolver sockcreator dhcp6
check-recursive: all-recursive
diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am
index cdfc55e..64136c1 100644
--- a/src/bin/auth/Makefile.am
+++ b/src/bin/auth/Makefile.am
@@ -16,7 +16,8 @@ endif
pkglibexecdir = $(libexecdir)/@PACKAGE@
-CLEANFILES = *.gcno *.gcda auth.spec spec_config.h
+CLEANFILES = *.gcno *.gcda auth.spec spec_config.h
+CLEANFILES += auth_messages.h auth_messages.cc
man_MANS = b10-auth.8
EXTRA_DIST = $(man_MANS) b10-auth.xml
@@ -34,23 +35,33 @@ auth.spec: auth.spec.pre
spec_config.h: spec_config.h.pre
$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
-BUILT_SOURCES = spec_config.h
+auth_messages.h auth_messages.cc: auth_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/auth/auth_messages.mes
+
+BUILT_SOURCES = spec_config.h auth_messages.h auth_messages.cc
+
pkglibexec_PROGRAMS = b10-auth
b10_auth_SOURCES = query.cc query.h
b10_auth_SOURCES += auth_srv.cc auth_srv.h
+b10_auth_SOURCES += auth_log.cc auth_log.h
b10_auth_SOURCES += change_user.cc change_user.h
-b10_auth_SOURCES += config.cc config.h
+b10_auth_SOURCES += auth_config.cc auth_config.h
b10_auth_SOURCES += command.cc command.h
-b10_auth_SOURCES += common.h
+b10_auth_SOURCES += common.h common.cc
b10_auth_SOURCES += statistics.cc statistics.h
b10_auth_SOURCES += main.cc
+
+nodist_b10_auth_SOURCES = auth_messages.h auth_messages.cc
+EXTRA_DIST += auth_messages.mes
+
b10_auth_LDADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la
b10_auth_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
b10_auth_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.la
b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+b10_auth_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
b10_auth_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-b10_auth_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
+b10_auth_LDADD += $(top_builddir)/src/lib/log/liblog.la
b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
b10_auth_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
b10_auth_LDADD += $(SQLITE_LIBS)
diff --git a/src/bin/auth/auth_config.cc b/src/bin/auth/auth_config.cc
new file mode 100644
index 0000000..2943cb5
--- /dev/null
+++ b/src/bin/auth/auth_config.cc
@@ -0,0 +1,361 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <boost/foreach.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <cc/data.h>
+
+#include <datasrc/memory_datasrc.h>
+#include <datasrc/zonetable.h>
+
+#include <auth/auth_srv.h>
+#include <auth/auth_config.h>
+#include <auth/common.h>
+
+#include <server_common/portconfig.h>
+
+using namespace std;
+using boost::shared_ptr;
+using namespace isc::dns;
+using namespace isc::data;
+using namespace isc::datasrc;
+using namespace isc::server_common::portconfig;
+
+namespace {
+// Forward declaration
+AuthConfigParser*
+createAuthConfigParser(AuthSrv& server, const std::string& config_id,
+ bool internal);
+
+/// A derived \c AuthConfigParser class for the "datasources" configuration
+/// identifier.
+class DatasourcesConfig : public AuthConfigParser {
+public:
+ DatasourcesConfig(AuthSrv& server) : server_(server) {}
+ virtual void build(ConstElementPtr config_value);
+ virtual void commit();
+private:
+ AuthSrv& server_;
+ vector<shared_ptr<AuthConfigParser> > datasources_;
+ set<string> configured_sources_;
+};
+
+/// A derived \c AuthConfigParser for the version value
+/// (which is not used at this moment)
+class VersionConfig : public AuthConfigParser {
+public:
+ VersionConfig() {}
+ virtual void build(ConstElementPtr) {};
+ virtual void commit() {};
+};
+
+void
+DatasourcesConfig::build(ConstElementPtr config_value) {
+ BOOST_FOREACH(ConstElementPtr datasrc_elem, config_value->listValue()) {
+ // The caller is supposed to perform syntax-level checks, but we'll
+ // do minimum level of validation ourselves so that we won't crash due
+ // to a buggy application.
+ ConstElementPtr datasrc_type = datasrc_elem->get("type");
+ if (!datasrc_type) {
+ isc_throw(AuthConfigError, "Missing data source type");
+ }
+
+ if (configured_sources_.find(datasrc_type->stringValue()) !=
+ configured_sources_.end()) {
+ isc_throw(AuthConfigError, "Data source type '" <<
+ datasrc_type->stringValue() << "' already configured");
+ }
+
+ shared_ptr<AuthConfigParser> datasrc_config =
+ shared_ptr<AuthConfigParser>(
+ createAuthConfigParser(server_, string("datasources/") +
+ datasrc_type->stringValue(),
+ true));
+ datasrc_config->build(datasrc_elem);
+ datasources_.push_back(datasrc_config);
+
+ configured_sources_.insert(datasrc_type->stringValue());
+ }
+}
+
+void
+DatasourcesConfig::commit() {
+ // XXX a short term workaround: clear all data sources and then reset
+ // to new ones so that we can remove data sources that don't exist in
+ // the new configuration and have been used in the server.
+ // This could be inefficient and requires knowledge about
+ // server implementation details, and isn't scalable wrt the number of
+ // data source types, and should eventually be improved.
+ // Currently memory data source for class IN is the only possibility.
+ server_.setMemoryDataSrc(RRClass::IN(), AuthSrv::MemoryDataSrcPtr());
+
+ BOOST_FOREACH(shared_ptr<AuthConfigParser> datasrc_config, datasources_) {
+ datasrc_config->commit();
+ }
+}
+
+/// A derived \c AuthConfigParser class for the memory type datasource
+/// configuration. It does not correspond to the configuration syntax;
+/// it's instantiated for internal use.
+class MemoryDatasourceConfig : public AuthConfigParser {
+public:
+ MemoryDatasourceConfig(AuthSrv& server) :
+ server_(server),
+ rrclass_(0) // XXX: dummy initial value
+ {}
+ virtual void build(ConstElementPtr config_value);
+ virtual void commit() {
+ server_.setMemoryDataSrc(rrclass_, memory_datasrc_);
+ }
+private:
+ AuthSrv& server_;
+ RRClass rrclass_;
+ AuthSrv::MemoryDataSrcPtr memory_datasrc_;
+};
+
+void
+MemoryDatasourceConfig::build(ConstElementPtr config_value) {
+ // XXX: apparently we cannot retrieve the default RR class from the
+ // module spec. As a temporary workaround we hardcode the default value.
+ ConstElementPtr rrclass_elem = config_value->get("class");
+ rrclass_ = RRClass(rrclass_elem ? rrclass_elem->stringValue() : "IN");
+
+ // We'd eventually optimize building zones (in case of reloading) by
+ // selectively loading fresh zones. Right now we simply check the
+ // RR class is supported by the server implementation.
+ server_.getMemoryDataSrc(rrclass_);
+ memory_datasrc_ = AuthSrv::MemoryDataSrcPtr(new MemoryDataSrc());
+
+ ConstElementPtr zones_config = config_value->get("zones");
+ if (!zones_config) {
+ // XXX: Like the RR class, we cannot retrieve the default value here,
+ // so we assume an empty zone list in this case.
+ return;
+ }
+
+ BOOST_FOREACH(ConstElementPtr zone_config, zones_config->listValue()) {
+ ConstElementPtr origin = zone_config->get("origin");
+ if (!origin) {
+ isc_throw(AuthConfigError, "Missing zone origin");
+ }
+ ConstElementPtr file = zone_config->get("file");
+ if (!file) {
+ isc_throw(AuthConfigError, "Missing zone file for zone: "
+ << origin->str());
+ }
+ shared_ptr<MemoryZone> new_zone(new MemoryZone(rrclass_,
+ Name(origin->stringValue())));
+ const result::Result result = memory_datasrc_->addZone(new_zone);
+ if (result == result::EXIST) {
+ isc_throw(AuthConfigError, "zone "<< origin->str()
+ << " already exists");
+ }
+
+ /*
+ * TODO: Once we have better reloading of configuration (something
+ * else than throwing everything away and loading it again), we will
+ * need the load method to be split into some kind of build and
+ * commit/abort parts.
+ */
+ new_zone->load(file->stringValue());
+ }
+}
+
+/// A derived \c AuthConfigParser class for the "statistics-internal"
+/// configuration identifier.
+class StatisticsIntervalConfig : public AuthConfigParser {
+public:
+ StatisticsIntervalConfig(AuthSrv& server) :
+ server_(server), interval_(0)
+ {}
+ virtual void build(ConstElementPtr config_value) {
+ const int32_t config_interval = config_value->intValue();
+ if (config_interval < 0) {
+ isc_throw(AuthConfigError, "Negative statistics interval value: "
+ << config_interval);
+ }
+ if (config_interval > 86400) {
+ isc_throw(AuthConfigError, "Statistics interval value "
+ << config_interval
+ << " must be equal to or shorter than 86400");
+ }
+ interval_ = config_interval;
+ }
+ virtual void commit() {
+ // setStatisticsTimerInterval() is not 100% exception free. But
+ // exceptions should happen only in a very rare situation, so we
+ // let them be thrown and subsequently regard them as a fatal error.
+ server_.setStatisticsTimerInterval(interval_);
+ }
+private:
+ AuthSrv& server_;
+ uint32_t interval_;
+};
+
+/// A special parser for testing: it throws from commit() despite the
+/// suggested convention of the class interface.
+class ThrowerCommitConfig : public AuthConfigParser {
+public:
+ virtual void build(ConstElementPtr) {} // ignore param, do nothing
+ virtual void commit() {
+ throw 10;
+ }
+};
+
+/**
+ * \brief Configuration parser for listen_on.
+ *
+ * It parses and sets the listening addresses of the server.
+ *
+ * It acts in unusual way. Since actually binding (changing) the sockets
+ * is an operation that is expected to throw often, it shouldn't happen
+ * in commit. Thefere we do it in build. But if the config is not committed
+ * then, we would have it wrong. So we store the old addresses and if
+ * commit is not called before destruction of the object, we return the
+ * old addresses (which is the same kind of dangerous operation, but it is
+ * expected that if we just managed to bind some and had the old ones binded
+ * before, it should work).
+ *
+ * We might do something better in future (like open only the ports that are
+ * extra, put them in in commit and close the old ones), but that's left out
+ * for now.
+ */
+class ListenAddressConfig : public AuthConfigParser {
+public:
+ ListenAddressConfig(AuthSrv& server) :
+ server_(server)
+ { }
+ ~ ListenAddressConfig() {
+ if (rollbackAddresses_.get() != NULL) {
+ server_.setListenAddresses(*rollbackAddresses_);
+ }
+ }
+private:
+ typedef auto_ptr<AddressList> AddrListPtr;
+public:
+ virtual void build(ConstElementPtr config) {
+ AddressList newAddresses = parseAddresses(config, "listen_on");
+ AddrListPtr old(new AddressList(server_.getListenAddresses()));
+ server_.setListenAddresses(newAddresses);
+ /*
+ * Set the rollback addresses only after successful setting of the
+ * new addresses, so we don't try to rollback if the setup is
+ * unsuccessful (the above can easily throw).
+ */
+ rollbackAddresses_ = old;
+ }
+ virtual void commit() {
+ rollbackAddresses_.release();
+ }
+private:
+ AuthSrv& server_;
+ /**
+ * This is the old address list, if we expect to roll back. When we commit,
+ * this is set to NULL.
+ */
+ AddrListPtr rollbackAddresses_;
+};
+
+// This is a generalized version of create function that can create
+// an AuthConfigParser object for "internal" use.
+AuthConfigParser*
+createAuthConfigParser(AuthSrv& server, const std::string& config_id,
+ bool internal)
+{
+ // For the initial implementation we use a naive if-else blocks for
+ // simplicity. In future we'll probably generalize it using map-like
+ // data structure, and may even provide external register interface so
+ // that it can be dynamically customized.
+ if (config_id == "datasources") {
+ return (new DatasourcesConfig(server));
+ } else if (config_id == "statistics-interval") {
+ return (new StatisticsIntervalConfig(server));
+ } else if (internal && config_id == "datasources/memory") {
+ return (new MemoryDatasourceConfig(server));
+ } else if (config_id == "listen_on") {
+ return (new ListenAddressConfig(server));
+ } else if (config_id == "_commit_throw") {
+ // This is for testing purpose only and should not appear in the
+ // actual configuration syntax. While this could crash the caller
+ // as a result, the server implementation is expected to perform
+ // syntax level validation and should be safe in practice. In future,
+ // we may introduce dynamic registration of configuration parsers,
+ // and then this test can be done in a cleaner and safer way.
+ return (new ThrowerCommitConfig());
+ } else if (config_id == "version") {
+ // Currently, the version identifier is ignored, but it should
+ // later be used to mark backwards incompatible changes in the
+ // config data
+ return (new VersionConfig());
+ } else {
+ isc_throw(AuthConfigError, "Unknown configuration identifier: " <<
+ config_id);
+ }
+}
+} // end of unnamed namespace
+
+AuthConfigParser*
+createAuthConfigParser(AuthSrv& server, const std::string& config_id) {
+ return (createAuthConfigParser(server, config_id, false));
+}
+
+void
+configureAuthServer(AuthSrv& server, ConstElementPtr config_set) {
+ if (!config_set) {
+ isc_throw(AuthConfigError,
+ "Null pointer is passed to configuration parser");
+ }
+
+ typedef shared_ptr<AuthConfigParser> ParserPtr;
+ vector<ParserPtr> parsers;
+ typedef pair<string, ConstElementPtr> ConfigPair;
+ try {
+ BOOST_FOREACH(ConfigPair config_pair, config_set->mapValue()) {
+ // We should eventually integrate the sqlite3 DB configuration to
+ // this framework, but to minimize diff we begin with skipping that
+ // part.
+ if (config_pair.first == "database_file") {
+ continue;
+ }
+
+ ParserPtr parser(createAuthConfigParser(server,
+ config_pair.first));
+ parser->build(config_pair.second);
+ parsers.push_back(parser);
+ }
+ } catch (const AuthConfigError& ex) {
+ throw; // simply rethrowing it
+ } catch (const isc::Exception& ex) {
+ isc_throw(AuthConfigError, "Server configuration failed: " <<
+ ex.what());
+ }
+
+ try {
+ BOOST_FOREACH(ParserPtr parser, parsers) {
+ parser->commit();
+ }
+ } catch (...) {
+ throw FatalError("Unrecoverable error: "
+ "a configuration parser threw in commit");
+ }
+}
diff --git a/src/bin/auth/auth_config.h b/src/bin/auth/auth_config.h
new file mode 100644
index 0000000..6f18810
--- /dev/null
+++ b/src/bin/auth/auth_config.h
@@ -0,0 +1,202 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <exceptions/exceptions.h>
+
+#include <cc/data.h>
+
+#ifndef __CONFIG_H
+#define __CONFIG_H 1
+
+class AuthSrv;
+
+/// An exception that is thrown if an error occurs while configuring an
+/// \c AuthSrv object.
+class AuthConfigError : public isc::Exception {
+public:
+ AuthConfigError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// The abstract base class that represents a single configuration identifier
+/// for an \c AuthSrv object.
+///
+/// In general, each top level configuration identifier for \c AuthSrv is
+/// expected to have its own derived class of this base class.
+/// For example, for the following configuration:
+/// \code { "param1": 10, "param2": { "subparam1": "foo", "subparam2": [] } }
+/// \endcode
+/// "param1" and "param2" are top level identifiers, and would correspond to
+/// derived \c AuthConfigParser classes.
+/// "subparam1" and/or "subparam2" may also have dedicated derived classes.
+///
+/// These derived classes are hidden inside the implementation; applications
+/// are not expected to (and in fact cannot) instantiate them directly.
+///
+/// Each derived class is generally expected to be constructed with an
+/// \c AuthSrv object to be configured and hold a reference to the server
+/// throughout the configuration process.
+/// For each derived class, the \c build() method parses the configuration
+/// value for the corresponding identifier and prepares new configuration
+/// value(s) to be applied to the server. This method may throw an exception
+/// when it encounters an error.
+/// The \c commit() method actually applies the new configuration value
+/// to the server. It's basically not expected to throw an exception;
+/// any configuration operations that can fail (such as ones involving
+/// resource allocation) should be done in \c build().
+///
+/// When the destructor is called before \c commit(), the destructor is
+/// supposed to make sure the state of the \c AuthSrv object is the same
+/// as that before it starts building the configuration value.
+/// If \c build() doesn't change the server state (which is recommended)
+/// the destructor doesn't have to do anything special in this regard.
+/// This is a key to ensure the strong exception guarantee (see also
+/// the description of \c configureAuthServer()).
+class AuthConfigParser {
+ ///
+ /// \name Constructors and Destructor
+ ///
+ /// Note: The copy constructor and the assignment operator are
+ /// intentionally defined as private to make it explicit that this is a
+ /// pure base class.
+ //@{
+private:
+ AuthConfigParser(const AuthConfigParser& source);
+ AuthConfigParser& operator=(const AuthConfigParser& source);
+protected:
+ /// \brief The default constructor.
+ ///
+ /// This is intentionally defined as \c protected as this base class should
+ /// never be instantiated (except as part of a derived class).
+ AuthConfigParser() {}
+public:
+ /// The destructor.
+ virtual ~AuthConfigParser() {}
+ //@}
+
+ /// Prepare configuration value.
+ ///
+ /// This method parses the "value part" of the configuration identifier
+ /// that corresponds to this derived class and prepares a new value to
+ /// apply to the server.
+ /// In the above example, the derived class for the identifier "param1"
+ /// would be passed an data \c Element storing an integer whose value
+ /// is 10, and would record that value internally;
+ /// the derived class for the identifier "param2" would be passed a
+ /// map element and (after parsing) convert it into some internal
+ /// data structure.
+ ///
+ /// This method must validate the given value both in terms of syntax
+ /// and semantics of the configuration, so that the server will be
+ /// validly configured at the time of \c commit(). Note: the given
+ /// configuration value is normally syntactically validated, but the
+ /// \c build() implementation must also expect invalid input. If it
+ /// detects an error it may throw an exception of a derived class
+ /// of \c isc::Exception.
+ ///
+ /// Preparing a configuration value will often require resource
+ /// allocation. If it fails, it may throw a corresponding standard
+ /// exception.
+ ///
+ /// This method is not expected to be called more than once. Although
+ /// multiple calls are not prohibited by the interface, the behavior
+ /// is undefined.
+ ///
+ /// \param config_value The configuration value for the identifier
+ /// corresponding to the derived class.
+ virtual void build(isc::data::ConstElementPtr config_value) = 0;
+
+ /// Apply the prepared configuration value to the server.
+ ///
+ /// This method is expected to be exception free, and, as a consequence,
+ /// it should normally not involve resource allocation.
+ /// Typically it would simply perform exception free assignment or swap
+ /// operation on the value prepared in \c build().
+ /// In some cases, however, it may be very difficult to meet this
+ /// condition in a realistic way, while the failure case should really
+ /// be very rare. In such a case it may throw, and, if the parser is
+ /// called via \c configureAuthServer(), the caller will convert the
+ /// exception as a fatal error.
+ ///
+ /// This method is expected to be called after \c build(), and only once.
+ /// The result is undefined otherwise.
+ virtual void commit() = 0;
+};
+
+/// Configure an \c AuthSrv object with a set of configuration values.
+///
+/// This function parses configuration information stored in \c config_set
+/// and configures the \c server by applying the configuration to it.
+/// It provides the strong exception guarantee as long as the underlying
+/// derived class implementations of \c AuthConfigParser meet the assumption,
+/// that is, it ensures that either configuration is fully applied or the
+/// state of the server is intact.
+///
+/// If a syntax or semantics level error happens during the configuration
+/// (such as malformed configuration or invalid configuration parameter),
+/// this function throws an exception of class \c AuthConfigError.
+/// If the given configuration requires resource allocation and it fails,
+/// a corresponding standard exception will be thrown.
+/// Other exceptions may also be thrown, depending on the implementation of
+/// the underlying derived class of \c AuthConfigError.
+/// In any case the strong guarantee is provided as described above except
+/// in the very rare cases where the \c commit() method of a parser throws
+/// an exception. If that happens this function converts the exception
+/// into a \c FatalError exception and rethrows it. This exception is
+/// expected to be caught at the highest level of the application to terminate
+/// the program gracefully.
+///
+/// \param server The \c AuthSrv object to be configured.
+/// \param config_set A JSON style configuration to apply to \c server.
+void configureAuthServer(AuthSrv& server,
+ isc::data::ConstElementPtr config_set);
+
+/// Create a new \c AuthConfigParser object for a given configuration
+/// identifier.
+///
+/// It internally identifies an appropriate derived class for the given
+/// identifier and creates a new instance of that class. The caller can
+/// then configure the \c server regarding the identifier by calling
+/// the \c build() and \c commit() methods of the returned object.
+///
+/// In practice, this function is only expected to be used as a backend of
+/// \c configureAuthServer() and is not supposed to be called directly
+/// by applications. It is publicly available mainly for testing purposes.
+/// When called directly, the created object must be deleted by the caller.
+/// Note: this means if this module and the caller use incompatible sets of
+/// new/delete, it may cause unexpected strange failure. We could avoid that
+/// by providing a separate deallocation function or by using a smart pointer,
+/// but since the expected usage of this function is very limited (i.e. for
+/// our own testing purposes) it would be an overkilling. We therefore prefer
+/// simplicity and keeping the interface intuitive.
+///
+/// If the resource allocation for the new object fails, a corresponding
+/// standard exception will be thrown. Otherwise this function is not
+/// expected to throw an exception, unless the constructor of the underlying
+/// derived class implementation (unexpectedly) throws.
+///
+/// \param server The \c AuthSrv object to be configured.
+/// \param config_id The configuration identifier for which a parser object
+/// is to be created.
+/// \return A pointer to an \c AuthConfigParser object.
+AuthConfigParser* createAuthConfigParser(AuthSrv& server,
+ const std::string& config_id);
+
+#endif // __CONFIG_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/bin/auth/auth_log.cc b/src/bin/auth/auth_log.cc
new file mode 100644
index 0000000..d41eaea
--- /dev/null
+++ b/src/bin/auth/auth_log.cc
@@ -0,0 +1,26 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// Defines the logger used by the top-level component of b10-auth.
+
+#include "auth_log.h"
+
+namespace isc {
+namespace auth {
+
+isc::log::Logger auth_logger("auth");
+
+} // namespace auth
+} // namespace isc
+
diff --git a/src/bin/auth/auth_log.h b/src/bin/auth/auth_log.h
new file mode 100644
index 0000000..5205624
--- /dev/null
+++ b/src/bin/auth/auth_log.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __AUTH_LOG__H
+#define __AUTH_LOG__H
+
+#include <log/macros.h>
+#include <auth/auth_messages.h>
+
+namespace isc {
+namespace auth {
+
+/// \brief Auth Logging
+///
+/// Defines the levels used to output debug messages in the "auth" part of
+/// the b10-auth program. Higher numbers equate to more verbose (and detailed)
+/// output.
+
+// Debug messages indicating normal startup are logged at this debug level.
+const int DBG_AUTH_START = 10;
+
+// Debug level used to log setting information (such as configuration changes).
+const int DBG_AUTH_OPS = 30;
+
+// Trace detailed operations, including errors raised when processing invalid
+// packets. (These are not logged at severities of WARN or higher for fear
+// that a set of deliberately invalid packets set to the authoritative server
+// could overwhelm the logging.)
+const int DBG_AUTH_DETAIL = 50;
+
+// This level is used to log the contents of packets received and sent.
+const int DBG_AUTH_MESSAGES = 70;
+
+/// Define the logger for the "auth" module part of b10-auth. We could define
+/// a logger in each file, but we would want to define a common name to avoid
+/// spelling mistakes, so it is just one small step from there to define a
+/// module-common logger.
+extern isc::log::Logger auth_logger;
+
+} // namespace nsas
+} // namespace isc
+
+#endif // __AUTH_LOG__H
diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes
new file mode 100644
index 0000000..2bb402c
--- /dev/null
+++ b/src/bin/auth/auth_messages.mes
@@ -0,0 +1,260 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+$NAMESPACE isc::auth
+
+% AUTH_AXFR_ERROR error handling AXFR request: %1
+This is a debug message produced by the authoritative server when it
+has encountered an error processing an AXFR request. The message gives
+the reason for the error, and the server will return a SERVFAIL code to
+the sender.
+
+% AUTH_AXFR_UDP AXFR query received over UDP
+This is a debug message output when the authoritative server has received
+an AXFR query over UDP. Use of UDP for AXFRs is not permitted by the
+protocol, so the server will return a FORMERR error to the sender.
+
+% AUTH_COMMAND_FAILED execution of command channel instruction '%1' failed: %2
+Execution of the specified command by the authoritative server failed. The
+message contains the reason for the failure.
+
+% AUTH_CONFIG_CHANNEL_CREATED configuration session channel created
+This is a debug message indicating that authoritative server has created
+the channel to the configuration manager. It is issued during server
+startup is an indication that the initialization is proceeding normally.
+
+% AUTH_CONFIG_CHANNEL_ESTABLISHED configuration session channel established
+This is a debug message indicating that authoritative server
+has established communication the configuration manager over the
+previously-created channel. It is issued during server startup is an
+indication that the initialization is proceeding normally.
+
+% AUTH_CONFIG_CHANNEL_STARTED configuration session channel started
+This is a debug message, issued when the authoritative server has
+posted a request to be notified when new configuration information is
+available. It is issued during server startup is an indication that
+the initialization is proceeding normally.
+
+% AUTH_CONFIG_LOAD_FAIL load of configuration failed: %1
+An attempt to configure the server with information from the configuration
+database during the startup sequence has failed. (The reason for
+the failure is given in the message.) The server will continue its
+initialization although it may not be configured in the desired way.
+
+% AUTH_CONFIG_UPDATE_FAIL update of configuration failed: %1
+At attempt to update the configuration the server with information
+from the configuration database has failed, the reason being given in
+the message.
+
+% AUTH_DATA_SOURCE data source database file: %1
+This is a debug message produced by the authoritative server when it accesses a
+datebase data source, listing the file that is being accessed.
+
+% AUTH_DNS_SERVICES_CREATED DNS services created
+This is a debug message indicating that the component that will handling
+incoming queries for the authoritiative server (DNSServices) has been
+successfully created. It is issued during server startup is an indication
+that the initialization is proceeding normally.
+
+% AUTH_HEADER_PARSE_FAIL unable to parse header in received DNS packet: %1
+This is a debug message, generated by the authoritative server when an
+attempt to parse the header of a received DNS packet has failed. (The
+reason for the failure is given in the message.) The server will drop the
+packet.
+
+% AUTH_LOAD_TSIG loading TSIG keys
+This is a debug message indicating that the authoritiative server
+has requested the keyring holding TSIG keys from the configuration
+database. It is issued during server startup is an indication that the
+initialization is proceeding normally.
+
+% AUTH_LOAD_ZONE loaded zone %1/%2
+This debug message is issued during the processing of the 'loadzone' command
+when the authoritative server has successfully loaded the named zone of the
+named class.
+
+% AUTH_MEM_DATASRC_DISABLED memory data source is disabled for class %1
+This is a debug message reporting that the authoritative server has
+discovered that the memory data source is disabled for the given class.
+
+% AUTH_MEM_DATASRC_ENABLED memory data source is enabled for class %1
+This is a debug message reporting that the authoritative server has
+discovered that the memory data source is enabled for the given class.
+
+% AUTH_NO_STATS_SESSION session interface for statistics is not available
+The authoritative server had no session with the statistics module at the
+time it attempted to send it data: the attempt has been abandoned. This
+could be an error in configuration.
+
+% AUTH_NO_XFRIN received NOTIFY but XFRIN session is not running
+This is a debug message produced by the authoritative server when it receives
+a NOTIFY packet but the XFRIN process is not running. The packet will be
+dropped and nothing returned to the sender.
+
+% AUTH_NOTIFY_RRTYPE invalid question RR type (%1) in incoming NOTIFY
+This debug message is logged by the authoritative server when it receives
+a NOTIFY packet that an RR type of something other than SOA in the
+question section. (The RR type received is included in the message.) The
+server will return a FORMERR error to the sender.
+
+% AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY
+This debug message is logged by the authoritative server when it receives
+a NOTIFY packet that contains zero or more than one question. (A valid
+NOTIFY packet contains one question.) The server will return a FORMERR
+error to the sender.
+
+% AUTH_PACKET_PARSE_ERROR unable to parse received DNS packet: %1
+This is a debug message, generated by the authoritative server when an
+attempt to parse a received DNS packet has failed due to something other
+than a protocol error. The reason for the failure is given in the message;
+the server will return a SERVFAIL error code to the sender.
+
+% AUTH_PACKET_PROTOCOL_ERROR DNS packet protocol error: %1. Returning %2
+This is a debug message, generated by the authoritative server when an
+attempt to parse a received DNS packet has failed due to a protocol error.
+The reason for the failure is given in the message, as is the error code
+that will be returned to the sender.
+
+% AUTH_PACKET_RECEIVED message received:\n%1
+This is a debug message output by the authoritative server when it
+receives a valid DNS packet.
+
+Note: This message includes the packet received, rendered in the form of
+multiple lines of text. For this reason, it is suggested that this log message
+not be routed to the syslog file, where the multiple lines could confuse
+programs that expect a format of one message per line.
+
+% AUTH_PROCESS_FAIL message processing failure: %1
+This message is generated by the authoritative server when it has
+encountered an internal error whilst processing a received packet:
+the cause of the error is included in the message.
+
+The server will return a SERVFAIL error code to the sender of the packet.
+However, this message indicates a potential error in the server.
+Please open a bug ticket for this issue.
+
+% AUTH_RECEIVED_COMMAND command '%1' received
+This is a debug message issued when the authoritative server has received
+a command on the command channel.
+
+% AUTH_RECEIVED_SENDSTATS command 'sendstats' received
+This is a debug message issued when the authoritative server has received
+a command from the statistics module to send it data. The 'sendstats'
+command is handled differently to other commands, which is why the debug
+message associated with it has its own code.
+
+% AUTH_RESPONSE_RECEIVED received response message, ignoring
+This is a debug message, this is output if the authoritative server
+receives a DNS packet with the QR bit set, i.e. a DNS response. The
+server ignores the packet as it only responds to question packets.
+
+% AUTH_SEND_ERROR_RESPONSE sending an error response (%1 bytes):\n%2
+This is a debug message recording that the authoritative server is sending
+an error response to the originator of the query. A previous message will
+have recorded details of the failure.
+
+Note: This message includes the packet sent, rendered in the form of
+multiple lines of text. For this reason, it is suggested that this log message
+not be routed to the syslog file, where the multiple lines could confuse
+programs that expect a format of one message per line.
+
+% AUTH_SEND_NORMAL_RESPONSE sending an error response (%1 bytes):\n%2
+This is a debug message recording that the authoritative server is sending
+a response to the originator of a query.
+
+Note: This message includes the packet sent, rendered in the form of
+multiple lines of text. For this reason, it is suggested that this log message
+not be routed to the syslog file, where the multiple lines could confuse
+programs that expect a format of one message per line.
+
+% AUTH_SERVER_CREATED server created
+An informational message indicating that the authoritative server process has
+been created and is initializing. The AUTH_SERVER_STARTED message will be
+output when initialization has successfully completed and the server starts
+accepting queries.
+
+% AUTH_SERVER_FAILED server failed: %1
+The authoritative server has encountered a fatal error and is terminating. The
+reason for the failure is included in the message.
+
+% AUTH_SERVER_STARTED server started
+Initialization of the authoritative server has completed successfully
+and it is entering the main loop, waiting for queries to arrive.
+
+% AUTH_SQLITE3 nothing to do for loading sqlite3
+This is a debug message indicating that the authoritative server has
+found that the data source it is loading is an SQLite3 data source,
+so no further validation is needed.
+
+% AUTH_STATS_CHANNEL_CREATED STATS session channel created
+This is a debug message indicating that the authoritative server has
+created a channel to the statistics process. It is issued during server
+startup is an indication that the initialization is proceeding normally.
+
+% AUTH_STATS_CHANNEL_ESTABLISHED STATS session channel established
+This is a debug message indicating that the authoritative server
+has established communication over the previously created statistics
+channel. It is issued during server startup is an indication that the
+initialization is proceeding normally.
+
+% AUTH_STATS_COMMS communication error in sending statistics data: %1
+An error was encountered when the authoritiative server tried to send data
+to the statistics daemon. The message includes additional information
+describing the reason for the failure.
+
+% AUTH_STATS_TIMEOUT timeout while sending statistics data: %1
+The authoritative server sent data to the statistics daemon but received
+no acknowledgement within the specified time. The message includes
+additional information describing the reason for the failure.
+
+% AUTH_STATS_TIMER_DISABLED statistics timer has been disabled
+This is a debug message indicating that the statistics timer has been
+disabled in the authoritative server and no statistics information is
+being produced.
+
+% AUTH_STATS_TIMER_SET statistics timer set to %1 second(s)
+This is a debug message indicating that the statistics timer has been
+enabled and that the authoritative server will produce statistics data
+at the specified interval.
+
+% AUTH_UNSUPPORTED_OPCODE unsupported opcode: %1
+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.
+
+% AUTH_XFRIN_CHANNEL_CREATED XFRIN session channel created
+This is a debug message indicating that the authoritative server has
+created a channel to the XFRIN (Transfer-in) process. It is issued
+during server startup is an indication that the initialization is
+proceeding normally.
+
+% AUTH_XFRIN_CHANNEL_ESTABLISHED XFRIN session channel established
+This is a debug message indicating that the authoritative server has
+established communication over the previously-created channel to the
+XFRIN (Transfer-in) process. It is issued during server startup is an
+indication that the initialization is proceeding normally.
+
+% AUTH_ZONEMGR_COMMS error communicating with zone manager: %1
+This is a debug message output during the processing of a NOTIFY request.
+An error (listed in the message) has been encountered whilst communicating
+with the zone manager. The NOTIFY request will not be honored.
+
+% AUTH_ZONEMGR_ERROR received error response from zone manager: %1
+This is a debug message output during the processing of a NOTIFY
+request. The zone manager component has been informed of the request,
+but has returned an error response (which is included in the message). The
+NOTIFY request will not be honored.
+
+
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index f46752a..f29fd05 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -20,6 +20,7 @@
#include <cassert>
#include <iostream>
#include <vector>
+#include <memory>
#include <boost/bind.hpp>
@@ -31,7 +32,8 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
+
#include <dns/edns.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
@@ -42,6 +44,7 @@
#include <dns/rrset.h>
#include <dns/rrttl.h>
#include <dns/message.h>
+#include <dns/tsig.h>
#include <datasrc/query.h>
#include <datasrc/data_source.h>
@@ -52,10 +55,11 @@
#include <xfr/xfrout_client.h>
#include <auth/common.h>
-#include <auth/config.h>
+#include <auth/auth_config.h>
#include <auth/auth_srv.h>
#include <auth/query.h>
#include <auth/statistics.h>
+#include <auth/auth_log.h>
using namespace std;
@@ -63,13 +67,16 @@ using namespace isc;
using namespace isc::cc;
using namespace isc::datasrc;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::auth;
using namespace isc::dns::rdata;
using namespace isc::data;
using namespace isc::config;
using namespace isc::xfr;
-using namespace asiolink;
+using namespace isc::asiolink;
+using namespace isc::asiodns;
using namespace isc::server_common::portconfig;
+using boost::shared_ptr;
class AuthSrvImpl {
private:
@@ -82,11 +89,14 @@ public:
isc::data::ConstElementPtr setDbFile(isc::data::ConstElementPtr config);
bool processNormalQuery(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer);
+ OutputBufferPtr buffer,
+ auto_ptr<TSIGContext> tsig_context);
bool processAxfrQuery(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer);
+ OutputBufferPtr buffer,
+ auto_ptr<TSIGContext> tsig_context);
bool processNotify(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer);
+ OutputBufferPtr buffer,
+ auto_ptr<TSIGContext> tsig_context);
IOService io_service_;
@@ -95,7 +105,6 @@ public:
/// These members are public because AuthSrv accesses them directly.
ModuleCCSession* config_session_;
- bool verbose_mode_;
AbstractSession* xfrin_session_;
/// In-memory data source. Currently class IN only for simplicity.
@@ -113,6 +122,9 @@ public:
/// Addresses we listen on
AddressList listen_addresses_;
+
+ /// The TSIG keyring
+ const shared_ptr<TSIGKeyRing>* keyring_;
private:
std::string db_file_;
@@ -131,11 +143,12 @@ private:
AuthSrvImpl::AuthSrvImpl(const bool use_cache,
AbstractXfroutClient& xfrout_client) :
- config_session_(NULL), verbose_mode_(false),
+ config_session_(NULL),
xfrin_session_(NULL),
memory_datasrc_class_(RRClass::IN()),
statistics_timer_(io_service_),
- counters_(verbose_mode_),
+ counters_(),
+ keyring_(NULL),
xfrout_connected_(false),
xfrout_client_(xfrout_client)
{
@@ -238,7 +251,9 @@ public:
void
makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
- const Rcode& rcode, const bool verbose_mode)
+ const Rcode& rcode,
+ std::auto_ptr<TSIGContext> tsig_context =
+ std::auto_ptr<TSIGContext>())
{
// extract the parameters that should be kept.
// XXX: with the current implementation, it's not easy to set EDNS0
@@ -269,25 +284,16 @@ makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
message->setRcode(rcode);
MessageRenderer renderer(*buffer);
- message->toWire(renderer);
-
- if (verbose_mode) {
- cerr << "[b10-auth] sending an error response (" <<
- renderer.getLength() << " bytes):\n" << message->toText() << endl;
+ if (tsig_context.get() != NULL) {
+ message->toWire(renderer, *tsig_context);
+ } else {
+ message->toWire(renderer);
}
+ LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_ERROR_RESPONSE)
+ .arg(message->toText());
}
}
-void
-AuthSrv::setVerbose(const bool on) {
- impl_->verbose_mode_ = on;
-}
-
-bool
-AuthSrv::getVerbose() const {
- return (impl_->verbose_mode_);
-}
-
IOService&
AuthSrv::getIOService() {
return (impl_->io_service_);
@@ -343,15 +349,12 @@ AuthSrv::setMemoryDataSrc(const isc::dns::RRClass& rrclass,
isc_throw(InvalidParameter,
"Memory data source is not supported for RR class "
<< rrclass);
- }
- if (impl_->verbose_mode_) {
- if (!impl_->memory_datasrc_ && memory_datasrc) {
- cerr << "[b10-auth] Memory data source is enabled for class "
- << rrclass << endl;
- } else if (impl_->memory_datasrc_ && !memory_datasrc) {
- cerr << "[b10-auth] Memory data source is disabled for class "
- << rrclass << endl;
- }
+ } else if (!impl_->memory_datasrc_ && memory_datasrc) {
+ LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_MEM_DATASRC_ENABLED)
+ .arg(rrclass);
+ } else if (impl_->memory_datasrc_ && !memory_datasrc) {
+ LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_MEM_DATASRC_DISABLED)
+ .arg(rrclass);
}
impl_->memory_datasrc_ = memory_datasrc;
}
@@ -373,18 +376,13 @@ AuthSrv::setStatisticsTimerInterval(uint32_t interval) {
}
if (interval == 0) {
impl_->statistics_timer_.cancel();
+ LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_STATS_TIMER_DISABLED);
} else {
impl_->statistics_timer_.setup(boost::bind(&AuthSrv::submitStatistics,
this),
interval * 1000);
- }
- if (impl_->verbose_mode_) {
- if (interval == 0) {
- cerr << "[b10-auth] Disabled statistics timer" << endl;
- } else {
- cerr << "[b10-auth] Set statistics timer to " << interval
- << " seconds" << endl;
- }
+ LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_STATS_TIMER_SET)
+ .arg(interval);
}
}
@@ -401,17 +399,13 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
// Ignore all responses.
if (message->getHeaderFlag(Message::HEADERFLAG_QR)) {
- if (impl_->verbose_mode_) {
- cerr << "[b10-auth] received unexpected response, ignoring"
- << endl;
- }
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_RECEIVED);
server->resume(false);
return;
}
} catch (const Exception& ex) {
- if (impl_->verbose_mode_) {
- cerr << "[b10-auth] DNS packet exception: " << ex.what() << endl;
- }
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_HEADER_PARSE_FAIL)
+ .arg(ex.what());
server->resume(false);
return;
}
@@ -420,52 +414,63 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
// Parse the message.
message->fromWire(request_buffer);
} catch (const DNSProtocolError& error) {
- if (impl_->verbose_mode_) {
- cerr << "[b10-auth] returning " << error.getRcode().toText()
- << ": " << error.what() << endl;
- }
- makeErrorMessage(message, buffer, error.getRcode(),
- impl_->verbose_mode_);
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PROTOCOL_ERROR)
+ .arg(error.getRcode().toText()).arg(error.what());
+ makeErrorMessage(message, buffer, error.getRcode());
server->resume(true);
return;
} catch (const Exception& ex) {
- if (impl_->verbose_mode_) {
- cerr << "[b10-auth] returning SERVFAIL: " << ex.what() << endl;
- }
- makeErrorMessage(message, buffer, Rcode::SERVFAIL(),
- impl_->verbose_mode_);
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PARSE_ERROR)
+ .arg(ex.what());
+ makeErrorMessage(message, buffer, Rcode::SERVFAIL());
server->resume(true);
return;
} // other exceptions will be handled at a higher layer.
- if (impl_->verbose_mode_) {
- cerr << "[b10-auth] received a message:\n" << message->toText() << endl;
- }
+ LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_PACKET_RECEIVED)
+ .arg(message->toText());
// Perform further protocol-level validation.
+ // TSIG first
+ // If this is set to something, we know we need to answer with TSIG as well
+ std::auto_ptr<TSIGContext> tsig_context;
+ const TSIGRecord* tsig_record(message->getTSIGRecord());
+ TSIGError tsig_error(TSIGError::NOERROR());
+
+ // Do we do TSIG?
+ // The keyring can be null if we're in test
+ if (impl_->keyring_ != NULL && tsig_record != NULL) {
+ tsig_context.reset(new TSIGContext(tsig_record->getName(),
+ tsig_record->getRdata().
+ getAlgorithm(),
+ **impl_->keyring_));
+ tsig_error = tsig_context->verify(tsig_record, io_message.getData(),
+ io_message.getDataSize());
+ }
bool sendAnswer = true;
- if (message->getOpcode() == Opcode::NOTIFY()) {
- sendAnswer = impl_->processNotify(io_message, message, buffer);
+ if (tsig_error != TSIGError::NOERROR()) {
+ makeErrorMessage(message, buffer, tsig_error.toRcode(), tsig_context);
+ } else if (message->getOpcode() == Opcode::NOTIFY()) {
+ sendAnswer = impl_->processNotify(io_message, message, buffer,
+ tsig_context);
} else if (message->getOpcode() != Opcode::QUERY()) {
- if (impl_->verbose_mode_) {
- cerr << "[b10-auth] unsupported opcode" << endl;
- }
- makeErrorMessage(message, buffer, Rcode::NOTIMP(),
- impl_->verbose_mode_);
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
+ .arg(message->getOpcode().toText());
+ makeErrorMessage(message, buffer, Rcode::NOTIMP(), tsig_context);
} else if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
- makeErrorMessage(message, buffer, Rcode::FORMERR(),
- impl_->verbose_mode_);
+ makeErrorMessage(message, buffer, Rcode::FORMERR(), tsig_context);
} else {
ConstQuestionPtr question = *message->beginQuestion();
const RRType &qtype = question->getType();
if (qtype == RRType::AXFR()) {
- sendAnswer = impl_->processAxfrQuery(io_message, message, buffer);
+ sendAnswer = impl_->processAxfrQuery(io_message, message, buffer,
+ tsig_context);
} else if (qtype == RRType::IXFR()) {
- makeErrorMessage(message, buffer, Rcode::NOTIMP(),
- impl_->verbose_mode_);
+ makeErrorMessage(message, buffer, Rcode::NOTIMP(), tsig_context);
} else {
- sendAnswer = impl_->processNormalQuery(io_message, message, buffer);
+ sendAnswer = impl_->processNormalQuery(io_message, message, buffer,
+ tsig_context);
}
}
@@ -474,7 +479,8 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
bool
AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer)
+ OutputBufferPtr buffer,
+ auto_ptr<TSIGContext> tsig_context)
{
ConstEDNSPtr remote_edns = message->getEDNS();
const bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
@@ -508,11 +514,8 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
data_sources_.doQuery(query);
}
} catch (const Exception& ex) {
- if (verbose_mode_) {
- cerr << "[b10-auth] Internal error, returning SERVFAIL: " <<
- ex.what() << endl;
- }
- makeErrorMessage(message, buffer, Rcode::SERVFAIL(), verbose_mode_);
+ LOG_ERROR(auth_logger, AUTH_PROCESS_FAIL).arg(ex.what());
+ makeErrorMessage(message, buffer, Rcode::SERVFAIL());
return (true);
}
@@ -520,29 +523,28 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
const bool udp_buffer =
(io_message.getSocket().getProtocol() == IPPROTO_UDP);
renderer.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
- message->toWire(renderer);
-
- if (verbose_mode_) {
- cerr << "[b10-auth] sending a response ("
- << renderer.getLength()
- << " bytes):\n" << message->toText() << endl;
+ if (tsig_context.get() != NULL) {
+ message->toWire(renderer, *tsig_context);
+ } else {
+ message->toWire(renderer);
}
+ LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_NORMAL_RESPONSE)
+ .arg(renderer.getLength()).arg(message->toText());
return (true);
}
bool
AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer)
+ OutputBufferPtr buffer,
+ auto_ptr<TSIGContext> tsig_context)
{
// Increment query counter.
incCounter(io_message.getSocket().getProtocol());
if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
- if (verbose_mode_) {
- cerr << "[b10-auth] AXFR query over UDP isn't allowed" << endl;
- }
- makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_);
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_UDP);
+ makeErrorMessage(message, buffer, Rcode::FORMERR(), tsig_context);
return (true);
}
@@ -565,11 +567,9 @@ AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
xfrout_connected_ = false;
}
- if (verbose_mode_) {
- cerr << "[b10-auth] Error in handling XFR request: " << err.what()
- << endl;
- }
- makeErrorMessage(message, buffer, Rcode::SERVFAIL(), verbose_mode_);
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_ERROR)
+ .arg(err.what());
+ makeErrorMessage(message, buffer, Rcode::SERVFAIL(), tsig_context);
return (true);
}
@@ -578,25 +578,22 @@ AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
bool
AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer)
+ OutputBufferPtr buffer,
+ std::auto_ptr<TSIGContext> tsig_context)
{
// The incoming notify must contain exactly one question for SOA of the
// zone name.
if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
- if (verbose_mode_) {
- cerr << "[b10-auth] invalid number of questions in notify: "
- << message->getRRCount(Message::SECTION_QUESTION) << endl;
- }
- makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_);
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_QUESTIONS)
+ .arg(message->getRRCount(Message::SECTION_QUESTION));
+ makeErrorMessage(message, buffer, Rcode::FORMERR(), tsig_context);
return (true);
}
ConstQuestionPtr question = *message->beginQuestion();
if (question->getType() != RRType::SOA()) {
- if (verbose_mode_) {
- cerr << "[b10-auth] invalid question RR type in notify: "
- << question->getType() << endl;
- }
- makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_);
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_RRTYPE)
+ .arg(question->getType().toText());
+ makeErrorMessage(message, buffer, Rcode::FORMERR(), tsig_context);
return (true);
}
@@ -612,10 +609,7 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
// silent about such cases, but there doesn't seem to be anything we can
// improve at the primary server side by sending an error anyway.
if (xfrin_session_ == NULL) {
- if (verbose_mode_) {
- cerr << "[b10-auth] "
- "session interface for xfrin is not available" << endl;
- }
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NO_XFRIN);
return (false);
}
@@ -641,16 +635,12 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
int rcode;
parsed_answer = parseAnswer(rcode, answer);
if (rcode != 0) {
- if (verbose_mode_) {
- cerr << "[b10-auth] failed to notify Zonemgr: "
- << parsed_answer->str() << endl;
- }
+ LOG_ERROR(auth_logger, AUTH_ZONEMGR_ERROR)
+ .arg(parsed_answer->str());
return (false);
}
} catch (const Exception& ex) {
- if (verbose_mode_) {
- cerr << "[b10-auth] failed to notify Zonemgr: " << ex.what() << endl;
- }
+ LOG_ERROR(auth_logger, AUTH_ZONEMGR_COMMS).arg(ex.what());
return (false);
}
@@ -659,7 +649,11 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
message->setRcode(Rcode::NOERROR());
MessageRenderer renderer(*buffer);
- message->toWire(renderer);
+ if (tsig_context.get() != NULL) {
+ message->toWire(renderer, *tsig_context);
+ } else {
+ message->toWire(renderer);
+ }
return (true);
}
@@ -706,10 +700,7 @@ AuthSrvImpl::setDbFile(ConstElementPtr config) {
} else {
return (answer);
}
-
- if (verbose_mode_) {
- cerr << "[b10-auth] Data source database file: " << db_file_ << endl;
- }
+ LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_DATA_SOURCE).arg(db_file_);
// create SQL data source
// Note: the following step is tricky to be exception-safe and to ensure
@@ -739,9 +730,7 @@ AuthSrv::updateConfig(ConstElementPtr new_config) {
}
return (impl_->setDbFile(new_config));
} catch (const isc::Exception& error) {
- if (impl_->verbose_mode_) {
- cerr << "[b10-auth] error: " << error.what() << endl;
- }
+ LOG_ERROR(auth_logger, AUTH_CONFIG_UPDATE_FAIL).arg(error.what());
return (isc::config::createAnswer(1, error.what()));
}
}
@@ -766,6 +755,11 @@ AuthSrv::setListenAddresses(const AddressList& addresses) {
}
void
-AuthSrv::setDNSService(asiolink::DNSService& dnss) {
+AuthSrv::setDNSService(isc::asiodns::DNSService& dnss) {
dnss_ = &dnss;
}
+
+void
+AuthSrv::setTSIGKeyRing(const shared_ptr<TSIGKeyRing>* keyring) {
+ impl_->keyring_ = keyring;
+}
diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h
index 8253c85..7eede97 100644
--- a/src/bin/auth/auth_srv.h
+++ b/src/bin/auth/auth_srv.h
@@ -23,6 +23,15 @@
#include <cc/data.h>
#include <config/ccsession.h>
+#include <dns/message.h>
+#include <util/buffer.h>
+
+#include <asiodns/dns_server.h>
+#include <asiodns/dns_lookup.h>
+#include <asiodns/dns_answer.h>
+#include <asiolink/io_message.h>
+#include <asiolink/io_service.h>
+#include <asiolink/simple_callback.h>
#include <asiolink/asiolink.h>
#include <server_common/portconfig.h>
@@ -35,6 +44,9 @@ class MemoryDataSrc;
namespace xfr {
class AbstractXfroutClient;
}
+namespace dns {
+class TSIGKeyRing;
+}
}
@@ -107,31 +119,10 @@ public:
/// \param server Pointer to the \c DNSServer
///
/// \throw isc::Unexpected Protocol type of \a message is unexpected
- void processMessage(const asiolink::IOMessage& io_message,
+ void processMessage(const isc::asiolink::IOMessage& io_message,
isc::dns::MessagePtr message,
- isc::dns::OutputBufferPtr buffer,
- asiolink::DNSServer* server);
-
- /// \brief Set verbose flag
- ///
- /// \param on The new value of the verbose flag
-
- /// \brief Enable or disable verbose logging.
- ///
- /// This method never throws an exception.
- ///
- /// \param on \c true to enable verbose logging; \c false to disable
- /// verbose logging.
- void setVerbose(const bool on);
-
- /// \brief Returns the logging verbosity of the \c AuthSrv object.
- ///
- /// This method never throws an exception.
- ///
- /// \return \c true if verbose logging is enabled; otherwise \c false.
-
- /// \brief Get the current value of the verbose flag
- bool getVerbose() const;
+ isc::util::OutputBufferPtr buffer,
+ isc::asiodns::DNSServer* server);
/// \brief Updates the data source for the \c AuthSrv object.
///
@@ -193,16 +184,16 @@ public:
void setConfigSession(isc::config::ModuleCCSession* config_session);
/// \brief Return this object's ASIO IO Service queue
- asiolink::IOService& getIOService();
+ isc::asiolink::IOService& getIOService();
/// \brief Return pointer to the DNS Lookup callback function
- asiolink::DNSLookup* getDNSLookupProvider() const { return (dns_lookup_); }
+ isc::asiodns::DNSLookup* getDNSLookupProvider() const { return (dns_lookup_); }
/// \brief Return pointer to the DNS Answer callback function
- asiolink::DNSAnswer* getDNSAnswerProvider() const { return (dns_answer_); }
+ isc::asiodns::DNSAnswer* getDNSAnswerProvider() const { return (dns_answer_); }
/// \brief Return pointer to the Checkin callback function
- asiolink::SimpleCallback* getCheckinProvider() const { return (checkin_); }
+ isc::asiolink::SimpleCallback* getCheckinProvider() const { return (checkin_); }
/// \brief Set or update the size (number of slots) of hot spot cache.
///
@@ -363,15 +354,23 @@ public:
const;
/// \brief Assign an ASIO DNS Service queue to this Auth object
- void setDNSService(asiolink::DNSService& dnss);
+ void setDNSService(isc::asiodns::DNSService& dnss);
+ /// \brief Sets the keyring used for verifying and signing
+ ///
+ /// The parameter is pointer to shared pointer, because the automatic
+ /// reloading routines of tsig keys replace the actual keyring object.
+ /// It is expected the pointer will point to some statically-allocated
+ /// object, it doesn't take ownership of it.
+ void setTSIGKeyRing(const boost::shared_ptr<isc::dns::TSIGKeyRing>*
+ keyring);
private:
AuthSrvImpl* impl_;
- asiolink::SimpleCallback* checkin_;
- asiolink::DNSLookup* dns_lookup_;
- asiolink::DNSAnswer* dns_answer_;
- asiolink::DNSService* dnss_;
+ isc::asiolink::SimpleCallback* checkin_;
+ isc::asiodns::DNSLookup* dns_lookup_;
+ isc::asiodns::DNSAnswer* dns_answer_;
+ isc::asiodns::DNSService* dnss_;
};
#endif // __AUTH_SRV_H
diff --git a/src/bin/auth/benchmarks/Makefile.am b/src/bin/auth/benchmarks/Makefile.am
index 3078dd5..cf3fe4a 100644
--- a/src/bin/auth/benchmarks/Makefile.am
+++ b/src/bin/auth/benchmarks/Makefile.am
@@ -10,8 +10,11 @@ noinst_PROGRAMS = query_bench
query_bench_SOURCES = query_bench.cc
query_bench_SOURCES += ../query.h ../query.cc
query_bench_SOURCES += ../auth_srv.h ../auth_srv.cc
-query_bench_SOURCES += ../config.h ../config.cc
+query_bench_SOURCES += ../auth_config.h ../auth_config.cc
query_bench_SOURCES += ../statistics.h ../statistics.cc
+query_bench_SOURCES += ../auth_log.h ../auth_log.cc
+
+nodist_query_bench_SOURCES = ../auth_messages.h ../auth_messages.cc
query_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
query_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
@@ -22,6 +25,8 @@ query_bench_LDADD += $(top_builddir)/src/lib/cc/libcc.la
query_bench_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
query_bench_LDADD += $(top_builddir)/src/lib/log/liblog.la
query_bench_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
+query_bench_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
query_bench_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
query_bench_LDADD += $(SQLITE_LIBS)
+
diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc
index 5e69134..ba2e7b2 100644
--- a/src/bin/auth/benchmarks/query_bench.cc
+++ b/src/bin/auth/benchmarks/query_bench.cc
@@ -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 <stdlib.h>
#include <iostream>
@@ -22,7 +24,7 @@
#include <bench/benchmark.h>
#include <bench/benchmark_util.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/message.h>
#include <dns/name.h>
#include <dns/question.h>
@@ -31,9 +33,10 @@
#include <xfr/xfrout_client.h>
#include <auth/auth_srv.h>
-#include <auth/config.h>
+#include <auth/auth_config.h>
#include <auth/query.h>
+#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
using namespace std;
@@ -41,9 +44,11 @@ using namespace isc;
using namespace isc::data;
using namespace isc::auth;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::xfr;
using namespace isc::bench;
-using namespace asiolink;
+using namespace isc::asiodns;
+using namespace isc::asiolink;
namespace {
// Commonly used constant:
diff --git a/src/bin/auth/command.cc b/src/bin/auth/command.cc
index eafcae8..fe3d729 100644
--- a/src/bin/auth/command.cc
+++ b/src/bin/auth/command.cc
@@ -27,16 +27,18 @@
#include <config/ccsession.h>
+#include <auth/auth_log.h>
#include <auth/auth_srv.h>
#include <auth/command.h>
-using namespace std;
-using boost::shared_ptr;
using boost::scoped_ptr;
-using namespace isc::dns;
+using boost::shared_ptr;
+using namespace isc::auth;
+using namespace isc::config;
using namespace isc::data;
using namespace isc::datasrc;
-using namespace isc::config;
+using namespace isc::dns;
+using namespace std;
namespace {
/// An exception that is thrown if an error occurs while handling a command
@@ -115,9 +117,7 @@ public:
class SendStatsCommand : public AuthCommand {
public:
virtual void exec(AuthSrv& server, isc::data::ConstElementPtr) {
- if (server.getVerbose()) {
- cerr << "[b10-auth] command 'sendstats' received" << endl;
- }
+ LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_RECEIVED_SENDSTATS);
server.submitStatistics();
}
};
@@ -140,11 +140,8 @@ public:
oldzone->getOrigin()));
newzone->load(oldzone->getFileName());
oldzone->swap(*newzone);
-
- if (server.getVerbose()) {
- cerr << "[b10-auth] Loaded zone '" << newzone->getOrigin()
- << "'/" << newzone->getClass() << endl;
- }
+ LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_LOAD_ZONE)
+ .arg(newzone->getOrigin()).arg(newzone->getClass());
}
private:
@@ -164,10 +161,7 @@ private:
ConstElementPtr datasrc_elem = args->get("datasrc");
if (datasrc_elem) {
if (datasrc_elem->stringValue() == "sqlite3") {
- if (server.getVerbose()) {
- cerr << "[b10-auth] Nothing to do for loading sqlite3"
- << endl;
- }
+ LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_SQLITE3);
return (false);
} else if (datasrc_elem->stringValue() != "memory") {
// (note: at this point it's guaranteed that datasrc_elem
@@ -233,18 +227,13 @@ ConstElementPtr
execAuthServerCommand(AuthSrv& server, const string& command_id,
ConstElementPtr args)
{
- if (server.getVerbose()) {
- cerr << "[b10-auth] Received '" << command_id << "' command" << endl;
- }
-
+ LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_RECEIVED_COMMAND).arg(command_id);
try {
scoped_ptr<AuthCommand>(createAuthCommand(command_id))->exec(server,
args);
} catch (const isc::Exception& ex) {
- if (server.getVerbose()) {
- cerr << "[b10-auth] Command '" << command_id
- << "' execution failed: " << ex.what() << endl;
- }
+ LOG_ERROR(auth_logger, AUTH_COMMAND_FAILED).arg(command_id)
+ .arg(ex.what());
return (createAnswer(1, ex.what()));
}
diff --git a/src/bin/auth/common.cc b/src/bin/auth/common.cc
new file mode 100644
index 0000000..35381a1
--- /dev/null
+++ b/src/bin/auth/common.cc
@@ -0,0 +1,36 @@
+// Copyright (C) 2009-2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <auth/common.h>
+#include <auth/spec_config.h>
+#include <stdlib.h>
+
+using std::string;
+
+string getXfroutSocketPath() {
+ if (getenv("B10_FROM_BUILD") != NULL) {
+ if (getenv("B10_FROM_SOURCE_LOCALSTATEDIR")) {
+ return (string(getenv("B10_FROM_SOURCE_LOCALSTATEDIR")) +
+ "/auth_xfrout_conn");
+ } else {
+ return (string(getenv("B10_FROM_BUILD")) + "/auth_xfrout_conn");
+ }
+ } else {
+ if (getenv("BIND10_XFROUT_SOCKET_FILE")) {
+ return (getenv("BIND10_XFROUT_SOCKET_FILE"));
+ } else {
+ return (UNIX_SOCKET_FILE);
+ }
+ }
+}
diff --git a/src/bin/auth/common.h b/src/bin/auth/common.h
index 6af09fb..b913593 100644
--- a/src/bin/auth/common.h
+++ b/src/bin/auth/common.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2009-2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -29,6 +29,15 @@ public:
{}
};
+/// \short Get the path of socket to talk to xfrout
+///
+/// It takes some environment variables into account (B10_FROM_BUILD,
+/// B10_FROM_SOURCE_LOCALSTATEDIR and BIND10_XFROUT_SOCKET_FILE). It
+/// also considers the installation prefix.
+///
+/// The logic should be the same as in b10-xfrout, so they find each other.
+std::string getXfroutSocketPath();
+
#endif // __COMMON_H
// Local Variables:
diff --git a/src/bin/auth/config.cc b/src/bin/auth/config.cc
deleted file mode 100644
index f289ca0..0000000
--- a/src/bin/auth/config.cc
+++ /dev/null
@@ -1,347 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <boost/foreach.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include <dns/name.h>
-#include <dns/rrclass.h>
-
-#include <cc/data.h>
-
-#include <datasrc/memory_datasrc.h>
-#include <datasrc/zonetable.h>
-
-#include <auth/auth_srv.h>
-#include <auth/config.h>
-#include <auth/common.h>
-
-#include <server_common/portconfig.h>
-
-using namespace std;
-using boost::shared_ptr;
-using namespace isc::dns;
-using namespace isc::data;
-using namespace isc::datasrc;
-using namespace isc::server_common::portconfig;
-
-namespace {
-// Forward declaration
-AuthConfigParser*
-createAuthConfigParser(AuthSrv& server, const std::string& config_id,
- bool internal);
-
-/// A derived \c AuthConfigParser class for the "datasources" configuration
-/// identifier.
-class DatasourcesConfig : public AuthConfigParser {
-public:
- DatasourcesConfig(AuthSrv& server) : server_(server) {}
- virtual void build(ConstElementPtr config_value);
- virtual void commit();
-private:
- AuthSrv& server_;
- vector<shared_ptr<AuthConfigParser> > datasources_;
- set<string> configured_sources_;
-};
-
-void
-DatasourcesConfig::build(ConstElementPtr config_value) {
- BOOST_FOREACH(ConstElementPtr datasrc_elem, config_value->listValue()) {
- // The caller is supposed to perform syntax-level checks, but we'll
- // do minimum level of validation ourselves so that we won't crash due
- // to a buggy application.
- ConstElementPtr datasrc_type = datasrc_elem->get("type");
- if (!datasrc_type) {
- isc_throw(AuthConfigError, "Missing data source type");
- }
-
- if (configured_sources_.find(datasrc_type->stringValue()) !=
- configured_sources_.end()) {
- isc_throw(AuthConfigError, "Data source type '" <<
- datasrc_type->stringValue() << "' already configured");
- }
-
- shared_ptr<AuthConfigParser> datasrc_config =
- shared_ptr<AuthConfigParser>(
- createAuthConfigParser(server_, string("datasources/") +
- datasrc_type->stringValue(),
- true));
- datasrc_config->build(datasrc_elem);
- datasources_.push_back(datasrc_config);
-
- configured_sources_.insert(datasrc_type->stringValue());
- }
-}
-
-void
-DatasourcesConfig::commit() {
- // XXX a short term workaround: clear all data sources and then reset
- // to new ones so that we can remove data sources that don't exist in
- // the new configuration and have been used in the server.
- // This could be inefficient and requires knowledge about
- // server implementation details, and isn't scalable wrt the number of
- // data source types, and should eventually be improved.
- // Currently memory data source for class IN is the only possibility.
- server_.setMemoryDataSrc(RRClass::IN(), AuthSrv::MemoryDataSrcPtr());
-
- BOOST_FOREACH(shared_ptr<AuthConfigParser> datasrc_config, datasources_) {
- datasrc_config->commit();
- }
-}
-
-/// A derived \c AuthConfigParser class for the memory type datasource
-/// configuration. It does not correspond to the configuration syntax;
-/// it's instantiated for internal use.
-class MemoryDatasourceConfig : public AuthConfigParser {
-public:
- MemoryDatasourceConfig(AuthSrv& server) :
- server_(server),
- rrclass_(0) // XXX: dummy initial value
- {}
- virtual void build(ConstElementPtr config_value);
- virtual void commit() {
- server_.setMemoryDataSrc(rrclass_, memory_datasrc_);
- }
-private:
- AuthSrv& server_;
- RRClass rrclass_;
- AuthSrv::MemoryDataSrcPtr memory_datasrc_;
-};
-
-void
-MemoryDatasourceConfig::build(ConstElementPtr config_value) {
- // XXX: apparently we cannot retrieve the default RR class from the
- // module spec. As a temporary workaround we hardcode the default value.
- ConstElementPtr rrclass_elem = config_value->get("class");
- rrclass_ = RRClass(rrclass_elem ? rrclass_elem->stringValue() : "IN");
-
- // We'd eventually optimize building zones (in case of reloading) by
- // selectively loading fresh zones. Right now we simply check the
- // RR class is supported by the server implementation.
- server_.getMemoryDataSrc(rrclass_);
- memory_datasrc_ = AuthSrv::MemoryDataSrcPtr(new MemoryDataSrc());
-
- ConstElementPtr zones_config = config_value->get("zones");
- if (!zones_config) {
- // XXX: Like the RR class, we cannot retrieve the default value here,
- // so we assume an empty zone list in this case.
- return;
- }
-
- BOOST_FOREACH(ConstElementPtr zone_config, zones_config->listValue()) {
- ConstElementPtr origin = zone_config->get("origin");
- if (!origin) {
- isc_throw(AuthConfigError, "Missing zone origin");
- }
- ConstElementPtr file = zone_config->get("file");
- if (!file) {
- isc_throw(AuthConfigError, "Missing zone file for zone: "
- << origin->str());
- }
- shared_ptr<MemoryZone> new_zone(new MemoryZone(rrclass_,
- Name(origin->stringValue())));
- const result::Result result = memory_datasrc_->addZone(new_zone);
- if (result == result::EXIST) {
- isc_throw(AuthConfigError, "zone "<< origin->str()
- << " already exists");
- }
-
- /*
- * TODO: Once we have better reloading of configuration (something
- * else than throwing everything away and loading it again), we will
- * need the load method to be split into some kind of build and
- * commit/abort parts.
- */
- new_zone->load(file->stringValue());
- }
-}
-
-/// A derived \c AuthConfigParser class for the "statistics-internal"
-/// configuration identifier.
-class StatisticsIntervalConfig : public AuthConfigParser {
-public:
- StatisticsIntervalConfig(AuthSrv& server) :
- server_(server), interval_(0)
- {}
- virtual void build(ConstElementPtr config_value) {
- const int32_t config_interval = config_value->intValue();
- if (config_interval < 0) {
- isc_throw(AuthConfigError, "Negative statistics interval value: "
- << config_interval);
- }
- if (config_interval > 86400) {
- isc_throw(AuthConfigError, "Statistics interval value "
- << config_interval
- << " must be equal to or shorter than 86400");
- }
- interval_ = config_interval;
- }
- virtual void commit() {
- // setStatisticsTimerInterval() is not 100% exception free. But
- // exceptions should happen only in a very rare situation, so we
- // let them be thrown and subsequently regard them as a fatal error.
- server_.setStatisticsTimerInterval(interval_);
- }
-private:
- AuthSrv& server_;
- uint32_t interval_;
-};
-
-/// A special parser for testing: it throws from commit() despite the
-/// suggested convention of the class interface.
-class ThrowerCommitConfig : public AuthConfigParser {
-public:
- virtual void build(ConstElementPtr) {} // ignore param, do nothing
- virtual void commit() {
- throw 10;
- }
-};
-
-/**
- * \brief Configuration parser for listen_on.
- *
- * It parses and sets the listening addresses of the server.
- *
- * It acts in unusual way. Since actually binding (changing) the sockets
- * is an operation that is expected to throw often, it shouldn't happen
- * in commit. Thefere we do it in build. But if the config is not committed
- * then, we would have it wrong. So we store the old addresses and if
- * commit is not called before destruction of the object, we return the
- * old addresses (which is the same kind of dangerous operation, but it is
- * expected that if we just managed to bind some and had the old ones binded
- * before, it should work).
- *
- * We might do something better in future (like open only the ports that are
- * extra, put them in in commit and close the old ones), but that's left out
- * for now.
- */
-class ListenAddressConfig : public AuthConfigParser {
-public:
- ListenAddressConfig(AuthSrv& server) :
- server_(server)
- { }
- ~ ListenAddressConfig() {
- if (rollbackAddresses_.get() != NULL) {
- server_.setListenAddresses(*rollbackAddresses_);
- }
- }
-private:
- typedef auto_ptr<AddressList> AddrListPtr;
-public:
- virtual void build(ConstElementPtr config) {
- AddressList newAddresses = parseAddresses(config, "listen_on");
- AddrListPtr old(new AddressList(server_.getListenAddresses()));
- server_.setListenAddresses(newAddresses);
- /*
- * Set the rollback addresses only after successful setting of the
- * new addresses, so we don't try to rollback if the setup is
- * unsuccessful (the above can easily throw).
- */
- rollbackAddresses_ = old;
- }
- virtual void commit() {
- rollbackAddresses_.release();
- }
-private:
- AuthSrv& server_;
- /**
- * This is the old address list, if we expect to roll back. When we commit,
- * this is set to NULL.
- */
- AddrListPtr rollbackAddresses_;
-};
-
-// This is a generalized version of create function that can create
-// an AuthConfigParser object for "internal" use.
-AuthConfigParser*
-createAuthConfigParser(AuthSrv& server, const std::string& config_id,
- bool internal)
-{
- // For the initial implementation we use a naive if-else blocks for
- // simplicity. In future we'll probably generalize it using map-like
- // data structure, and may even provide external register interface so
- // that it can be dynamically customized.
- if (config_id == "datasources") {
- return (new DatasourcesConfig(server));
- } else if (config_id == "statistics-interval") {
- return (new StatisticsIntervalConfig(server));
- } else if (internal && config_id == "datasources/memory") {
- return (new MemoryDatasourceConfig(server));
- } else if (config_id == "listen_on") {
- return (new ListenAddressConfig(server));
- } else if (config_id == "_commit_throw") {
- // This is for testing purpose only and should not appear in the
- // actual configuration syntax. While this could crash the caller
- // as a result, the server implementation is expected to perform
- // syntax level validation and should be safe in practice. In future,
- // we may introduce dynamic registration of configuration parsers,
- // and then this test can be done in a cleaner and safer way.
- return (new ThrowerCommitConfig());
- } else {
- isc_throw(AuthConfigError, "Unknown configuration identifier: " <<
- config_id);
- }
-}
-} // end of unnamed namespace
-
-AuthConfigParser*
-createAuthConfigParser(AuthSrv& server, const std::string& config_id) {
- return (createAuthConfigParser(server, config_id, false));
-}
-
-void
-configureAuthServer(AuthSrv& server, ConstElementPtr config_set) {
- if (!config_set) {
- isc_throw(AuthConfigError,
- "Null pointer is passed to configuration parser");
- }
-
- typedef shared_ptr<AuthConfigParser> ParserPtr;
- vector<ParserPtr> parsers;
- typedef pair<string, ConstElementPtr> ConfigPair;
- try {
- BOOST_FOREACH(ConfigPair config_pair, config_set->mapValue()) {
- // We should eventually integrate the sqlite3 DB configuration to
- // this framework, but to minimize diff we begin with skipping that
- // part.
- if (config_pair.first == "database_file") {
- continue;
- }
-
- ParserPtr parser(createAuthConfigParser(server,
- config_pair.first));
- parser->build(config_pair.second);
- parsers.push_back(parser);
- }
- } catch (const AuthConfigError& ex) {
- throw; // simply rethrowing it
- } catch (const isc::Exception& ex) {
- isc_throw(AuthConfigError, "Server configuration failed: " <<
- ex.what());
- }
-
- try {
- BOOST_FOREACH(ParserPtr parser, parsers) {
- parser->commit();
- }
- } catch (...) {
- throw FatalError("Unrecoverable error: "
- "a configuration parser threw in commit");
- }
-}
diff --git a/src/bin/auth/config.h b/src/bin/auth/config.h
deleted file mode 100644
index 6f18810..0000000
--- a/src/bin/auth/config.h
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <string>
-
-#include <exceptions/exceptions.h>
-
-#include <cc/data.h>
-
-#ifndef __CONFIG_H
-#define __CONFIG_H 1
-
-class AuthSrv;
-
-/// An exception that is thrown if an error occurs while configuring an
-/// \c AuthSrv object.
-class AuthConfigError : public isc::Exception {
-public:
- AuthConfigError(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
-};
-
-/// The abstract base class that represents a single configuration identifier
-/// for an \c AuthSrv object.
-///
-/// In general, each top level configuration identifier for \c AuthSrv is
-/// expected to have its own derived class of this base class.
-/// For example, for the following configuration:
-/// \code { "param1": 10, "param2": { "subparam1": "foo", "subparam2": [] } }
-/// \endcode
-/// "param1" and "param2" are top level identifiers, and would correspond to
-/// derived \c AuthConfigParser classes.
-/// "subparam1" and/or "subparam2" may also have dedicated derived classes.
-///
-/// These derived classes are hidden inside the implementation; applications
-/// are not expected to (and in fact cannot) instantiate them directly.
-///
-/// Each derived class is generally expected to be constructed with an
-/// \c AuthSrv object to be configured and hold a reference to the server
-/// throughout the configuration process.
-/// For each derived class, the \c build() method parses the configuration
-/// value for the corresponding identifier and prepares new configuration
-/// value(s) to be applied to the server. This method may throw an exception
-/// when it encounters an error.
-/// The \c commit() method actually applies the new configuration value
-/// to the server. It's basically not expected to throw an exception;
-/// any configuration operations that can fail (such as ones involving
-/// resource allocation) should be done in \c build().
-///
-/// When the destructor is called before \c commit(), the destructor is
-/// supposed to make sure the state of the \c AuthSrv object is the same
-/// as that before it starts building the configuration value.
-/// If \c build() doesn't change the server state (which is recommended)
-/// the destructor doesn't have to do anything special in this regard.
-/// This is a key to ensure the strong exception guarantee (see also
-/// the description of \c configureAuthServer()).
-class AuthConfigParser {
- ///
- /// \name Constructors and Destructor
- ///
- /// Note: The copy constructor and the assignment operator are
- /// intentionally defined as private to make it explicit that this is a
- /// pure base class.
- //@{
-private:
- AuthConfigParser(const AuthConfigParser& source);
- AuthConfigParser& operator=(const AuthConfigParser& source);
-protected:
- /// \brief The default constructor.
- ///
- /// This is intentionally defined as \c protected as this base class should
- /// never be instantiated (except as part of a derived class).
- AuthConfigParser() {}
-public:
- /// The destructor.
- virtual ~AuthConfigParser() {}
- //@}
-
- /// Prepare configuration value.
- ///
- /// This method parses the "value part" of the configuration identifier
- /// that corresponds to this derived class and prepares a new value to
- /// apply to the server.
- /// In the above example, the derived class for the identifier "param1"
- /// would be passed an data \c Element storing an integer whose value
- /// is 10, and would record that value internally;
- /// the derived class for the identifier "param2" would be passed a
- /// map element and (after parsing) convert it into some internal
- /// data structure.
- ///
- /// This method must validate the given value both in terms of syntax
- /// and semantics of the configuration, so that the server will be
- /// validly configured at the time of \c commit(). Note: the given
- /// configuration value is normally syntactically validated, but the
- /// \c build() implementation must also expect invalid input. If it
- /// detects an error it may throw an exception of a derived class
- /// of \c isc::Exception.
- ///
- /// Preparing a configuration value will often require resource
- /// allocation. If it fails, it may throw a corresponding standard
- /// exception.
- ///
- /// This method is not expected to be called more than once. Although
- /// multiple calls are not prohibited by the interface, the behavior
- /// is undefined.
- ///
- /// \param config_value The configuration value for the identifier
- /// corresponding to the derived class.
- virtual void build(isc::data::ConstElementPtr config_value) = 0;
-
- /// Apply the prepared configuration value to the server.
- ///
- /// This method is expected to be exception free, and, as a consequence,
- /// it should normally not involve resource allocation.
- /// Typically it would simply perform exception free assignment or swap
- /// operation on the value prepared in \c build().
- /// In some cases, however, it may be very difficult to meet this
- /// condition in a realistic way, while the failure case should really
- /// be very rare. In such a case it may throw, and, if the parser is
- /// called via \c configureAuthServer(), the caller will convert the
- /// exception as a fatal error.
- ///
- /// This method is expected to be called after \c build(), and only once.
- /// The result is undefined otherwise.
- virtual void commit() = 0;
-};
-
-/// Configure an \c AuthSrv object with a set of configuration values.
-///
-/// This function parses configuration information stored in \c config_set
-/// and configures the \c server by applying the configuration to it.
-/// It provides the strong exception guarantee as long as the underlying
-/// derived class implementations of \c AuthConfigParser meet the assumption,
-/// that is, it ensures that either configuration is fully applied or the
-/// state of the server is intact.
-///
-/// If a syntax or semantics level error happens during the configuration
-/// (such as malformed configuration or invalid configuration parameter),
-/// this function throws an exception of class \c AuthConfigError.
-/// If the given configuration requires resource allocation and it fails,
-/// a corresponding standard exception will be thrown.
-/// Other exceptions may also be thrown, depending on the implementation of
-/// the underlying derived class of \c AuthConfigError.
-/// In any case the strong guarantee is provided as described above except
-/// in the very rare cases where the \c commit() method of a parser throws
-/// an exception. If that happens this function converts the exception
-/// into a \c FatalError exception and rethrows it. This exception is
-/// expected to be caught at the highest level of the application to terminate
-/// the program gracefully.
-///
-/// \param server The \c AuthSrv object to be configured.
-/// \param config_set A JSON style configuration to apply to \c server.
-void configureAuthServer(AuthSrv& server,
- isc::data::ConstElementPtr config_set);
-
-/// Create a new \c AuthConfigParser object for a given configuration
-/// identifier.
-///
-/// It internally identifies an appropriate derived class for the given
-/// identifier and creates a new instance of that class. The caller can
-/// then configure the \c server regarding the identifier by calling
-/// the \c build() and \c commit() methods of the returned object.
-///
-/// In practice, this function is only expected to be used as a backend of
-/// \c configureAuthServer() and is not supposed to be called directly
-/// by applications. It is publicly available mainly for testing purposes.
-/// When called directly, the created object must be deleted by the caller.
-/// Note: this means if this module and the caller use incompatible sets of
-/// new/delete, it may cause unexpected strange failure. We could avoid that
-/// by providing a separate deallocation function or by using a smart pointer,
-/// but since the expected usage of this function is very limited (i.e. for
-/// our own testing purposes) it would be an overkilling. We therefore prefer
-/// simplicity and keeping the interface intuitive.
-///
-/// If the resource allocation for the new object fails, a corresponding
-/// standard exception will be thrown. Otherwise this function is not
-/// expected to throw an exception, unless the constructor of the underlying
-/// derived class implementation (unexpectedly) throws.
-///
-/// \param server The \c AuthSrv object to be configured.
-/// \param config_id The configuration identifier for which a parser object
-/// is to be created.
-/// \return A pointer to an \c AuthConfigParser object.
-AuthConfigParser* createAuthConfigParser(AuthSrv& server,
- const std::string& config_id);
-
-#endif // __CONFIG_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc
index 0701b94..c8f6762 100644
--- a/src/bin/auth/main.cc
+++ b/src/bin/auth/main.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2009-2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -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 <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
@@ -25,7 +27,8 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
+
#include <dns/message.h>
#include <dns/messagerenderer.h>
@@ -37,25 +40,30 @@
#include <auth/spec_config.h>
#include <auth/common.h>
-#include <auth/config.h>
+#include <auth/auth_config.h>
#include <auth/command.h>
#include <auth/change_user.h>
#include <auth/auth_srv.h>
+#include <auth/auth_log.h>
+#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
-#include <log/dummylog.h>
+#include <log/logger_support.h>
+#include <server_common/keyring.h>
using namespace std;
-using namespace isc::data;
+using namespace isc::asiodns;
+using namespace isc::asiolink;
+using namespace isc::auth;
using namespace isc::cc;
using namespace isc::config;
+using namespace isc::data;
using namespace isc::dns;
+using namespace isc::log;
+using namespace isc::util;
using namespace isc::xfr;
-using namespace asiolink;
namespace {
-bool verbose_mode = false;
-
/* need global var for config/command handlers.
* todo: turn this around, and put handlers in the authserver
* class itself? */
@@ -81,6 +89,7 @@ usage() {
cerr << "\t-v: verbose output" << endl;
exit(1);
}
+
} // end of anonymous namespace
int
@@ -88,6 +97,7 @@ main(int argc, char* argv[]) {
int ch;
const char* uid = NULL;
bool cache = true;
+ bool verbose = false;
while ((ch = getopt(argc, argv, ":nu:v")) != -1) {
switch (ch) {
@@ -98,8 +108,7 @@ main(int argc, char* argv[]) {
uid = optarg;
break;
case 'v':
- verbose_mode = true;
- isc::log::denabled = true;
+ verbose = true;
break;
case '?':
default:
@@ -111,6 +120,11 @@ main(int argc, char* argv[]) {
usage();
}
+ // Initialize logging. If verbose, we'll use maximum verbosity.
+ isc::log::initLogger("b10-auth",
+ (verbose ? isc::log::DEBUG : isc::log::INFO),
+ isc::log::MAX_DEBUG_LEVEL, NULL);
+
int ret = 0;
// XXX: we should eventually pass io_service here.
@@ -120,19 +134,7 @@ main(int argc, char* argv[]) {
bool xfrin_session_established = false; // XXX (see Trac #287)
bool statistics_session_established = false; // XXX (see Trac #287)
ModuleCCSession* config_session = NULL;
- string xfrout_socket_path;
- if (getenv("B10_FROM_BUILD") != NULL) {
- if (getenv("B10_FROM_SOURCE_LOCALSTATEDIR")) {
- xfrout_socket_path = string("B10_FROM_SOURCE_LOCALSTATEDIR") +
- "/auth_xfrout_conn";
- } else {
- xfrout_socket_path = string(getenv("B10_FROM_BUILD")) +
- "/auth_xfrout_conn";
- }
- } else {
- xfrout_socket_path = UNIX_SOCKET_FILE;
- }
- XfroutClient xfrout_client(xfrout_socket_path);
+ XfroutClient xfrout_client(getXfroutSocketPath());
try {
string specfile;
if (getenv("B10_FROM_BUILD")) {
@@ -143,8 +145,7 @@ main(int argc, char* argv[]) {
}
auth_server = new AuthSrv(cache, xfrout_client);
- auth_server->setVerbose(verbose_mode);
- cout << "[b10-auth] Server created." << endl;
+ LOG_INFO(auth_logger, AUTH_SERVER_CREATED);
SimpleCallback* checkin = auth_server->getCheckinProvider();
IOService& io_service = auth_server->getIOService();
@@ -153,31 +154,32 @@ main(int argc, char* argv[]) {
DNSService dns_service(io_service, checkin, lookup, answer);
auth_server->setDNSService(dns_service);
- cout << "[b10-auth] DNSServices created." << endl;
+ LOG_DEBUG(auth_logger, DBG_AUTH_START, AUTH_DNS_SERVICES_CREATED);
cc_session = new Session(io_service.get_io_service());
- cout << "[b10-auth] Configuration session channel created." << endl;
+ LOG_DEBUG(auth_logger, DBG_AUTH_START, AUTH_CONFIG_CHANNEL_CREATED);
+ // We delay starting listening to new commands/config just before we
+ // go into the main loop to avoid confusion due to mixture of
+ // synchronous and asynchronous operations (this would happen in
+ // initializing TSIG keys below). Until then all operations on the
+ // CC session will take place synchronously.
config_session = new ModuleCCSession(specfile, *cc_session,
my_config_handler,
- my_command_handler);
- cout << "[b10-auth] Configuration channel established." << endl;
-
- if (uid != NULL) {
- changeUser(uid);
- }
+ my_command_handler, false);
+ LOG_DEBUG(auth_logger, DBG_AUTH_START, AUTH_CONFIG_CHANNEL_ESTABLISHED);
xfrin_session = new Session(io_service.get_io_service());
- cout << "[b10-auth] Xfrin session channel created." << endl;
+ LOG_DEBUG(auth_logger, DBG_AUTH_START, AUTH_XFRIN_CHANNEL_CREATED);
xfrin_session->establish(NULL);
xfrin_session_established = true;
- cout << "[b10-auth] Xfrin session channel established." << endl;
+ LOG_DEBUG(auth_logger, DBG_AUTH_START, AUTH_XFRIN_CHANNEL_ESTABLISHED);
statistics_session = new Session(io_service.get_io_service());
- cout << "[b10-auth] Statistics session channel created." << endl;
+ LOG_DEBUG(auth_logger, DBG_AUTH_START, AUTH_STATS_CHANNEL_CREATED);
statistics_session->establish(NULL);
statistics_session_established = true;
- cout << "[b10-auth] Statistics session channel established." << endl;
+ LOG_DEBUG(auth_logger, DBG_AUTH_START, AUTH_STATS_CHANNEL_ESTABLISHED);
auth_server->setXfrinSession(xfrin_session);
auth_server->setStatisticsSession(statistics_session);
@@ -186,15 +188,34 @@ main(int argc, char* argv[]) {
// all initial configurations, but as a short term workaround we
// handle the traditional "database_file" setup by directly calling
// updateConfig().
+ // if server load configure failed, we won't exit, give user second
+ // chance to correct the configure.
auth_server->setConfigSession(config_session);
- configureAuthServer(*auth_server, config_session->getFullConfig());
- auth_server->updateConfig(ElementPtr());
+ try {
+ configureAuthServer(*auth_server, config_session->getFullConfig());
+ auth_server->updateConfig(ElementPtr());
+ } catch (const AuthConfigError& ex) {
+ LOG_ERROR(auth_logger, AUTH_CONFIG_LOAD_FAIL).arg(ex.what());
+ }
+
+ if (uid != NULL) {
+ changeUser(uid);
+ }
+
+ LOG_DEBUG(auth_logger, DBG_AUTH_START, AUTH_LOAD_TSIG);
+ isc::server_common::initKeyring(*config_session);
+ auth_server->setTSIGKeyRing(&isc::server_common::keyring);
+
+ // Now start asynchronous read.
+ config_session->start();
+ LOG_DEBUG(auth_logger, DBG_AUTH_START, AUTH_CONFIG_CHANNEL_STARTED);
- cout << "[b10-auth] Server started." << endl;
+ // Successfully initialized.
+ LOG_INFO(auth_logger, AUTH_SERVER_STARTED);
io_service.run();
} catch (const std::exception& ex) {
- cerr << "[b10-auth] Server failed: " << ex.what() << endl;
+ LOG_FATAL(auth_logger, AUTH_SERVER_FAILED).arg(ex.what());
ret = 1;
}
diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc
index e936c97..323f890 100644
--- a/src/bin/auth/query.cc
+++ b/src/bin/auth/query.cc
@@ -210,6 +210,8 @@ Query::process() const {
// into answer section.
BOOST_FOREACH(RRsetPtr rrset, *target) {
response_.addRRset(Message::SECTION_ANSWER, rrset);
+ // Handle additional for answer section
+ getAdditional(*result.zone, *rrset.get());
}
} else {
response_.addRRset(Message::SECTION_ANSWER,
diff --git a/src/bin/auth/statistics.cc b/src/bin/auth/statistics.cc
index e68793c..76e5007 100644
--- a/src/bin/auth/statistics.cc
+++ b/src/bin/auth/statistics.cc
@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <auth/statistics.h>
+#include <auth/auth_log.h>
#include <cc/data.h>
#include <cc/session.h>
@@ -20,6 +21,8 @@
#include <sstream>
#include <iostream>
+using namespace isc::auth;
+
// TODO: We need a namespace ("auth_server"?) to hold
// AuthSrv and AuthCounters.
@@ -29,10 +32,7 @@ private:
AuthCountersImpl(const AuthCountersImpl& source);
AuthCountersImpl& operator=(const AuthCountersImpl& source);
public:
- // References verbose_mode flag in AuthSrvImpl
- // TODO: Fix this short term workaround for logging
- // after we have logging framework
- AuthCountersImpl(const bool& verbose_mode);
+ AuthCountersImpl();
~AuthCountersImpl();
void inc(const AuthCounters::CounterType type);
bool submitStatistics() const;
@@ -42,15 +42,13 @@ public:
private:
std::vector<uint64_t> counters_;
isc::cc::AbstractSession* statistics_session_;
- const bool& verbose_mode_;
};
-AuthCountersImpl::AuthCountersImpl(const bool& verbose_mode) :
+AuthCountersImpl::AuthCountersImpl() :
// initialize counter
// size: AuthCounters::COUNTER_TYPES, initial value: 0
counters_(AuthCounters::COUNTER_TYPES, 0),
- statistics_session_(NULL),
- verbose_mode_(verbose_mode)
+ statistics_session_(NULL)
{}
AuthCountersImpl::~AuthCountersImpl()
@@ -64,11 +62,7 @@ AuthCountersImpl::inc(const AuthCounters::CounterType type) {
bool
AuthCountersImpl::submitStatistics() const {
if (statistics_session_ == NULL) {
- if (verbose_mode_) {
- std::cerr << "[b10-auth] "
- << "session interface for statistics"
- << " is not available" << std::endl;
- }
+ LOG_ERROR(auth_logger, AUTH_NO_STATS_SESSION);
return (false);
}
std::stringstream statistics_string;
@@ -91,26 +85,14 @@ AuthCountersImpl::submitStatistics() const {
const int seq =
statistics_session_->group_sendmsg(statistics_element, "Stats");
isc::data::ConstElementPtr env, answer;
- if (verbose_mode_) {
- std::cerr << "[b10-auth] "
- << "send statistics data" << std::endl;
- }
// TODO: parse and check response from statistics module
// currently it just returns empty message
statistics_session_->group_recvmsg(env, answer, false, seq);
} catch (const isc::cc::SessionError& ex) {
- if (verbose_mode_) {
- std::cerr << "[b10-auth] "
- << "communication error in sending statistics data: "
- << ex.what() << std::endl;
- }
+ LOG_ERROR(auth_logger, AUTH_STATS_COMMS).arg(ex.what());
return (false);
} catch (const isc::cc::SessionTimeout& ex) {
- if (verbose_mode_) {
- std::cerr << "[b10-auth] "
- << "timeout happened while sending statistics data: "
- << ex.what() << std::endl;
- }
+ LOG_ERROR(auth_logger, AUTH_STATS_TIMEOUT).arg(ex.what());
return (false);
}
return (true);
@@ -129,8 +111,7 @@ AuthCountersImpl::getCounter(const AuthCounters::CounterType type) const {
return (counters_.at(type));
}
-AuthCounters::AuthCounters(const bool& verbose_mode) :
- impl_(new AuthCountersImpl(verbose_mode))
+AuthCounters::AuthCounters() : impl_(new AuthCountersImpl())
{}
AuthCounters::~AuthCounters() {
diff --git a/src/bin/auth/statistics.h b/src/bin/auth/statistics.h
index 9e5240e..5bf6436 100644
--- a/src/bin/auth/statistics.h
+++ b/src/bin/auth/statistics.h
@@ -61,15 +61,10 @@ public:
};
/// The constructor.
///
- /// \param verbose_mode reference to verbose_mode_ of AuthSrvImpl
- ///
/// This constructor is mostly exception free. But it may still throw
/// a standard exception if memory allocation fails inside the method.
///
- /// \todo Fix this short term workaround for logging
- /// after we have logging framework.
- ///
- AuthCounters(const bool& verbose_mode);
+ AuthCounters();
/// The destructor.
///
/// This method never throws an exception.
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am
index 7d489a1..71520c2 100644
--- a/src/bin/auth/tests/Makefile.am
+++ b/src/bin/auth/tests/Makefile.am
@@ -1,4 +1,5 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_builddir)/src/bin # for generated spec_config.h header
AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
AM_CPPFLAGS += $(BOOST_INCLUDES)
@@ -16,22 +17,29 @@ CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
+
TESTS += run_unittests
run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += ../auth_srv.h ../auth_srv.cc
+run_unittests_SOURCES += ../auth_log.h ../auth_log.cc
run_unittests_SOURCES += ../query.h ../query.cc
run_unittests_SOURCES += ../change_user.h ../change_user.cc
-run_unittests_SOURCES += ../config.h ../config.cc
+run_unittests_SOURCES += ../auth_config.h ../auth_config.cc
run_unittests_SOURCES += ../command.h ../command.cc
+run_unittests_SOURCES += ../common.h ../common.cc
run_unittests_SOURCES += ../statistics.h ../statistics.cc
run_unittests_SOURCES += auth_srv_unittest.cc
run_unittests_SOURCES += config_unittest.cc
run_unittests_SOURCES += command_unittest.cc
+run_unittests_SOURCES += common_unittest.cc
run_unittests_SOURCES += query_unittest.cc
run_unittests_SOURCES += change_user_unittest.cc
run_unittests_SOURCES += statistics_unittest.cc
run_unittests_SOURCES += run_unittests.cc
+
+nodist_run_unittests_SOURCES = ../auth_messages.h ../auth_messages.cc
+
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
@@ -39,6 +47,7 @@ run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
@@ -47,6 +56,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index 379342e..2b20d65 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -16,6 +16,8 @@
#include <vector>
+#include <boost/shared_ptr.hpp>
+
#include <gtest/gtest.h>
#include <dns/message.h>
@@ -25,8 +27,10 @@
#include <dns/rrtype.h>
#include <dns/rrttl.h>
#include <dns/rdataclass.h>
+#include <dns/tsig.h>
#include <server_common/portconfig.h>
+#include <server_common/keyring.h>
#include <datasrc/memory_datasrc.h>
#include <auth/auth_srv.h>
@@ -41,13 +45,16 @@
using namespace std;
using namespace isc::cc;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
using namespace isc::data;
using namespace isc::xfr;
-using namespace asiolink;
+using namespace isc::asiodns;
+using namespace isc::asiolink;
using namespace isc::testutils;
using namespace isc::server_common::portconfig;
using isc::UnitTestUtil;
+using boost::shared_ptr;
namespace {
const char* const CONFIG_TESTDB =
@@ -183,15 +190,6 @@ TEST_F(AuthSrvTest, unsupportedRequest) {
unsupportedRequest();
}
-// Simple API check
-TEST_F(AuthSrvTest, verbose) {
- EXPECT_FALSE(server.getVerbose());
- server.setVerbose(true);
- EXPECT_TRUE(server.getVerbose());
- server.setVerbose(false);
- EXPECT_FALSE(server.getVerbose());
-}
-
// Multiple questions. Should result in FORMERR.
TEST_F(AuthSrvTest, multiQuestion) {
multiQuestion();
@@ -240,6 +238,139 @@ TEST_F(AuthSrvTest, AXFRSuccess) {
EXPECT_TRUE(xfrout.isConnected());
}
+// Try giving the server a TSIG signed request and see it can anwer signed as
+// well
+TEST_F(AuthSrvTest, TSIGSigned) {
+ // Prepare key, the client message, etc
+ const TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
+ TSIGContext context(key);
+ UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
+ Name("version.bind"), RRClass::CH(), RRType::TXT());
+ createRequestPacket(request_message, IPPROTO_UDP, &context);
+
+ // Run the message through the server
+ shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+ keyring->add(key);
+ server.setTSIGKeyRing(&keyring);
+ server.processMessage(*io_message, parse_message, response_obuffer,
+ &dnsserv);
+
+ // What did we get?
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+ opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
+ // We need to parse the message ourself, or getTSIGRecord won't work
+ InputBuffer ib(response_obuffer->getData(), response_obuffer->getLength());
+ Message m(Message::PARSE);
+ m.fromWire(ib);
+
+ const TSIGRecord* tsig = m.getTSIGRecord();
+ ASSERT_TRUE(tsig != NULL) << "Missing TSIG signature";
+ TSIGError error(context.verify(tsig, response_obuffer->getData(),
+ response_obuffer->getLength()));
+ EXPECT_EQ(TSIGError::NOERROR(), error) <<
+ "The server signed the response, but it doesn't seem to be valid";
+}
+
+// Give the server a signed request, but don't give it the key. It will
+// not be able to verify it, returning BADKEY
+TEST_F(AuthSrvTest, TSIGSignedBadKey) {
+ TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
+ TSIGContext context(key);
+ UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
+ Name("version.bind"), RRClass::CH(), RRType::TXT());
+ createRequestPacket(request_message, IPPROTO_UDP, &context);
+
+ // Process the message, but use a different key there
+ shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+ server.setTSIGKeyRing(&keyring);
+ server.processMessage(*io_message, parse_message, response_obuffer,
+ &dnsserv);
+
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, TSIGError::BAD_KEY().toRcode(),
+ opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+ // We need to parse the message ourself, or getTSIGRecord won't work
+ InputBuffer ib(response_obuffer->getData(), response_obuffer->getLength());
+ Message m(Message::PARSE);
+ m.fromWire(ib);
+
+ const TSIGRecord* tsig = m.getTSIGRecord();
+ ASSERT_TRUE(tsig != NULL) <<
+ "Missing TSIG signature (we should have one even at error)";
+ EXPECT_EQ(TSIGError::BAD_KEY_CODE, tsig->getRdata().getError());
+ EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
+ "It should be unsigned with this error";
+}
+
+// Give the server a signed request, but signed by a different key
+// (with the same name). It should return BADSIG
+TEST_F(AuthSrvTest, TSIGBadSig) {
+ TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
+ TSIGContext context(key);
+ UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
+ Name("version.bind"), RRClass::CH(), RRType::TXT());
+ createRequestPacket(request_message, IPPROTO_UDP, &context);
+
+ // Process the message, but use a different key there
+ shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+ keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
+ server.setTSIGKeyRing(&keyring);
+ server.processMessage(*io_message, parse_message, response_obuffer,
+ &dnsserv);
+
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, TSIGError::BAD_SIG().toRcode(),
+ opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+ // We need to parse the message ourself, or getTSIGRecord won't work
+ InputBuffer ib(response_obuffer->getData(), response_obuffer->getLength());
+ Message m(Message::PARSE);
+ m.fromWire(ib);
+
+ const TSIGRecord* tsig = m.getTSIGRecord();
+ ASSERT_TRUE(tsig != NULL) <<
+ "Missing TSIG signature (we should have one even at error)";
+ EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig->getRdata().getError());
+ EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
+ "It should be unsigned with this error";
+}
+
+// Give the server a signed unsupported request with a bad signature.
+// This checks the server first verifies the signature before anything
+// else.
+TEST_F(AuthSrvTest, TSIGCheckFirst) {
+ TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
+ TSIGContext context(key);
+ // Pass a wrong opcode there. The server shouldn't know what to do
+ // about it.
+ UnitTestUtil::createRequestMessage(request_message, Opcode::RESERVED14(),
+ default_qid, Name("version.bind"),
+ RRClass::CH(), RRType::TXT());
+ createRequestPacket(request_message, IPPROTO_UDP, &context);
+
+ // Process the message, but use a different key there
+ shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+ keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
+ server.setTSIGKeyRing(&keyring);
+ server.processMessage(*io_message, parse_message, response_obuffer,
+ &dnsserv);
+
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, TSIGError::BAD_SIG().toRcode(),
+ Opcode::RESERVED14().getCode(), QR_FLAG, 0, 0, 0, 0);
+ // We need to parse the message ourself, or getTSIGRecord won't work
+ InputBuffer ib(response_obuffer->getData(), response_obuffer->getLength());
+ Message m(Message::PARSE);
+ m.fromWire(ib);
+
+ const TSIGRecord* tsig = m.getTSIGRecord();
+ ASSERT_TRUE(tsig != NULL) <<
+ "Missing TSIG signature (we should have one even at error)";
+ EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig->getRdata().getError());
+ EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
+ "It should be unsigned with this error";
+}
+
TEST_F(AuthSrvTest, AXFRConnectFail) {
EXPECT_FALSE(xfrout.isConnected()); // check prerequisite
xfrout.disableConnect();
diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc
index f788d9e..2fc8052 100644
--- a/src/bin/auth/tests/command_unittest.cc
+++ b/src/bin/auth/tests/command_unittest.cc
@@ -12,6 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <config.h>
+
#include <cassert>
#include <cstdlib>
#include <string>
@@ -32,7 +34,7 @@
#include <datasrc/memory_datasrc.h>
#include <auth/auth_srv.h>
-#include <auth/config.h>
+#include <auth/auth_config.h>
#include <auth/command.h>
#include <asiolink/asiolink.h>
@@ -46,9 +48,9 @@ using namespace isc::datasrc;
using namespace isc::config;
namespace {
-class AuthConmmandTest : public ::testing::Test {
+class AuthCommandTest : public ::testing::Test {
protected:
- AuthConmmandTest() : server(false, xfrout), rcode(-1) {
+ AuthCommandTest() : server(false, xfrout), rcode(-1) {
server.setStatisticsSession(&statistics_session);
}
void checkAnswer(const int expected_code) {
@@ -65,14 +67,14 @@ public:
void stopServer(); // need to be public for boost::bind
};
-TEST_F(AuthConmmandTest, unknownCommand) {
+TEST_F(AuthCommandTest, unknownCommand) {
result = execAuthServerCommand(server, "no_such_command",
ConstElementPtr());
parseAnswer(rcode, result);
EXPECT_EQ(1, rcode);
}
-TEST_F(AuthConmmandTest, DISABLED_unexpectedException) {
+TEST_F(AuthCommandTest, DISABLED_unexpectedException) {
// execAuthServerCommand() won't catch standard exceptions.
// Skip this test for now: ModuleCCSession doesn't seem to validate
// commands.
@@ -81,7 +83,7 @@ TEST_F(AuthConmmandTest, DISABLED_unexpectedException) {
runtime_error);
}
-TEST_F(AuthConmmandTest, sendStatistics) {
+TEST_F(AuthCommandTest, sendStatistics) {
result = execAuthServerCommand(server, "sendstats", ConstElementPtr());
// Just check some message has been sent. Detailed tests specific to
// statistics are done in its own tests.
@@ -90,15 +92,15 @@ TEST_F(AuthConmmandTest, sendStatistics) {
}
void
-AuthConmmandTest::stopServer() {
+AuthCommandTest::stopServer() {
result = execAuthServerCommand(server, "shutdown", ConstElementPtr());
parseAnswer(rcode, result);
assert(rcode == 0); // make sure the test stops when something is wrong
}
-TEST_F(AuthConmmandTest, shutdown) {
- asiolink::IntervalTimer itimer(server.getIOService());
- itimer.setup(boost::bind(&AuthConmmandTest::stopServer, this), 1);
+TEST_F(AuthCommandTest, shutdown) {
+ isc::asiolink::IntervalTimer itimer(server.getIOService());
+ itimer.setup(boost::bind(&AuthCommandTest::stopServer, this), 1);
server.getIOService().run();
EXPECT_EQ(0, rcode);
}
@@ -163,7 +165,7 @@ newZoneChecks(AuthSrv& server) {
find(Name("ns.test2.example"), RRType::AAAA()).code);
}
-TEST_F(AuthConmmandTest, loadZone) {
+TEST_F(AuthCommandTest, loadZone) {
configureZones(server);
ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR
@@ -180,7 +182,7 @@ TEST_F(AuthConmmandTest, loadZone) {
newZoneChecks(server);
}
-TEST_F(AuthConmmandTest, loadBrokenZone) {
+TEST_F(AuthCommandTest, loadBrokenZone) {
configureZones(server);
ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR
@@ -193,7 +195,7 @@ TEST_F(AuthConmmandTest, loadBrokenZone) {
zoneChecks(server); // zone shouldn't be replaced
}
-TEST_F(AuthConmmandTest, loadUnreadableZone) {
+TEST_F(AuthCommandTest, loadUnreadableZone) {
configureZones(server);
// install the zone file as unreadable
@@ -207,7 +209,7 @@ TEST_F(AuthConmmandTest, loadUnreadableZone) {
zoneChecks(server); // zone shouldn't be replaced
}
-TEST_F(AuthConmmandTest, loadZoneWithoutDataSrc) {
+TEST_F(AuthCommandTest, loadZoneWithoutDataSrc) {
// try to execute load command without configuring the zone beforehand.
// it should fail.
result = execAuthServerCommand(server, "loadzone",
@@ -216,7 +218,7 @@ TEST_F(AuthConmmandTest, loadZoneWithoutDataSrc) {
checkAnswer(1);
}
-TEST_F(AuthConmmandTest, loadSqlite3DataSrc) {
+TEST_F(AuthCommandTest, loadSqlite3DataSrc) {
// For sqlite3 data source we don't have to do anything (the data source
// (re)loads itself automatically)
result = execAuthServerCommand(server, "loadzone",
@@ -226,7 +228,7 @@ TEST_F(AuthConmmandTest, loadSqlite3DataSrc) {
checkAnswer(0);
}
-TEST_F(AuthConmmandTest, loadZoneInvalidParams) {
+TEST_F(AuthCommandTest, loadZoneInvalidParams) {
configureZones(server);
// null arg
diff --git a/src/bin/auth/tests/common_unittest.cc b/src/bin/auth/tests/common_unittest.cc
new file mode 100644
index 0000000..9b18142
--- /dev/null
+++ b/src/bin/auth/tests/common_unittest.cc
@@ -0,0 +1,96 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+#include <auth/common.h>
+#include <auth/spec_config.h>
+#include <vector>
+#include <string>
+#include <cstdio>
+#include <boost/foreach.hpp>
+
+using std::pair;
+using std::vector;
+using std::string;
+
+namespace {
+
+class Paths : public ::testing::Test {
+private:
+ typedef pair<string, string*> Environ;
+ vector<Environ> restoreEnviron;
+public:
+ void TearDown() {
+ // Restore the original environment
+ BOOST_FOREACH(const Environ &env, restoreEnviron) {
+ if (env.second == NULL) {
+ EXPECT_EQ(0, unsetenv(env.first.c_str())) <<
+ "Couldn't restore environment, results of other tests"
+ "are uncertain";
+ } else {
+ EXPECT_EQ(0, setenv(env.first.c_str(), env.second->c_str(),
+ 1)) << "Couldn't restore environment, "
+ "results of other tests are uncertain";
+ }
+ }
+ }
+protected:
+ // Sets a temporary value into environment. If value is empty, it deletes
+ // the variable from environment (just for simplicity).
+ void setEnv(const string& name, const string& value) {
+ // Backup the original environment
+ char* env(getenv(name.c_str()));
+ restoreEnviron.push_back(Environ(name, env == NULL ? NULL :
+ new string(env)));
+ // Set the new value
+ if (value.empty()) {
+ EXPECT_EQ(0, unsetenv(name.c_str()));
+ } else {
+ EXPECT_EQ(0, setenv(name.c_str(), value.c_str(), 1));
+ }
+ }
+ // Test getXfroutSocketPath under given environment
+ void testXfrout(const string& fromBuild, const string& localStateDir,
+ const string& socketFile, const string& expected)
+ {
+ setEnv("B10_FROM_BUILD", fromBuild);
+ setEnv("B10_FROM_SOURCE_LOCALSTATEDIR", localStateDir);
+ setEnv("BIND10_XFROUT_SOCKET_FILE", socketFile);
+ EXPECT_EQ(expected, getXfroutSocketPath());
+ }
+};
+
+// Test that when we have no special environment, we get the default from prefix
+TEST_F(Paths, xfroutNoEnv) {
+ testXfrout("", "", "", UNIX_SOCKET_FILE);
+}
+
+// Override by B10_FROM_BUILD
+TEST_F(Paths, xfroutFromBuild) {
+ testXfrout("/from/build", "", "/wrong/path",
+ "/from/build/auth_xfrout_conn");
+}
+
+// Override by B10_FROM_SOURCE_LOCALSTATEDIR
+TEST_F(Paths, xfroutLocalStatedir) {
+ testXfrout("/wrong/path", "/state/dir", "/wrong/path",
+ "/state/dir/auth_xfrout_conn");
+}
+
+// Override by BIND10_XFROUT_SOCKET_FILE explicitly
+TEST_F(Paths, xfroutFromEnv) {
+ testXfrout("", "", "/the/path/to/file", "/the/path/to/file");
+}
+
+}
diff --git a/src/bin/auth/tests/config_unittest.cc b/src/bin/auth/tests/config_unittest.cc
index 8cce0af..0890c55 100644
--- a/src/bin/auth/tests/config_unittest.cc
+++ b/src/bin/auth/tests/config_unittest.cc
@@ -26,7 +26,7 @@
#include <xfr/xfrout_client.h>
#include <auth/auth_srv.h>
-#include <auth/config.h>
+#include <auth/auth_config.h>
#include <auth/common.h>
#include <testutils/mockups.h>
@@ -35,7 +35,8 @@
using namespace isc::dns;
using namespace isc::data;
using namespace isc::datasrc;
-using namespace asiolink;
+using namespace isc::asiodns;
+using namespace isc::asiolink;
namespace {
class AuthConfigTest : public ::testing::Test {
@@ -73,6 +74,13 @@ TEST_F(AuthConfigTest, databaseConfig) {
"{\"database_file\": \"should_be_ignored\"}")));
}
+TEST_F(AuthConfigTest, versionConfig) {
+ // make sure it does not throw on 'version'
+ EXPECT_NO_THROW(configureAuthServer(
+ server,
+ Element::fromJSON("{\"version\": 0}")));
+}
+
TEST_F(AuthConfigTest, exceptionGuarantee) {
EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
// This configuration contains an invalid item, which will trigger
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index 05dd748..c68b672 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -341,12 +341,20 @@ TEST_F(QueryTest, apexAnyMatch) {
// in the answer section from the additional.
EXPECT_NO_THROW(Query(memory_datasrc, Name("example.com"),
RRType::ANY(), response).process());
- responseCheck(response, Rcode::NOERROR(), AA_FLAG, 4, 0, 0,
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 4, 0, 3,
"example.com. 3600 IN SOA . . 0 0 0 0 0\n"
"example.com. 3600 IN NS glue.delegation.example.com.\n"
"example.com. 3600 IN NS noglue.example.com.\n"
"example.com. 3600 IN NS example.net.\n",
- NULL, NULL, mock_zone->getOrigin());
+ NULL, ns_addrs_txt, mock_zone->getOrigin());
+}
+
+TEST_F(QueryTest, mxANYMatch) {
+ EXPECT_NO_THROW(Query(memory_datasrc, Name("mx.example.com"),
+ RRType::ANY(), response).process());
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 3, 4,
+ mx_txt, zone_ns_txt,
+ (string(ns_addrs_txt) + string(www_a_txt)).c_str());
}
TEST_F(QueryTest, glueANYMatch) {
diff --git a/src/bin/auth/tests/run_unittests.cc b/src/bin/auth/tests/run_unittests.cc
index 6ae848d..d3bbab7 100644
--- a/src/bin/auth/tests/run_unittests.cc
+++ b/src/bin/auth/tests/run_unittests.cc
@@ -13,6 +13,8 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <log/logger_support.h>
+#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
@@ -21,6 +23,7 @@ main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
+ isc::log::initLogger();
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/bin/auth/tests/statistics_unittest.cc b/src/bin/auth/tests/statistics_unittest.cc
index 062b70d..9a3dded 100644
--- a/src/bin/auth/tests/statistics_unittest.cc
+++ b/src/bin/auth/tests/statistics_unittest.cc
@@ -69,13 +69,12 @@ private:
};
protected:
- AuthCountersTest() : verbose_mode_(false), counters(verbose_mode_) {
+ AuthCountersTest() : counters() {
counters.setStatisticsSession(&statistics_session_);
}
~AuthCountersTest() {
}
MockSession statistics_session_;
- bool verbose_mode_;
AuthCounters counters;
};
diff --git a/src/bin/bind10/Makefile.am b/src/bin/bind10/Makefile.am
index 254875f..126c429 100644
--- a/src/bin/bind10/Makefile.am
+++ b/src/bin/bind10/Makefile.am
@@ -1,16 +1,17 @@
SUBDIRS = . tests
sbin_SCRIPTS = bind10
-CLEANFILES = bind10 bind10.pyc
+CLEANFILES = bind10 bind10.pyc bind10_messages.py bind10_messages.pyc
pkglibexecdir = $(libexecdir)/@PACKAGE@
+pyexec_DATA = bind10_messages.py
bind10dir = $(pkgdatadir)
bind10_DATA = bob.spec
EXTRA_DIST = bob.spec
man_MANS = bind10.8
-EXTRA_DIST += $(man_MANS) bind10.xml
+EXTRA_DIST += $(man_MANS) bind10.xml bind10_messages.mes
if ENABLE_MAN
@@ -19,6 +20,9 @@ bind10.8: bind10.xml
endif
+bind10_messages.py: bind10_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/bind10/bind10_messages.mes
+
# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
bind10: bind10.py
$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
@@ -27,3 +31,8 @@ bind10: bind10.py
pytest:
$(SHELL) tests/bind10_test
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/bind10/bind10.8 b/src/bin/bind10/bind10.8
index a75136b..d5ab905 100644
--- a/src/bin/bind10/bind10.8
+++ b/src/bin/bind10/bind10.8
@@ -2,12 +2,12 @@
.\" Title: bind10
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: February 22, 2011
+.\" Date: March 31, 2011
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "BIND10" "8" "February 22, 2011" "BIND10" "BIND10"
+.TH "BIND10" "8" "March 31, 2011" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -22,7 +22,7 @@
bind10 \- BIND 10 boss process
.SH "SYNOPSIS"
.HP \w'\fBbind10\fR\ 'u
-\fBbind10\fR [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-pretty\-name\ \fR\fB\fIname\fR\fR] [\fB\-\-verbose\fR]
+\fBbind10\fR [\fB\-c\ \fR\fB\fIconfig\-filename\fR\fR] [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-p\ \fR\fB\fIdata_path\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-\-brittle\fR] [\fB\-\-cmdctl\-port\fR\ \fIport\fR] [\fB\-\-config\-file\fR\ \fIconfig\-filename\fR] [\fB\-\-data\-path\fR\ \fIdirectory\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-pid\-file\fR\ \fIfilename\fR] [\fB\-\-pretty\-name\ \fR\fB\fIname\fR\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-verbose\fR]
.SH "DESCRIPTION"
.PP
The
@@ -32,6 +32,34 @@ daemon starts up other BIND 10 required daemons\&. It handles restarting of exit
.PP
The arguments are as follows:
.PP
+\fB\-\-brittle\fR
+.RS 4
+Shutdown if any of the child processes of
+\fBbind10\fR
+exit\&. This is intended to help developers debug the server, and should not be used in production\&.
+.RE
+.PP
+\fB\-c\fR \fIconfig\-filename\fR, \fB\-\-config\-file\fR \fIconfig\-filename\fR
+.RS 4
+The configuration filename to use\&. Can be either absolute or relative to data path\&. In case it is absolute, value of data path is not considered\&.
+.sp
+Defaults to b10\-config\&.db\&.
+.RE
+.PP
+\fB\-\-cmdctl\-port\fR \fIport\fR
+.RS 4
+The
+\fBb10\-cmdctl\fR
+daemon will listen on this port\&. (See
+b10\-cmdctl(8)
+for the default\&.)
+.RE
+.PP
+\fB\-p\fR \fIdirectory\fR, \fB\-\-data\-path\fR \fIdirectory\fR
+.RS 4
+The path where BIND 10 programs look for various data files\&. Currently only b10\-cfgmgr uses it to locate the configuration file, but the usage might be extended for other programs and other types of files\&.
+.RE
+.PP
\fB\-m\fR \fIfile\fR, \fB\-\-msgq\-socket\-file\fR \fIfile\fR
.RS 4
The UNIX domain socket file for the
@@ -57,6 +85,13 @@ to run as\&.
must be initially ran as the root user to use this option\&. The default is to run as the current user\&.
.RE
.PP
+\fB\-\-pid\-file\fR \fIfilename\fR
+.RS 4
+If defined, the PID of the
+\fBbind10\fR
+is stored in this file\&. This is used for testing purposes\&.
+.RE
+.PP
\fB\-\-pretty\-name \fR\fB\fIname\fR\fR
.RS 4
The name this process should have in tools like
diff --git a/src/bin/bind10/bind10.py.in b/src/bin/bind10/bind10.py.in
index ce6e523..6e4997d 100755
--- a/src/bin/bind10/bind10.py.in
+++ b/src/bin/bind10/bind10.py.in
@@ -1,6 +1,6 @@
#!@PYTHON@
-# Copyright (C) 2010 Internet Systems Consortium.
+# Copyright (C) 2010,2011 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
@@ -65,6 +65,16 @@ import posix
import isc.cc
import isc.util.process
import isc.net.parse
+import isc.log
+from bind10_messages import *
+
+isc.log.init("b10-boss")
+logger = isc.log.Logger("boss")
+
+# Pending system-wide debug level definitions, the ones we
+# use here are hardcoded for now
+DBG_PROCESS = 10
+DBG_COMMANDS = 30
# Assign this process some longer name
isc.util.process.rename(sys.argv[0])
@@ -139,7 +149,8 @@ class ProcessInfo:
self.restart_schedule = RestartSchedule()
self.uid = uid
self.username = username
- self._spawn()
+ self.process = None
+ self.pid = None
def _preexec_work(self):
"""Function used before running a program that needs to run as a
@@ -186,6 +197,11 @@ class ProcessInfo:
self.pid = self.process.pid
self.restart_schedule.set_run_start_time()
+ # spawn() and respawn() are the same for now, but in the future they
+ # may have different functionality
+ def spawn(self):
+ self._spawn()
+
def respawn(self):
self._spawn()
@@ -194,19 +210,28 @@ class CChannelConnectError(Exception): pass
class BoB:
"""Boss of BIND class."""
- def __init__(self, msgq_socket_file=None, nocache=False, verbose=False,
- setuid=None, username=None):
+ def __init__(self, msgq_socket_file=None, data_path=None,
+ config_filename=None, nocache=False, verbose=False, setuid=None,
+ username=None, cmdctl_port=None, brittle=False):
"""
Initialize the Boss of BIND. This is a singleton (only one can run).
The msgq_socket_file specifies the UNIX domain socket file that the
msgq process listens on. If verbose is True, then the boss reports
what it is doing.
+
+ Data path and config filename are passed trough to config manager
+ (if provided) and specify the config file to be used.
+
+ The cmdctl_port is passed to cmdctl and specify on which port it
+ should listen.
"""
self.cc_session = None
self.ccs = None
self.cfg_start_auth = True
self.cfg_start_resolver = False
+ self.cfg_start_dhcp6 = False
+ self.cfg_start_dhcp4 = False
self.started_auth_family = False
self.started_resolver_family = False
self.curproc = None
@@ -219,6 +244,10 @@ class BoB:
self.uid = setuid
self.username = username
self.verbose = verbose
+ self.data_path = data_path
+ self.config_filename = config_filename
+ self.cmdctl_port = cmdctl_port
+ self.brittle = brittle
def config_handler(self, new_config):
# If this is initial update, don't do anything now, leave it to startup
@@ -233,8 +262,7 @@ class BoB:
if new_config['start_' + name]:
if not started:
if self.uid is not None:
- sys.stderr.write("[bind10] Starting " + name + " as " +
- "a user, not root. This might fail.\n")
+ logger.info(BIND10_START_AS_NON_ROOT, name)
start()
else:
stop()
@@ -260,9 +288,8 @@ class BoB:
self.started_auth_family = False
# The real code of the config handler function follows here
- if self.verbose:
- sys.stdout.write("[bind10] Handling new configuration: " +
- str(new_config) + "\n")
+ logger.debug(DBG_COMMANDS, BIND10_RECEIVED_NEW_CONFIGURATION,
+ new_config)
start_stop('resolver', self.started_resolver_family, resolver_on,
resolver_off)
start_stop('auth', self.started_auth_family, auth_on, auth_off)
@@ -270,9 +297,16 @@ class BoB:
answer = isc.config.ccsession.create_answer(0)
return answer
+ def get_processes(self):
+ pids = list(self.processes.keys())
+ pids.sort()
+ process_list = [ ]
+ for pid in pids:
+ process_list.append([pid, self.processes[pid].name])
+ return process_list
+
def command_handler(self, command, args):
- if self.verbose:
- sys.stdout.write("[bind10] Boss got command: " + command + "\n")
+ logger.debug(DBG_COMMANDS, BIND10_RECEIVED_COMMAND, command)
answer = isc.config.ccsession.create_answer(1, "command not implemented")
if type(command) != str:
answer = isc.config.ccsession.create_answer(1, "bad command")
@@ -280,8 +314,22 @@ class BoB:
if command == "shutdown":
self.runnable = False
answer = isc.config.ccsession.create_answer(0)
+ elif command == "sendstats":
+ # send statistics data to the stats daemon immediately
+ cmd = isc.config.ccsession.create_command(
+ 'set', { "stats_data": {
+ 'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME)
+ }})
+ seq = self.cc_session.group_sendmsg(cmd, 'Stats')
+ self.cc_session.group_recvmsg(True, seq)
+ answer = isc.config.ccsession.create_answer(0)
+ elif command == "ping":
+ answer = isc.config.ccsession.create_answer(0, "pong")
+ elif command == "show_processes":
+ answer = isc.config.ccsession. \
+ create_answer(0, self.get_processes())
else:
- answer = isc.config.ccsession.create_answer(1,
+ answer = isc.config.ccsession.create_answer(1,
"Unknown command")
return answer
@@ -291,12 +339,10 @@ class BoB:
start, this runs through the list of started processes, killing
each one. It then clears that list.
"""
- if self.verbose:
- sys.stdout.write("[bind10] killing started processes:\n")
+ logger.info(BIND10_KILLING_ALL_PROCESSES)
for pid in self.processes:
- if self.verbose:
- sys.stdout.write("[bind10] - %s\n" % self.processes[pid].name)
+ logger.info(BIND10_KILL_PROCESS, self.processes[pid].name)
self.processes[pid].process.kill()
self.processes = {}
@@ -310,23 +356,20 @@ class BoB:
xfrin/xfrout and zone manager as we don't need to start those if we
are not running the authoritative server.)
"""
- if self.verbose:
- sys.stdout.write("[bind10] Reading Boss configuration:\n")
+ logger.info(BIND10_READING_BOSS_CONFIGURATION)
config_data = self.ccs.get_full_config()
self.cfg_start_auth = config_data.get("start_auth")
self.cfg_start_resolver = config_data.get("start_resolver")
- if self.verbose:
- sys.stdout.write("[bind10] - start_auth: %s\n" %
- str(self.cfg_start_auth))
- sys.stdout.write("[bind10] - start_resolver: %s\n" %
- str(self.cfg_start_resolver))
+ logger.info(BIND10_CONFIGURATION_START_AUTH, self.cfg_start_auth)
+ logger.info(BIND10_CONFIGURATION_START_RESOLVER, self.cfg_start_resolver)
def log_starting(self, process, port = None, address = None):
"""
A convenience function to output a "Starting xxx" message if the
- verbose option is set. Putting this into a separate method ensures
+ logging is set to DEBUG with debuglevel DBG_PROCESS or higher.
+ Putting this into a separate method ensures
that the output form is consistent across all processes.
The process name (passed as the first argument) is put into
@@ -336,13 +379,14 @@ class BoB:
appended to the message (if present).
"""
self.curproc = process
- if self.verbose:
- sys.stdout.write("[bind10] Starting %s" % self.curproc)
- if port is not None:
- sys.stdout.write(" on port %d" % port)
- if address is not None:
- sys.stdout.write(" (address %s)" % str(address))
- sys.stdout.write("\n")
+ if port is None and address is None:
+ logger.info(BIND10_STARTING_PROCESS, self.curproc)
+ elif address is None:
+ logger.info(BIND10_STARTING_PROCESS_PORT, self.curproc,
+ port)
+ else:
+ logger.info(BIND10_STARTING_PROCESS_PORT_ADDRESS,
+ self.curproc, address, port)
def log_started(self, pid = None):
"""
@@ -350,11 +394,10 @@ class BoB:
message. As with starting_message(), this ensures a consistent
format.
"""
- if self.verbose:
- sys.stdout.write("[bind10] Started %s" % self.curproc)
- if pid is not None:
- sys.stdout.write(" (PID %d)" % pid)
- sys.stdout.write("\n")
+ if pid is None:
+ logger.debug(DBG_PROCESS, BIND10_STARTED_PROCESS, self.curproc)
+ else:
+ logger.debug(DBG_PROCESS, BIND10_STARTED_PROCESS_PID, self.curproc, pid)
# The next few methods start the individual processes of BIND-10. They
# are called via start_all_processes(). If any fail, an exception is
@@ -369,6 +412,7 @@ class BoB:
c_channel = ProcessInfo("b10-msgq", ["b10-msgq"], c_channel_env,
True, not self.verbose, uid=self.uid,
username=self.username)
+ c_channel.spawn()
self.processes[c_channel.pid] = c_channel
self.log_started(c_channel.pid)
@@ -390,9 +434,15 @@ class BoB:
Starts the configuration manager process
"""
self.log_starting("b10-cfgmgr")
- bind_cfgd = ProcessInfo("b10-cfgmgr", ["b10-cfgmgr"],
+ args = ["b10-cfgmgr"]
+ if self.data_path is not None:
+ args.append("--data-path=" + self.data_path)
+ if self.config_filename is not None:
+ args.append("--config-filename=" + self.config_filename)
+ bind_cfgd = ProcessInfo("b10-cfgmgr", args,
c_channel_env, uid=self.uid,
username=self.username)
+ bind_cfgd.spawn()
self.processes[bind_cfgd.pid] = bind_cfgd
self.log_started(bind_cfgd.pid)
@@ -411,7 +461,9 @@ class BoB:
"""
self.log_starting("ccsession")
self.ccs = isc.config.ModuleCCSession(SPECFILE_LOCATION,
- self.config_handler, self.command_handler)
+ self.config_handler,
+ self.command_handler,
+ None, True)
self.ccs.start()
self.log_started()
@@ -427,6 +479,7 @@ class BoB:
"""
self.log_starting(name, port, address)
newproc = ProcessInfo(name, args, c_channel_env)
+ newproc.spawn()
self.processes[newproc.pid] = newproc
self.log_started(newproc.pid)
@@ -499,9 +552,20 @@ class BoB:
def start_stats(self, c_channel_env):
self.start_simple("b10-stats", c_channel_env)
+ def start_stats_httpd(self, c_channel_env):
+ self.start_simple("b10-stats-httpd", c_channel_env)
+
+ def start_dhcp6(self, c_channel_env):
+ self.start_simple("b10-dhcp6", c_channel_env)
+
def start_cmdctl(self, c_channel_env):
- # XXX: we hardcode port 8080
- self.start_simple("b10-cmdctl", c_channel_env, 8080)
+ """
+ Starts the command control process
+ """
+ args = ["b10-cmdctl"]
+ if self.cmdctl_port is not None:
+ args.append("--port=" + str(self.cmdctl_port))
+ self.start_process("b10-cmdctl", args, c_channel_env, self.cmdctl_port)
def start_all_processes(self):
"""
@@ -543,12 +607,16 @@ class BoB:
# ... and finally start the remaining processes
self.start_stats(c_channel_env)
+ self.start_stats_httpd(c_channel_env)
self.start_cmdctl(c_channel_env)
-
+
+ if self.cfg_start_dhcp6:
+ self.start_dhcp6(c_channel_env)
+
def startup(self):
"""
Start the BoB instance.
-
+
Returns None if successful, otherwise an string describing the
problem.
"""
@@ -556,12 +624,12 @@ class BoB:
# running
c_channel_env = {}
if self.msgq_socket_file is not None:
- c_channel_env["BIND10_MSGQ_SOCKET_FILE"] = self.msgq_socket_file
- if self.verbose:
- sys.stdout.write("[bind10] Checking for already running b10-msgq\n")
+ c_channel_env["BIND10_MSGQ_SOCKET_FILE"] = self.msgq_socket_file
+ logger.debug(DBG_PROCESS, BIND10_CHECK_MSGQ_ALREADY_RUNNING)
# try to connect, and if we can't wait a short while
try:
self.cc_session = isc.cc.Session(self.msgq_socket_file)
+ logger.fatal(BIND10_MSGQ_ALREADY_RUNNING)
return "b10-msgq already running, or socket file not cleaned , cannot start"
except isc.cc.session.SessionError:
# this is the case we want, where the msgq is not running
@@ -592,14 +660,14 @@ class BoB:
self.cc_session.group_sendmsg(cmd, "Xfrin", "Xfrin")
self.cc_session.group_sendmsg(cmd, "Zonemgr", "Zonemgr")
self.cc_session.group_sendmsg(cmd, "Stats", "Stats")
+ self.cc_session.group_sendmsg(cmd, "StatsHttpd", "StatsHttpd")
def stop_process(self, process, recipient):
"""
Stop the given process, friendly-like. The process is the name it has
(in logs, etc), the recipient is the address on msgq.
"""
- if self.verbose:
- sys.stdout.write("[bind10] Asking %s to terminate\n" % process)
+ logger.info(BIND10_STOP_PROCESS, process)
# TODO: Some timeout to solve processes that don't want to die would
# help. We can even store it in the dict, it is used only as a set
self.expected_shutdowns[process] = 1
@@ -625,8 +693,7 @@ class BoB:
def shutdown(self):
"""Stop the BoB instance."""
- if self.verbose:
- sys.stdout.write("[bind10] Stopping the server.\n")
+ logger.info(BIND10_SHUTDOWN)
# first try using the BIND 10 request to stop
try:
self.stop_all_processes()
@@ -640,9 +707,8 @@ class BoB:
# next try sending a SIGTERM
processes_to_stop = list(self.processes.values())
for proc_info in processes_to_stop:
- if self.verbose:
- sys.stdout.write("[bind10] Sending SIGTERM to %s (PID %d).\n" %
- (proc_info.name, proc_info.pid))
+ logger.info(BIND10_SEND_SIGTERM, proc_info.name,
+ proc_info.pid)
try:
proc_info.process.terminate()
except OSError:
@@ -656,17 +722,18 @@ class BoB:
self.reap_children()
processes_to_stop = list(self.processes.values())
for proc_info in processes_to_stop:
- if self.verbose:
- sys.stdout.write("[bind10] Sending SIGKILL to %s (PID %d).\n" %
- (proc_info.name, proc_info.pid))
+ logger.info(BIND10_SEND_SIGKILL, proc_info.name,
+ proc_info.pid)
try:
proc_info.process.kill()
except OSError:
# ignore these (usually ESRCH because the child
# finally exited)
pass
- if self.verbose:
- sys.stdout.write("[bind10] All processes ended, server done.\n")
+ logger.info(BIND10_SHUTDOWN_COMPLETE)
+
+ def _get_process_exit_status(self):
+ return os.waitpid(-1, os.WNOHANG)
def reap_children(self):
"""Check to see if any of our child processes have exited,
@@ -674,14 +741,13 @@ class BoB:
"""
while True:
try:
- (pid, exit_status) = os.waitpid(-1, os.WNOHANG)
+ (pid, exit_status) = self._get_process_exit_status()
except OSError as o:
if o.errno == errno.ECHILD: break
# XXX: should be impossible to get any other error here
raise
if pid == 0: break
if pid in self.processes:
-
# One of the processes we know about. Get information on it.
proc_info = self.processes.pop(pid)
proc_info.restart_schedule.set_run_stop_time()
@@ -692,21 +758,24 @@ class BoB:
# elsewhere.
if self.runnable:
if exit_status is None:
- sys.stdout.write(
- "[bind10] Process %s (PID %d) died: exit status not available" %
- (proc_info.name, proc_info.pid))
+ logger.warn(BIND10_PROCESS_ENDED_NO_EXIT_STATUS,
+ proc_info.name, proc_info.pid)
else:
- sys.stdout.write(
- "[bind10] Process %s (PID %d) terminated, exit status = %d\n" %
- (proc_info.name, proc_info.pid, exit_status))
+ logger.warn(BIND10_PROCESS_ENDED_WITH_EXIT_STATUS,
+ proc_info.name, proc_info.pid,
+ exit_status)
# Was it a special process?
if proc_info.name == "b10-msgq":
- sys.stdout.write(
- "[bind10] The b10-msgq process died, shutting down.\n")
+ logger.fatal(BIND10_MSGQ_DAEMON_ENDED)
self.runnable = False
+
+ # If we're in 'brittle' mode, we want to shutdown after
+ # any process dies.
+ if self.brittle:
+ self.runnable = False
else:
- sys.stdout.write("[bind10] Unknown child pid %d exited.\n" % pid)
+ logger.info(BIND10_UNKNOWN_CHILD_PROCESS_ENDED, pid)
def restart_processes(self):
"""
@@ -737,14 +806,11 @@ class BoB:
next_restart = restart_time
still_dead[proc_info.pid] = proc_info
else:
- if self.verbose:
- sys.stdout.write("[bind10] Resurrecting dead %s process...\n" %
- proc_info.name)
+ logger.info(BIND10_RESURRECTING_PROCESS, proc_info.name)
try:
proc_info.respawn()
self.processes[proc_info.pid] = proc_info
- sys.stdout.write("[bind10] Resurrected %s (PID %d)\n" %
- (proc_info.name, proc_info.pid))
+ logger.info(BIND10_RESURRECTED_PROCESS, proc_info.name, proc_info.pid)
except:
still_dead[proc_info.pid] = proc_info
# remember any processes that refuse to be resurrected
@@ -776,8 +842,7 @@ def fatal_signal(signal_number, stack_frame):
"""We need to exit (SIGINT or SIGTERM received)."""
global options
global boss_of_bind
- if options.verbose:
- sys.stdout.write("[bind10] Received %s.\n" % get_signame(signal_number))
+ logger.info(BIND10_RECEIVED_SIGNAL, get_signame(signal_number))
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
boss_of_bind.runnable = False
@@ -785,6 +850,52 @@ def process_rename(option, opt_str, value, parser):
"""Function that renames the process if it is requested by a option."""
isc.util.process.rename(value)
+def parse_args(args=sys.argv[1:], Parser=OptionParser):
+ """
+ Function for parsing command line arguments. Returns the
+ options object from OptionParser.
+ """
+ parser = Parser(version=VERSION)
+ parser.add_option("-m", "--msgq-socket-file", dest="msgq_socket_file",
+ type="string", default=None,
+ help="UNIX domain socket file the b10-msgq daemon will use")
+ parser.add_option("-n", "--no-cache", action="store_true", dest="nocache",
+ default=False, help="disable hot-spot cache in authoritative DNS server")
+ parser.add_option("-u", "--user", dest="user", type="string", default=None,
+ help="Change user after startup (must run as root)")
+ parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
+ help="display more about what is going on")
+ parser.add_option("--pretty-name", type="string", action="callback",
+ callback=process_rename,
+ help="Set the process name (displayed in ps, top, ...)")
+ parser.add_option("-c", "--config-file", action="store",
+ dest="config_file", default=None,
+ help="Configuration database filename")
+ parser.add_option("-p", "--data-path", dest="data_path",
+ help="Directory to search for configuration files",
+ default=None)
+ parser.add_option("--cmdctl-port", dest="cmdctl_port", type="int",
+ default=None, help="Port of command control")
+ parser.add_option("--pid-file", dest="pid_file", type="string",
+ default=None,
+ help="file to dump the PID of the BIND 10 process")
+ parser.add_option("--brittle", dest="brittle", action="store_true",
+ help="debugging flag: exit if any component dies")
+
+ (options, args) = parser.parse_args(args)
+
+ if options.cmdctl_port is not None:
+ try:
+ isc.net.parse.port_parse(options.cmdctl_port)
+ except ValueError as e:
+ parser.error(e)
+
+ if args:
+ parser.print_help()
+ sys.exit(1)
+
+ return options
+
def dump_pid(pid_file):
"""
Dump the PID of the current process to the specified file. If the given
@@ -814,33 +925,14 @@ def unlink_pid_file(pid_file):
if error.errno is not errno.ENOENT:
raise
+
def main():
global options
global boss_of_bind
# Enforce line buffering on stdout, even when not a TTY
sys.stdout = io.TextIOWrapper(sys.stdout.detach(), line_buffering=True)
- # Parse any command-line options.
- parser = OptionParser(version=VERSION)
- parser.add_option("-m", "--msgq-socket-file", dest="msgq_socket_file",
- type="string", default=None,
- help="UNIX domain socket file the b10-msgq daemon will use")
- parser.add_option("-n", "--no-cache", action="store_true", dest="nocache",
- default=False, help="disable hot-spot cache in authoritative DNS server")
- parser.add_option("-u", "--user", dest="user", type="string", default=None,
- help="Change user after startup (must run as root)")
- parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
- help="display more about what is going on")
- parser.add_option("--pretty-name", type="string", action="callback",
- callback=process_rename,
- help="Set the process name (displayed in ps, top, ...)")
- parser.add_option("--pid-file", dest="pid_file", type="string",
- default=None,
- help="file to dump the PID of the BIND 10 process")
- (options, args) = parser.parse_args()
- if args:
- parser.print_help()
- sys.exit(1)
+ options = parse_args()
# Check user ID.
setuid = None
@@ -868,12 +960,11 @@ def main():
pass
if setuid is None:
- sys.stderr.write("bind10: invalid user: '%s'\n" % options.user)
+ logger.fatal(BIND10_INVALID_USER, options.user)
sys.exit(1)
# Announce startup.
- if options.verbose:
- sys.stdout.write("%s\n" % VERSION)
+ logger.info(BIND10_STARTING, VERSION)
# Create wakeup pipe for signal handlers
wakeup_pipe = os.pipe()
@@ -890,26 +981,16 @@ def main():
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
# Go bob!
- boss_of_bind = BoB(options.msgq_socket_file, options.nocache,
- options.verbose, setuid, username)
+ boss_of_bind = BoB(options.msgq_socket_file, options.data_path,
+ options.config_file, options.nocache, options.verbose,
+ setuid, username, options.cmdctl_port, options.brittle)
startup_result = boss_of_bind.startup()
if startup_result:
- sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)
+ logger.fatal(BIND10_STARTUP_ERROR, startup_result)
sys.exit(1)
- sys.stdout.write("[bind10] BIND 10 started\n")
+ logger.info(BIND10_STARTUP_COMPLETE)
dump_pid(options.pid_file)
- # send "bind10.boot_time" to b10-stats
- time.sleep(1) # wait a second
- if options.verbose:
- sys.stdout.write("[bind10] send \"bind10.boot_time\" to b10-stats\n")
- cmd = isc.config.ccsession.create_command('set',
- { "stats_data": {
- 'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME)
- }
- })
- boss_of_bind.cc_session.group_sendmsg(cmd, 'Stats')
-
# In our main loop, we check for dead processes or messages
# on the c-channel.
wakeup_fd = wakeup_pipe[0]
@@ -933,7 +1014,7 @@ def main():
if err.args[0] == errno.EINTR:
(rlist, wlist, xlist) = ([], [], [])
else:
- sys.stderr.write("[bind10] Error with select(); %s\n" % err)
+ logger.fatal(BIND10_SELECT_ERROR, err)
break
for fd in rlist + xlist:
@@ -941,8 +1022,8 @@ def main():
try:
boss_of_bind.ccs.check_command()
except isc.cc.session.ProtocolError:
- if options.verbose:
- sys.stderr.write("[bind10] msgq channel disappeared.\n")
+ logger.fatal(BIND10_MSGQ_DISAPPEARED)
+ self.runnable = False
break
elif fd == wakeup_fd:
os.read(wakeup_fd, 32)
@@ -950,7 +1031,6 @@ def main():
# shutdown
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
boss_of_bind.shutdown()
- sys.stdout.write("[bind10] BIND 10 exiting\n");
unlink_pid_file(options.pid_file)
sys.exit(0)
diff --git a/src/bin/bind10/bind10.xml b/src/bin/bind10/bind10.xml
index f3964a6..1128264 100644
--- a/src/bin/bind10/bind10.xml
+++ b/src/bin/bind10/bind10.xml
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>February 22, 2011</date>
+ <date>March 31, 2011</date>
</refentryinfo>
<refmeta>
@@ -44,14 +44,21 @@
<refsynopsisdiv>
<cmdsynopsis>
<command>bind10</command>
+ <arg><option>-c <replaceable>config-filename</replaceable></option></arg>
<arg><option>-m <replaceable>file</replaceable></option></arg>
<arg><option>-n</option></arg>
+ <arg><option>-p <replaceable>data_path</replaceable></option></arg>
<arg><option>-u <replaceable>user</replaceable></option></arg>
<arg><option>-v</option></arg>
+ <arg><option>--brittle</option></arg>
+ <arg><option>--cmdctl-port</option> <replaceable>port</replaceable></arg>
+ <arg><option>--config-file</option> <replaceable>config-filename</replaceable></arg>
+ <arg><option>--data-path</option> <replaceable>directory</replaceable></arg>
<arg><option>--msgq-socket-file <replaceable>file</replaceable></option></arg>
<arg><option>--no-cache</option></arg>
- <arg><option>--user <replaceable>user</replaceable></option></arg>
+ <arg><option>--pid-file</option> <replaceable>filename</replaceable></arg>
<arg><option>--pretty-name <replaceable>name</replaceable></option></arg>
+ <arg><option>--user <replaceable>user</replaceable></option></arg>
<arg><option>--verbose</option></arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -82,6 +89,60 @@
<variablelist>
<varlistentry>
+ <term>
+ <option>--brittle</option>
+ </term>
+ <listitem>
+ <para>
+ Shutdown if any of the child processes of
+ <command>bind10</command> exit. This is intended to
+ help developers debug the server, and should not be
+ used in production.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-c</option> <replaceable>config-filename</replaceable>,
+ <option>--config-file</option> <replaceable>config-filename</replaceable>
+ </term>
+ <listitem>
+ <para>The configuration filename to use. Can be either absolute or
+ relative to data path. In case it is absolute, value of data path is
+ not considered.</para>
+ <para>Defaults to b10-config.db.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>--cmdctl-port</option> <replaceable>port</replaceable>
+ </term>
+ <listitem>
+ <para>The <command>b10-cmdctl</command> daemon will listen
+ on this port.
+ (See
+ <refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum>
+ for the default.)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-p</option> <replaceable>directory</replaceable>,
+ <option>--data-path</option> <replaceable>directory</replaceable>
+ </term>
+ <listitem>
+ <para>The path where BIND 10 programs look for various data files.
+ Currently only b10-cfgmgr uses it to locate the configuration file,
+ but the usage might be extended for other programs and other types
+ of files.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>-m</option> <replaceable>file</replaceable>,
<option>--msgq-socket-file</option> <replaceable>file</replaceable></term>
@@ -107,7 +168,6 @@
<varlistentry>
<term><option>-u</option> <replaceable>user</replaceable>, <option>--user</option> <replaceable>name</replaceable></term>
-
<listitem>
<para>The username for <command>bind10</command> to run as.
<!-- TODO: example more detail. -->
@@ -118,6 +178,16 @@
</varlistentry>
<varlistentry>
+ <term><option>--pid-file</option> <replaceable>filename</replaceable></term>
+ <listitem>
+ <para>If defined, the PID of the <command>bind10</command> is stored
+ in this file.
+ This is used for testing purposes.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>--pretty-name <replaceable>name</replaceable></option></term>
<listitem>
@@ -145,6 +215,9 @@ The default is the basename of ARG 0.
</refsect1>
<!--
+TODO: configuration section
+-->
+<!--
<refsect1>
<title>FILES</title>
<para><filename></filename>
diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes
new file mode 100644
index 0000000..3f5f637
--- /dev/null
+++ b/src/bin/bind10/bind10_messages.mes
@@ -0,0 +1,157 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# No namespace declaration - these constants go in the global namespace
+# of the xfrin messages python module.
+
+% BIND10_CHECK_MSGQ_ALREADY_RUNNING checking if msgq is already running
+The boss process is starting up and will now check if the message bus
+daemon is already running. If so, it will not be able to start, as it
+needs a dedicated message bus.
+
+% BIND10_CONFIGURATION_START_AUTH start authoritative server: %1
+This message shows whether or not the authoritative server should be
+started according to the configuration.
+
+% BIND10_CONFIGURATION_START_RESOLVER start resolver: %1
+This message shows whether or not the resolver should be
+started according to the configuration.
+
+% BIND10_INVALID_USER invalid user: %1
+The boss process was started with the -u option, to drop root privileges
+and continue running as the specified user, but the user is unknown.
+
+% BIND10_KILL_PROCESS killing process %1
+The boss module is sending a kill signal to process with the given name,
+as part of the process of killing all started processes during a failed
+startup, as described for BIND10_KILLING_ALL_PROCESSES
+
+% BIND10_KILLING_ALL_PROCESSES killing all started processes
+The boss module was not able to start every process it needed to start
+during startup, and will now kill the processes that did get started.
+
+% BIND10_MSGQ_ALREADY_RUNNING msgq daemon already running, cannot start
+There already appears to be a message bus daemon running. Either an
+old process was not shut down correctly, and needs to be killed, or
+another instance of BIND10, with the same msgq domain socket, is
+running, which needs to be stopped.
+
+% BIND10_MSGQ_DAEMON_ENDED b10-msgq process died, shutting down
+The message bus daemon has died. This is a fatal error, since it may
+leave the system in an inconsistent state. BIND10 will now shut down.
+
+% BIND10_MSGQ_DISAPPEARED msgq channel disappeared
+While listening on the message bus channel for messages, it suddenly
+disappeared. The msgq daemon may have died. This might lead to an
+inconsistent state of the system, and BIND 10 will now shut down.
+
+% BIND10_PROCESS_ENDED_NO_EXIT_STATUS process %1 (PID %2) died: exit status not available
+The given process ended unexpectedly, but no exit status is
+available. See BIND10_PROCESS_ENDED_WITH_EXIT_STATUS for a longer
+description.
+
+% BIND10_PROCESS_ENDED_WITH_EXIT_STATUS process %1 (PID %2) terminated, exit status = %3
+The given process ended unexpectedly with the given exit status.
+Depending on which module it was, it may simply be restarted, or it
+may be a problem that will cause the boss module to shut down too.
+The latter happens if it was the message bus daemon, which, if it has
+died suddenly, may leave the system in an inconsistent state. BIND10
+will also shut down now if it has been run with --brittle.
+
+% BIND10_READING_BOSS_CONFIGURATION reading boss configuration
+The boss process is starting up, and will now process the initial
+configuration, as received from the configuration manager.
+
+% BIND10_RECEIVED_COMMAND received command: %1
+The boss module received a command and shall now process it. The command
+is printed.
+
+% BIND10_RECEIVED_NEW_CONFIGURATION received new configuration: %1
+The boss module received a configuration update and is going to apply
+it now. The new configuration is printed.
+
+% BIND10_RECEIVED_SIGNAL received signal %1
+The boss module received the given signal.
+
+% BIND10_RESURRECTED_PROCESS resurrected %1 (PID %2)
+The given process has been restarted successfully, and is now running
+with the given process id.
+
+% BIND10_RESURRECTING_PROCESS resurrecting dead %1 process...
+The given process has ended unexpectedly, and is now restarted.
+
+% BIND10_SELECT_ERROR error in select() call: %1
+There was a fatal error in the call to select(), used to see if a child
+process has ended or if there is a message on the message bus. This
+should not happen under normal circumstances and is considered fatal,
+so BIND 10 will now shut down. The specific error is printed.
+
+% BIND10_SEND_SIGKILL sending SIGKILL to %1 (PID %2)
+The boss module is sending a SIGKILL signal to the given process.
+
+% BIND10_SEND_SIGTERM sending SIGTERM to %1 (PID %2)
+The boss module is sending a SIGTERM signal to the given process.
+
+% BIND10_SHUTDOWN stopping the server
+The boss process received a command or signal telling it to shut down.
+It will send a shutdown command to each process. The processes that do
+not shut down will then receive a SIGTERM signal. If that doesn't work,
+it shall send SIGKILL signals to the processes still alive.
+
+% BIND10_SHUTDOWN_COMPLETE all processes ended, shutdown complete
+All child processes have been stopped, and the boss process will now
+stop itself.
+
+% BIND10_START_AS_NON_ROOT starting %1 as a user, not root. This might fail.
+The given module is being started or restarted without root privileges.
+If the module needs these privileges, it may have problems starting.
+Note that this issue should be resolved by the pending 'socket-creator'
+process; once that has been implemented, modules should not need root
+privileges anymore. See tickets #800 and #801 for more information.
+
+% BIND10_STARTED_PROCESS started %1
+The given process has successfully been started.
+
+% BIND10_STARTED_PROCESS_PID started %1 (PID %2)
+The given process has successfully been started, and has the given PID.
+
+% BIND10_STARTING starting BIND10: %1
+Informational message on startup that shows the full version.
+
+% BIND10_STARTING_PROCESS starting process %1
+The boss module is starting the given process.
+
+% BIND10_STARTING_PROCESS_PORT starting process %1 (to listen on port %2)
+The boss module is starting the given process, which will listen on the
+given port number.
+
+% BIND10_STARTING_PROCESS_PORT_ADDRESS starting process %1 (to listen on %2#%3)
+The boss module is starting the given process, which will listen on the
+given address and port number (written as <address>#<port>).
+
+% BIND10_STARTUP_COMPLETE BIND 10 started
+All modules have been successfully started, and BIND 10 is now running.
+
+% BIND10_STARTUP_ERROR error during startup: %1
+There was a fatal error when BIND10 was trying to start. The error is
+shown, and BIND10 will now shut down.
+
+% BIND10_STOP_PROCESS asking %1 to shut down
+The boss module is sending a shutdown command to the given module over
+the message channel.
+
+% BIND10_UNKNOWN_CHILD_PROCESS_ENDED unknown child pid %1 exited
+An unknown child process has exited. The PID is printed, but no further
+action will be taken by the boss process.
+
diff --git a/src/bin/bind10/bob.spec b/src/bin/bind10/bob.spec
index b45a12f..1184fd1 100644
--- a/src/bin/bind10/bob.spec
+++ b/src/bin/bind10/bob.spec
@@ -21,6 +21,21 @@
"command_name": "shutdown",
"command_description": "Shut down BIND 10",
"command_args": []
+ },
+ {
+ "command_name": "sendstats",
+ "command_description": "Send data to a statistics module at once",
+ "command_args": []
+ },
+ {
+ "command_name": "ping",
+ "command_description": "Ping the boss process",
+ "command_args": []
+ },
+ {
+ "command_name": "show_processes",
+ "command_description": "List the running BIND 10 processes",
+ "command_args": []
}
]
}
diff --git a/src/bin/bind10/run_bind10.sh.in b/src/bin/bind10/run_bind10.sh.in
old mode 100644
new mode 100755
index edc01fe..4020593
--- a/src/bin/bind10/run_bind10.sh.in
+++ b/src/bin/bind10/run_bind10.sh.in
@@ -20,17 +20,17 @@ export PYTHON_EXEC
BIND10_PATH=@abs_top_builddir@/src/bin/bind10
-PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:$PATH
+PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/dhcp6:$PATH
export PATH
-PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs
+PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config
export PYTHONPATH
# If necessary (rare cases), explicitly specify paths to dynamic libraries
# required by loadable python modules.
SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@
if test $SET_ENV_LIBRARY_PATH = yes; then
- @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@
+ @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@
export @ENV_LIBRARY_PATH@
fi
diff --git a/src/bin/bind10/tests/Makefile.am b/src/bin/bind10/tests/Makefile.am
index be2c091..3d8d57a 100644
--- a/src/bin/bind10/tests/Makefile.am
+++ b/src/bin/bind10/tests/Makefile.am
@@ -1,8 +1,16 @@
PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
#PYTESTS = args_test.py bind10_test.py
+# NOTE: this has a generated test found in the builddir
PYTESTS = bind10_test.py
EXTRA_DIST = $(PYTESTS)
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
if ENABLE_PYTHON_COVERAGE
@@ -12,6 +20,8 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
+ $(LIBRARY_PATH_PLACEHOLDER) \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/bind10 \
- $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+ BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \
+ $(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
done
diff --git a/src/bin/bind10/tests/bind10_test.in b/src/bin/bind10/tests/bind10_test.in
deleted file mode 100755
index cbd7452..0000000
--- a/src/bin/bind10/tests/bind10_test.in
+++ /dev/null
@@ -1,32 +0,0 @@
-#! /bin/sh
-
-# Copyright (C) 2010 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.
-
-PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
-export PYTHON_EXEC
-
-BIND10_PATH=@abs_top_srcdir@/src/bin/bind10
-
-PATH=@abs_top_srcdir@/src/bin/msgq:@abs_top_srcdir@/src/bin/auth:@abs_top_srcdir@/src/bin/bind-cfgd:$PATH
-export PATH
-
-PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_srcdir@/src/bin/bind10
-export PYTHONPATH
-
-cd ${BIND10_PATH}/tests
-${PYTHON_EXEC} -O bind10_test.py $*
-exec ${PYTHON_EXEC} -O args_test.py $*
-
diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in
index 0603443..91d326c 100644
--- a/src/bin/bind10/tests/bind10_test.py.in
+++ b/src/bin/bind10/tests/bind10_test.py.in
@@ -1,4 +1,19 @@
-from bind10 import ProcessInfo, BoB, dump_pid, unlink_pid_file
+# Copyright (C) 2011 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.
+
+from bind10 import ProcessInfo, BoB, parse_args, dump_pid, unlink_pid_file, _BASETIME
# XXX: environment tests are currently disabled, due to the preprocessor
# setup that we have now complicating the environment
@@ -9,6 +24,10 @@ import os
import signal
import socket
from isc.net.addr import IPAddr
+import time
+import isc
+
+from isc.testutils.parse_args import TestOptParser, OptsError
class TestProcessInfo(unittest.TestCase):
def setUp(self):
@@ -30,6 +49,7 @@ class TestProcessInfo(unittest.TestCase):
def test_init(self):
pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
+ pi.spawn()
os.dup2(self.old_stdout, sys.stdout.fileno())
self.assertEqual(pi.name, 'Test Process')
self.assertEqual(pi.args, [ '/bin/echo', 'foo' ])
@@ -50,12 +70,14 @@ class TestProcessInfo(unittest.TestCase):
def test_setting_null_stdout(self):
pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ],
dev_null_stdout=True)
+ pi.spawn()
os.dup2(self.old_stdout, sys.stdout.fileno())
self.assertEqual(pi.dev_null_stdout, True)
self.assertEqual(os.read(self.pipes[0], 100), b"")
def test_respawn(self):
pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
+ pi.spawn()
# wait for old process to work...
self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
# respawn it
@@ -89,6 +111,9 @@ class TestBoB(unittest.TestCase):
self.assertEqual(bob.cfg_start_auth, True)
self.assertEqual(bob.cfg_start_resolver, False)
+ self.assertEqual(bob.cfg_start_dhcp4, False)
+ self.assertEqual(bob.cfg_start_dhcp6, False)
+
def test_init_alternate_socket(self):
bob = BoB("alt_socket_file")
self.assertEqual(bob.verbose, False)
@@ -103,18 +128,57 @@ class TestBoB(unittest.TestCase):
self.assertEqual(bob.nocache, False)
self.assertEqual(bob.cfg_start_auth, True)
self.assertEqual(bob.cfg_start_resolver, False)
-
-# Class for testing the BoB start/stop components routines.
+ self.assertEqual(bob.cfg_start_dhcp4, False)
+ self.assertEqual(bob.cfg_start_dhcp6, False)
+
+ def test_command_handler(self):
+ class DummySession():
+ def group_sendmsg(self, msg, group):
+ (self.msg, self.group) = (msg, group)
+ def group_recvmsg(self, nonblock, seq): pass
+ bob = BoB()
+ bob.verbose = True
+ bob.cc_session = DummySession()
+ # a bad command
+ self.assertEqual(bob.command_handler(-1, None),
+ isc.config.ccsession.create_answer(1, "bad command"))
+ # "shutdown" command
+ self.assertEqual(bob.command_handler("shutdown", None),
+ isc.config.ccsession.create_answer(0))
+ self.assertFalse(bob.runnable)
+ # "sendstats" command
+ self.assertEqual(bob.command_handler("sendstats", None),
+ isc.config.ccsession.create_answer(0))
+ self.assertEqual(bob.cc_session.group, "Stats")
+ self.assertEqual(bob.cc_session.msg,
+ isc.config.ccsession.create_command(
+ 'set', { "stats_data": {
+ 'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME)
+ }}))
+ # "ping" command
+ self.assertEqual(bob.command_handler("ping", None),
+ isc.config.ccsession.create_answer(0, "pong"))
+ # "show_processes" command
+ self.assertEqual(bob.command_handler("show_processes", None),
+ isc.config.ccsession.create_answer(0,
+ bob.get_processes()))
+ # an unknown command
+ self.assertEqual(bob.command_handler("__UNKNOWN__", None),
+ isc.config.ccsession.create_answer(1, "Unknown command"))
+
+# Class for testing the BoB without actually starting processes.
+# This is used for testing the start/stop components routines and
+# the BoB commands.
#
-# Although testing that external processes start is outside the scope
+# Testing that external processes start is outside the scope
# of the unit test, by overriding the process start methods we can check
# that the right processes are started depending on the configuration
# options.
-class StartStopCheckBob(BoB):
+class MockBob(BoB):
def __init__(self):
BoB.__init__(self)
-# Set flags as to which of the overridden methods has been run.
+ # Set flags as to which of the overridden methods has been run.
self.msgq = False
self.cfgmgr = False
self.ccsession = False
@@ -124,8 +188,10 @@ class StartStopCheckBob(BoB):
self.xfrin = False
self.zonemgr = False
self.stats = False
+ self.stats_httpd = False
self.cmdctl = False
self.c_channel_env = {}
+ self.processes = { }
def read_bind10_config(self):
# Configuration options are set directly
@@ -133,65 +199,125 @@ class StartStopCheckBob(BoB):
def start_msgq(self, c_channel_env):
self.msgq = True
+ self.processes[2] = ProcessInfo('b10-msgq', ['/bin/false'])
+ self.processes[2].pid = 2
def start_cfgmgr(self, c_channel_env):
self.cfgmgr = True
+ self.processes[3] = ProcessInfo('b10-cfgmgr', ['/bin/false'])
+ self.processes[3].pid = 3
def start_ccsession(self, c_channel_env):
self.ccsession = True
+ self.processes[4] = ProcessInfo('b10-ccsession', ['/bin/false'])
+ self.processes[4].pid = 4
def start_auth(self, c_channel_env):
self.auth = True
+ self.processes[5] = ProcessInfo('b10-auth', ['/bin/false'])
+ self.processes[5].pid = 5
def start_resolver(self, c_channel_env):
self.resolver = True
+ self.processes[6] = ProcessInfo('b10-resolver', ['/bin/false'])
+ self.processes[6].pid = 6
def start_xfrout(self, c_channel_env):
self.xfrout = True
+ self.processes[7] = ProcessInfo('b10-xfrout', ['/bin/false'])
+ self.processes[7].pid = 7
def start_xfrin(self, c_channel_env):
self.xfrin = True
+ self.processes[8] = ProcessInfo('b10-xfrin', ['/bin/false'])
+ self.processes[8].pid = 8
def start_zonemgr(self, c_channel_env):
self.zonemgr = True
+ self.processes[9] = ProcessInfo('b10-zonemgr', ['/bin/false'])
+ self.processes[9].pid = 9
def start_stats(self, c_channel_env):
self.stats = True
+ self.processes[10] = ProcessInfo('b10-stats', ['/bin/false'])
+ self.processes[10].pid = 10
+
+ def start_stats_httpd(self, c_channel_env):
+ self.stats_httpd = True
+ self.processes[11] = ProcessInfo('b10-stats-httpd', ['/bin/false'])
+ self.processes[11].pid = 11
def start_cmdctl(self, c_channel_env):
self.cmdctl = True
+ self.processes[12] = ProcessInfo('b10-cmdctl', ['/bin/false'])
+ self.processes[12].pid = 12
+
+ def start_dhcp6(self, c_channel_env):
+ self.dhcp6 = True
+ self.processes[13] = ProcessInfo('b10-dhcp6', ['/bin/false'])
+ self.processes[13]
+
+ def start_dhcp4(self, c_channel_env):
+ self.dhcp4 = True
+ self.processes[14] = ProcessInfo('b10-dhcp4', ['/bin/false'])
+ self.processes[14]
# We don't really use all of these stop_ methods. But it might turn out
# someone would add some stop_ method to BoB and we want that one overriden
# in case he forgets to update the tests.
def stop_msgq(self):
+ if self.msgq:
+ del self.processes[2]
self.msgq = False
def stop_cfgmgr(self):
+ if self.cfgmgr:
+ del self.processes[3]
self.cfgmgr = False
def stop_ccsession(self):
+ if self.ccssession:
+ del self.processes[4]
self.ccsession = False
def stop_auth(self):
+ if self.auth:
+ del self.processes[5]
self.auth = False
def stop_resolver(self):
+ if self.resolver:
+ del self.processes[6]
self.resolver = False
def stop_xfrout(self):
+ if self.xfrout:
+ del self.processes[7]
self.xfrout = False
def stop_xfrin(self):
+ if self.xfrin:
+ del self.processes[8]
self.xfrin = False
def stop_zonemgr(self):
+ if self.zonemgr:
+ del self.processes[9]
self.zonemgr = False
def stop_stats(self):
+ if self.stats:
+ del self.processes[10]
self.stats = False
+ def stop_stats_httpd(self):
+ if self.stats_httpd:
+ del self.processes[11]
+ self.stats_httpd = False
+
def stop_cmdctl(self):
+ if self.cmdctl:
+ del self.processes[12]
self.cmdctl = False
class TestStartStopProcessesBob(unittest.TestCase):
@@ -216,6 +342,7 @@ class TestStartStopProcessesBob(unittest.TestCase):
self.assertEqual(bob.xfrin, auth)
self.assertEqual(bob.zonemgr, auth)
self.assertEqual(bob.stats, core)
+ self.assertEqual(bob.stats_httpd, core)
self.assertEqual(bob.cmdctl, core)
def check_preconditions(self, bob):
@@ -247,11 +374,29 @@ class TestStartStopProcessesBob(unittest.TestCase):
"""
self.check_started(bob, True, False, True)
+ def check_started_dhcp(self, bob, v4, v6):
+ """
+ Check if proper combinations of DHCPv4 and DHCpv6 can be started
+ """
+ v4found = 0
+ v6found = 0
+
+ for pid in bob.processes:
+ if (bob.processes[pid].name == "b10-dhcp4"):
+ v4found += 1
+ if (bob.processes[pid].name == "b10-dhcp6"):
+ v6found += 1
+
+ # there should be exactly one DHCPv4 daemon (if v4==True)
+ # there should be exactly one DHCPv6 daemon (if v6==True)
+ self.assertEqual(v4==True, v4found==1)
+ self.assertEqual(v6==True, v6found==1)
+
# Checks the processes started when starting neither auth nor resolver
# is specified.
def test_start_none(self):
# Create BoB and ensure correct initialization
- bob = StartStopCheckBob()
+ bob = MockBob()
self.check_preconditions(bob)
# Start processes and check what was started
@@ -264,7 +409,7 @@ class TestStartStopProcessesBob(unittest.TestCase):
# Checks the processes started when starting only the auth process
def test_start_auth(self):
# Create BoB and ensure correct initialization
- bob = StartStopCheckBob()
+ bob = MockBob()
self.check_preconditions(bob)
# Start processes and check what was started
@@ -278,7 +423,7 @@ class TestStartStopProcessesBob(unittest.TestCase):
# Checks the processes started when starting only the resolver process
def test_start_resolver(self):
# Create BoB and ensure correct initialization
- bob = StartStopCheckBob()
+ bob = MockBob()
self.check_preconditions(bob)
# Start processes and check what was started
@@ -292,7 +437,7 @@ class TestStartStopProcessesBob(unittest.TestCase):
# Checks the processes started when starting both auth and resolver process
def test_start_both(self):
# Create BoB and ensure correct initialization
- bob = StartStopCheckBob()
+ bob = MockBob()
self.check_preconditions(bob)
# Start processes and check what was started
@@ -310,7 +455,7 @@ class TestStartStopProcessesBob(unittest.TestCase):
"""
# Create BoB and ensure correct initialization
- bob = StartStopCheckBob()
+ bob = MockBob()
self.check_preconditions(bob)
# Start processes (nothing much should be started, as in
@@ -375,7 +520,7 @@ class TestStartStopProcessesBob(unittest.TestCase):
Tests that a process is started only once.
"""
# Create BoB and ensure correct initialization
- bob = StartStopCheckBob()
+ bob = MockBob()
self.check_preconditions(bob)
# Start processes (both)
@@ -401,7 +546,7 @@ class TestStartStopProcessesBob(unittest.TestCase):
Test that processes are not started by the config handler before
startup.
"""
- bob = StartStopCheckBob()
+ bob = MockBob()
self.check_preconditions(bob)
bob.start_auth = lambda: self.fail("Started auth again")
@@ -412,6 +557,136 @@ class TestStartStopProcessesBob(unittest.TestCase):
bob.config_handler({'start_auth': True, 'start_resolver': True})
+ # Checks that DHCP (v4 and v6) processes are started when expected
+ def test_start_dhcp(self):
+
+ # Create BoB and ensure correct initialization
+ bob = MockBob()
+ self.check_preconditions(bob)
+
+ # don't care about DNS stuff
+ bob.cfg_start_auth = False
+ bob.cfg_start_resolver = False
+
+ # v4 and v6 disabled
+ bob.cfg_start_dhcp6 = False
+ bob.cfg_start_dhcp4 = False
+ bob.start_all_processes()
+ self.check_started_dhcp(bob, False, False)
+
+ # v6 only enabled
+ bob.cfg_start_dhcp6 = True
+ bob.cfg_start_dhcp4 = False
+ bob.start_all_processes()
+ self.check_started_dhcp(bob, False, True)
+
+ # uncomment when dhcpv4 becomes implemented
+ # v4 only enabled
+ #bob.cfg_start_dhcp6 = False
+ #bob.cfg_start_dhcp4 = True
+ #self.check_started_dhcp(bob, True, False)
+
+ # both v4 and v6 enabled
+ #bob.cfg_start_dhcp6 = True
+ #bob.cfg_start_dhcp4 = True
+ #self.check_started_dhcp(bob, True, True)
+
+class TestBossCmd(unittest.TestCase):
+ def test_ping(self):
+ """
+ Confirm simple ping command works.
+ """
+ bob = MockBob()
+ answer = bob.command_handler("ping", None)
+ self.assertEqual(answer, {'result': [0, 'pong']})
+
+ def test_show_processes(self):
+ """
+ Confirm getting a list of processes works.
+ """
+ bob = MockBob()
+ answer = bob.command_handler("show_processes", None)
+ self.assertEqual(answer, {'result': [0, []]})
+
+ def test_show_processes_started(self):
+ """
+ Confirm getting a list of processes works.
+ """
+ bob = MockBob()
+ bob.start_all_processes()
+ answer = bob.command_handler("show_processes", None)
+ processes = [[2, 'b10-msgq'],
+ [3, 'b10-cfgmgr'],
+ [4, 'b10-ccsession'],
+ [5, 'b10-auth'],
+ [7, 'b10-xfrout'],
+ [8, 'b10-xfrin'],
+ [9, 'b10-zonemgr'],
+ [10, 'b10-stats'],
+ [11, 'b10-stats-httpd'],
+ [12, 'b10-cmdctl']]
+ self.assertEqual(answer, {'result': [0, processes]})
+
+class TestParseArgs(unittest.TestCase):
+ """
+ This tests parsing of arguments of the bind10 master process.
+ """
+ #TODO: Write tests for the original parsing, bad options, etc.
+ def test_no_opts(self):
+ """
+ Test correct default values when no options are passed.
+ """
+ options = parse_args([], TestOptParser)
+ self.assertEqual(None, options.data_path)
+ self.assertEqual(None, options.config_file)
+ self.assertEqual(None, options.cmdctl_port)
+
+ def test_data_path(self):
+ """
+ Test it can parse the data path.
+ """
+ self.assertRaises(OptsError, parse_args, ['-p'], TestOptParser)
+ self.assertRaises(OptsError, parse_args, ['--data-path'],
+ TestOptParser)
+ options = parse_args(['-p', '/data/path'], TestOptParser)
+ self.assertEqual('/data/path', options.data_path)
+ options = parse_args(['--data-path=/data/path'], TestOptParser)
+ self.assertEqual('/data/path', options.data_path)
+
+ def test_config_filename(self):
+ """
+ Test it can parse the config switch.
+ """
+ self.assertRaises(OptsError, parse_args, ['-c'], TestOptParser)
+ self.assertRaises(OptsError, parse_args, ['--config-file'],
+ TestOptParser)
+ options = parse_args(['-c', 'config-file'], TestOptParser)
+ self.assertEqual('config-file', options.config_file)
+ options = parse_args(['--config-file=config-file'], TestOptParser)
+ self.assertEqual('config-file', options.config_file)
+
+ def test_cmdctl_port(self):
+ """
+ Test it can parse the command control port.
+ """
+ self.assertRaises(OptsError, parse_args, ['--cmdctl-port=abc'],
+ TestOptParser)
+ self.assertRaises(OptsError, parse_args, ['--cmdctl-port=100000000'],
+ TestOptParser)
+ self.assertRaises(OptsError, parse_args, ['--cmdctl-port'],
+ TestOptParser)
+ options = parse_args(['--cmdctl-port=1234'], TestOptParser)
+ self.assertEqual(1234, options.cmdctl_port)
+
+ def test_brittle(self):
+ """
+ Test we can use the "brittle" flag.
+ """
+ options = parse_args([], TestOptParser)
+ self.assertFalse(options.brittle)
+ options = parse_args(['--brittle'], TestOptParser)
+ self.assertTrue(options.brittle)
+
class TestPIDFile(unittest.TestCase):
def setUp(self):
self.pid_file = '@builddir@' + os.sep + 'bind10.pid'
@@ -459,5 +734,34 @@ class TestPIDFile(unittest.TestCase):
self.assertRaises(IOError, dump_pid,
'nonexistent_dir' + os.sep + 'bind10.pid')
+class TestBrittle(unittest.TestCase):
+ def test_brittle_disabled(self):
+ bob = MockBob()
+ bob.start_all_processes()
+ bob.runnable = True
+
+ bob.reap_children()
+ self.assertTrue(bob.runnable)
+
+ def simulated_exit(self):
+ ret_val = self.exit_info
+ self.exit_info = (0, 0)
+ return ret_val
+
+ def test_brittle_enabled(self):
+ bob = MockBob()
+ bob.start_all_processes()
+ bob.runnable = True
+
+ bob.brittle = True
+ self.exit_info = (5, 0)
+ bob._get_process_exit_status = self.simulated_exit
+
+ old_stdout = sys.stdout
+ sys.stdout = open("/dev/null", "w")
+ bob.reap_children()
+ sys.stdout = old_stdout
+ self.assertFalse(bob.runnable)
+
if __name__ == '__main__':
unittest.main()
diff --git a/src/bin/bindctl/Makefile.am b/src/bin/bindctl/Makefile.am
index 2f412ec..cd8bcb3 100644
--- a/src/bin/bindctl/Makefile.am
+++ b/src/bin/bindctl/Makefile.am
@@ -25,3 +25,8 @@ bindctl: bindctl_main.py
-e "s|@@SYSCONFDIR@@|@sysconfdir@|" \
-e "s|@@LIBEXECDIR@@|$(pkglibexecdir)|" bindctl_main.py >$@
chmod a+x $@
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py
index 83dab25..8973aa5 100644
--- a/src/bin/bindctl/bindcmd.py
+++ b/src/bin/bindctl/bindcmd.py
@@ -123,14 +123,19 @@ class BindCmdInterpreter(Cmd):
'''Parse commands from user and send them to cmdctl. '''
try:
if not self.login_to_cmdctl():
- return
+ return
self.cmdloop()
+ print('\nExit from bindctl')
except FailToLogin as err:
# error already printed when this was raised, ignoring
pass
except KeyboardInterrupt:
print('\nExit from bindctl')
+ except socket.error as err:
+ print('Failed to send request, the connection is closed')
+ except http.client.CannotSendRequest:
+ print('Can not send request, the connection is busy')
def _get_saved_user_info(self, dir, file_name):
''' Read all the available username and password pairs saved in
@@ -192,8 +197,10 @@ class BindCmdInterpreter(Cmd):
raise FailToLogin()
if response.status == http.client.OK:
- print(data + ' login as ' + row[0] )
- return True
+ # Is interactive?
+ if sys.stdin.isatty():
+ print(data + ' login as ' + row[0])
+ return True
count = 0
print("[TEMP MESSAGE]: username :root password :bind10")
@@ -273,8 +280,9 @@ class BindCmdInterpreter(Cmd):
self._update_commands()
def precmd(self, line):
- self._update_all_modules_info()
- return line
+ if line != 'EOF':
+ self._update_all_modules_info()
+ return line
def postcmd(self, stop, line):
'''Update the prompt after every command, but only if we
diff --git a/src/bin/bindctl/bindctl.1 b/src/bin/bindctl/bindctl.1
index e86eca2..97700d6 100644
--- a/src/bin/bindctl/bindctl.1
+++ b/src/bin/bindctl/bindctl.1
@@ -22,7 +22,7 @@
bindctl \- control and configure BIND 10
.SH "SYNOPSIS"
.HP \w'\fBbindctl\fR\ 'u
-\fBbindctl\fR [\fB\-a\ \fR\fB\fIaddress\fR\fR] [\fB\-h\fR] [\fB\-c\ \fR\fB\fIfile\fR\fR] [\fB\-p\ \fR\fB\fInumber\fR\fR] [\fB\-\-address\ \fR\fB\fIaddress\fR\fR] [\fB\-\-help\fR] [\fB\-\-certificate\-chain\ \fR\fB\fIfile\fR\fR] [\fB\-\-port\ \fR\fB\fInumber\fR\fR] [\fB\-\-version\fR]
+\fBbindctl\fR [\fB\-a\ \fR\fB\fIaddress\fR\fR] [\fB\-h\fR] [\fB\-c\ \fR\fB\fIfile\fR\fR] [\fB\-p\ \fR\fB\fInumber\fR\fR] [\fB\-\-address\ \fR\fB\fIaddress\fR\fR] [\fB\-\-help\fR] [\fB\-\-certificate\-chain\ \fR\fB\fIfile\fR\fR] [\fB\-\-csv\-file\-dir\fR\fB\fIfile\fR\fR] [\fB\-\-port\ \fR\fB\fInumber\fR\fR] [\fB\-\-version\fR]
.SH "DESCRIPTION"
.PP
The
@@ -52,6 +52,11 @@ daemon\&. The default is 127\&.0\&.0\&.1\&.
The PEM formatted server certificate validation chain file\&.
.RE
.PP
+\fB\-\-csv\-file\-dir\fR\fIfile\fR
+.RS 4
+The directory name in which the user/password CSV file is stored (see AUTHENTICATION)\&. By default this option doesn\'t have any value, in which case the "\&.bind10" directory under the user\'s home directory will be used\&.
+.RE
+.PP
\fB\-h\fR, \fB\-\-help\fR
.RS 4
Display command usage\&.
@@ -85,10 +90,10 @@ Display the version number and exit\&.
.RE
.SH "AUTHENTICATION"
.PP
-The tool will authenticate using a username and password\&. On the first successful login, it will save the details to
-~/\&.bind10/default_user\&.csv
-which will be used for later uses of
-\fBbindctl\fR\&.
+The tool will authenticate using a username and password\&. On the first successful login, it will save the details to a comma\-separated\-value (CSV) file which will be used for later uses of
+\fBbindctl\fR\&. The file name is
+default_user\&.csv
+located under the directory specified by the \-\-csv\-file\-dir option\&.
.SH "USAGE"
.PP
The
diff --git a/src/bin/bindctl/run_bindctl.sh.in b/src/bin/bindctl/run_bindctl.sh.in
old mode 100644
new mode 100755
index 730ce1e..8f6ba59
--- a/src/bin/bindctl/run_bindctl.sh.in
+++ b/src/bin/bindctl/run_bindctl.sh.in
@@ -23,6 +23,14 @@ BINDCTL_PATH=@abs_top_builddir@/src/bin/bindctl
PYTHONPATH=@abs_top_srcdir@/src/bin:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/bin:@abs_top_srcdir@/src/lib/python
export PYTHONPATH
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@
+if test $SET_ENV_LIBRARY_PATH = yes; then
+ @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@
+ export @ENV_LIBRARY_PATH@
+fi
+
B10_FROM_SOURCE=@abs_top_srcdir@
export B10_FROM_SOURCE
diff --git a/src/bin/bindctl/tests/Makefile.am b/src/bin/bindctl/tests/Makefile.am
index d2bb90f..891d413 100644
--- a/src/bin/bindctl/tests/Makefile.am
+++ b/src/bin/bindctl/tests/Makefile.am
@@ -2,6 +2,13 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
PYTESTS = bindctl_test.py cmdparse_test.py
EXTRA_DIST = $(PYTESTS)
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
if ENABLE_PYTHON_COVERAGE
@@ -11,6 +18,7 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
+ $(LIBRARY_PATH_PLACEHOLDER) \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/bindctl:$(abs_top_srcdir)/src/bin \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
diff --git a/src/bin/bindctl/tests/bindctl_test.py b/src/bin/bindctl/tests/bindctl_test.py
index dd492c1..0635b32 100644
--- a/src/bin/bindctl/tests/bindctl_test.py
+++ b/src/bin/bindctl/tests/bindctl_test.py
@@ -17,11 +17,16 @@
import unittest
import isc.cc.data
import os
+import io
+import sys
+import socket
+import http.client
import pwd
import getpass
from optparse import OptionParser
from isc.config.config_data import ConfigData, MultiConfigData
from isc.config.module_spec import ModuleSpec
+from isc.testutils.parse_args import TestOptParser, OptsError
from bindctl_main import set_bindctl_options
from bindctl import cmdparse
from bindctl import bindcmd
@@ -275,7 +280,33 @@ class FakeCCSession(MultiConfigData):
]
}
self.set_specification(ModuleSpec(spec))
-
+
+
+# fake socket
+class FakeSocket():
+ def __init__(self):
+ self.run = True
+
+ def connect(self, to):
+ if not self.run:
+ raise socket.error
+
+ def close(self):
+ self.run = False
+
+ def send(self, data):
+ if not self.run:
+ raise socket.error
+ return len(data)
+
+ def makefile(self, type):
+ return self
+
+ def sendall(self, data):
+ if not self.run:
+ raise socket.error
+ return len(data)
+
class TestConfigCommands(unittest.TestCase):
def setUp(self):
@@ -283,7 +314,47 @@ class TestConfigCommands(unittest.TestCase):
mod_info = ModuleInfo(name = "foo")
self.tool.add_module_info(mod_info)
self.tool.config_data = FakeCCSession()
-
+ self.stdout_backup = sys.stdout
+
+ def test_precmd(self):
+ def update_all_modules_info():
+ raise socket.error
+ def precmd(line):
+ self.tool.precmd(line)
+ self.tool._update_all_modules_info = update_all_modules_info
+ # If line is equals to 'EOF', _update_all_modules_info() shouldn't be called
+ precmd('EOF')
+ self.assertRaises(socket.error, precmd, 'continue')
+
+ def test_run(self):
+ def login_to_cmdctl():
+ return True
+ def cmd_loop():
+ self.tool._send_message("/module_spec", None)
+
+ self.tool.login_to_cmdctl = login_to_cmdctl
+ # rewrite cmdloop() to avoid interactive mode
+ self.tool.cmdloop = cmd_loop
+
+ self.tool.conn.sock = FakeSocket()
+ self.tool.conn.sock.close()
+
+ # validate log message for socket.err
+ socket_err_output = io.StringIO()
+ sys.stdout = socket_err_output
+ self.assertRaises(None, self.tool.run())
+ self.assertEqual("Failed to send request, the connection is closed\n",
+ socket_err_output.getvalue())
+ socket_err_output.close()
+
+ # validate log message for http.client.CannotSendRequest
+ cannot_send_output = io.StringIO()
+ sys.stdout = cannot_send_output
+ self.assertRaises(None, self.tool.run())
+ self.assertEqual("Can not send request, the connection is busy\n",
+ cannot_send_output.getvalue())
+ cannot_send_output.close()
+
def test_apply_cfg_command_int(self):
self.tool.location = '/'
@@ -332,10 +403,17 @@ class TestConfigCommands(unittest.TestCase):
# this should raise a TypeError
cmd = cmdparse.BindCmdParse("config set identifier=\"foo/a_list\" value=\"a\"")
self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
-
+
cmd = cmdparse.BindCmdParse("config set identifier=\"foo/a_list\" value=[1]")
self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
+ def tearDown(self):
+ sys.stdout = self.stdout_backup
+
+class FakeBindCmdInterpreter(bindcmd.BindCmdInterpreter):
+ def __init__(self):
+ pass
+
class TestBindCmdInterpreter(unittest.TestCase):
def _create_invalid_csv_file(self, csvfilename):
@@ -360,35 +438,23 @@ class TestBindCmdInterpreter(unittest.TestCase):
self.assertEqual(new_csv_dir, custom_cmd.csv_file_dir)
def test_get_saved_user_info(self):
+ old_stdout = sys.stdout
+ sys.stdout = open(os.devnull, 'w')
cmd = bindcmd.BindCmdInterpreter()
users = cmd._get_saved_user_info('/notexist', 'csv_file.csv')
self.assertEqual([], users)
-
+
csvfilename = 'csv_file.csv'
self._create_invalid_csv_file(csvfilename)
users = cmd._get_saved_user_info('./', csvfilename)
self.assertEqual([], users)
os.remove(csvfilename)
+ sys.stdout = old_stdout
class TestCommandLineOptions(unittest.TestCase):
- class FakeParserError(Exception):
- """An exception thrown from FakeOptionParser on parser error.
- """
- pass
-
- class FakeOptionParser(OptionParser):
- """This fake class emulates the OptionParser class with customized
- error handling for the convenient of tests.
- """
- def __init__(self):
- OptionParser.__init__(self)
-
- def error(self, msg):
- raise TestCommandLineOptions.FakeParserError
-
def setUp(self):
- self.parser = self.FakeOptionParser()
+ self.parser = TestOptParser()
set_bindctl_options(self.parser)
def test_csv_file_dir(self):
@@ -401,7 +467,7 @@ class TestCommandLineOptions(unittest.TestCase):
self.assertEqual('some_dir', options.csv_file_dir)
# missing option arg; should trigger parser error.
- self.assertRaises(self.FakeParserError, self.parser.parse_args,
+ self.assertRaises(OptsError, self.parser.parse_args,
['--csv-file-dir'])
if __name__== "__main__":
diff --git a/src/bin/cfgmgr/Makefile.am b/src/bin/cfgmgr/Makefile.am
index a41448b..aee78cf 100644
--- a/src/bin/cfgmgr/Makefile.am
+++ b/src/bin/cfgmgr/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . tests
+SUBDIRS = . plugins tests
pkglibexecdir = $(libexecdir)/@PACKAGE@
@@ -28,3 +28,8 @@ install-data-local:
$(mkinstalldirs) $(DESTDIR)/@localstatedir@/@PACKAGE@
# TODO: permissions handled later
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/cfgmgr/b10-cfgmgr.8 b/src/bin/cfgmgr/b10-cfgmgr.8
index 03371af..719f4c6 100644
--- a/src/bin/cfgmgr/b10-cfgmgr.8
+++ b/src/bin/cfgmgr/b10-cfgmgr.8
@@ -20,6 +20,9 @@
.\" -----------------------------------------------------------------
.SH "NAME"
b10-cfgmgr \- Configuration manager
+.SH "SYNOPSIS"
+.HP \w'\fBb10\-cfgmgr\fR\ 'u
+\fBb10\-cfgmgr\fR [\fB\-c\fR\fB\fIconfig\-filename\fR\fR] [\fB\-p\fR\fB\fIdata_path\fR\fR]
.SH "DESCRIPTION"
.PP
The
@@ -43,8 +46,21 @@ The daemon may be cleanly stopped by sending the SIGTERM signal to the process\&
When it exits, it saves its current configuration to
/usr/local/var/bind10\-devel/b10\-config\&.db\&.
+.SH "ARGUMENTS"
.PP
-The daemon has no command line options\&. It ignores any arguments\&.
+The arguments are as follows:
+.PP
+\fB\-c\fR\fIconfig\-filename\fR, \fB\-\-config\-filename\fR \fIconfig\-filename\fR
+.RS 4
+The configuration database filename to use\&. Can be either absolute or relative to data path\&.
+.sp
+Defaults to b10\-config\&.db
+.RE
+.PP
+\fB\-p\fR\fIdata\-path\fR, \fB\-\-data\-path\fR \fIdata\-path\fR
+.RS 4
+The path where BIND 10 looks for files\&. The configuration file is looked for here, if it is relative\&. If it is absolute, the path is ignored\&.
+.RE
.SH "FILES"
.PP
/usr/local/var/bind10\-devel/b10\-config\&.db
diff --git a/src/bin/cfgmgr/b10-cfgmgr.py.in b/src/bin/cfgmgr/b10-cfgmgr.py.in
index e37ec48..8befbdf 100755
--- a/src/bin/cfgmgr/b10-cfgmgr.py.in
+++ b/src/bin/cfgmgr/b10-cfgmgr.py.in
@@ -17,55 +17,89 @@
import sys; sys.path.append ('@@PYTHONPATH@@')
-from isc.config.cfgmgr import ConfigManager, ConfigManagerDataReadError
+import bind10_config
from isc.cc import SessionError
import isc.util.process
import signal
import os
+from optparse import OptionParser
+import glob
+import os.path
+import isc.log
+isc.log.init("b10-cfgmgr")
+from isc.config.cfgmgr import ConfigManager, ConfigManagerDataReadError, logger
+from cfgmgr_messages import *
isc.util.process.rename()
-# If B10_FROM_SOURCE is set in the environment, we use data files
-# from a directory relative to the value of that variable, or, if defined,
-# relative to the value of B10_FROM_SOURCE_LOCALSTATEDIR. Otherwise
-# we use the ones installed on the system.
-# B10_FROM_SOURCE_LOCALSTATEDIR is specifically intended to be used for
-# tests where we want to use variuos types of configuration within the test
-# environment. (We may want to make it even more generic so that the path is
-# passed from the boss process)
-if "B10_FROM_SOURCE" in os.environ:
- if "B10_FROM_SOURCE_LOCALSTATEDIR" in os.environ:
- DATA_PATH = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"]
- else:
- DATA_PATH = os.environ["B10_FROM_SOURCE"]
-else:
- PREFIX = "@prefix@"
- DATA_PATH = "@localstatedir@/@PACKAGE@".replace("${prefix}", PREFIX)
+# Import some paths from our configuration
+DATA_PATH = bind10_config.DATA_PATH
+PLUGIN_PATHS = bind10_config.PLUGIN_PATHS
+PREFIX = bind10_config.PREFIX
+DEFAULT_CONFIG_FILE = "b10-config.db"
cm = None
+def parse_options(args=sys.argv[1:], Parser=OptionParser):
+ parser = Parser()
+ parser.add_option("-p", "--data-path", dest="data_path",
+ help="Directory to search for configuration files " +
+ "(default=" + DATA_PATH + ")", default=DATA_PATH)
+ parser.add_option("-c", "--config-filename", dest="config_file",
+ help="Configuration database filename " +
+ "(default=" + DEFAULT_CONFIG_FILE + ")",
+ default=DEFAULT_CONFIG_FILE)
+ (options, args) = parser.parse_args(args)
+ if args:
+ parser.error("No non-option arguments allowed")
+ return options
+
def signal_handler(signal, frame):
global cm
if cm:
cm.running = False
+def load_plugins(path, cm):
+ """Load all python files in the given path and treat them as plugins."""
+ # Find the python files
+ plugins = glob.glob(path + os.sep + '*.py')
+ # Search this directory first, but leave the others there for the imports
+ # of the modules
+ sys.path.insert(0, path)
+ try:
+ for plugin in plugins:
+ # Generate the name of the plugin
+ filename = os.path.basename(plugin)
+ name = filename[:-3]
+ # Load it
+ module = __import__(name)
+ # Ask it to provide the spec and checking function
+ (spec, check_func) = module.load()
+ # And insert it into the manager
+ cm.set_virtual_module(spec, check_func)
+ finally:
+ # Restore the search path
+ sys.path = sys.path[1:]
+
def main():
+ options = parse_options()
global cm
try:
- cm = ConfigManager(DATA_PATH)
+ cm = ConfigManager(options.data_path, options.config_file)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
cm.read_config()
+ for ppath in PLUGIN_PATHS:
+ load_plugins(ppath, cm)
cm.notify_boss()
cm.run()
except SessionError as se:
- print("[b10-cfgmgr] Error creating config manager, "
- "is the command channel daemon running?")
+ logger.fatal(CFGMGR_CC_SESSION_ERROR, se)
return 1
except KeyboardInterrupt as kie:
- print("[b10-cfgmgr] Interrupted, exiting")
+ logger.info(CFGMGR_STOPPED_BY_KEYBOARD)
except ConfigManagerDataReadError as cmdre:
- print("[b10-cfgmgr] " + str(cmdre))
+ logger.fatal(CFGMGR_DATA_READ_ERROR, cmdre)
return 2
return 0
diff --git a/src/bin/cfgmgr/b10-cfgmgr.xml b/src/bin/cfgmgr/b10-cfgmgr.xml
index 9505eee..785a058 100644
--- a/src/bin/cfgmgr/b10-cfgmgr.xml
+++ b/src/bin/cfgmgr/b10-cfgmgr.xml
@@ -41,16 +41,13 @@
</copyright>
</docinfo>
-<!--
<refsynopsisdiv>
<cmdsynopsis>
- <command></command>
- <arg><option></option></arg>
- <arg choice="opt"></arg>
- <arg choice="opt"></arg>
+ <command>b10-cfgmgr</command>
+ <arg><option>-c<replaceable>config-filename</replaceable></option></arg>
+ <arg><option>-p<replaceable>data_path</replaceable></option></arg>
</cmdsynopsis>
</refsynopsisdiv>
--->
<refsect1>
<title>DESCRIPTION</title>
@@ -87,30 +84,41 @@
<!-- TODO: does it periodically save configuration? -->
</para>
- <para>
- The daemon has no command line options. It ignores any arguments.
<!-- TODO: add a verbose or quiet switch so it is not so noisy -->
- </para>
</refsect1>
-<!--
<refsect1>
<title>ARGUMENTS</title>
- <para>
- <orderedlist numeration="loweralpha">
+
+ <para>The arguments are as follows:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>-c</option><replaceable>config-filename</replaceable>,
+ <option>--config-filename</option> <replaceable>config-filename</replaceable>
+ </term>
<listitem>
- <para>
- </para>
+ <para>The configuration database filename to use. Can be either
+ absolute or relative to data path.</para>
+ <para>Defaults to b10-config.db</para>
</listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-p</option><replaceable>data-path</replaceable>,
+ <option>--data-path</option> <replaceable>data-path</replaceable>
+ </term>
<listitem>
- <para>
- </para>
+ <para>The path where BIND 10 looks for files. The
+ configuration file is looked for here, if it is relative. If it is
+ absolute, the path is ignored.</para>
</listitem>
- </orderedlist>
- </para>
-
+ </varlistentry>
+ </variablelist>
</refsect1>
--->
+
<refsect1>
<title>FILES</title>
<!-- TODO: fix path -->
diff --git a/src/bin/cfgmgr/plugins/Makefile.am b/src/bin/cfgmgr/plugins/Makefile.am
new file mode 100644
index 0000000..529a4ed
--- /dev/null
+++ b/src/bin/cfgmgr/plugins/Makefile.am
@@ -0,0 +1,12 @@
+SUBDIRS = tests
+EXTRA_DIST = README tsig_keys.py tsig_keys.spec
+EXTRA_DIST += logging.spec b10logging.py
+
+config_plugindir = @prefix@/share/@PACKAGE@/config_plugins
+config_plugin_DATA = tsig_keys.py tsig_keys.spec
+config_plugin_DATA += b10logging.py logging.spec
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/cfgmgr/plugins/README b/src/bin/cfgmgr/plugins/README
new file mode 100644
index 0000000..27fb0c0
--- /dev/null
+++ b/src/bin/cfgmgr/plugins/README
@@ -0,0 +1,34 @@
+How to write a configuration plugin
+===================================
+
+The plugins are used to describe configuration modules that have no hosting
+process. Therefore there's no process to provide their specification and to
+check them for correctness. So the plugin takes this responsibility.
+
+Each plugin is a python file installed into the
+`@prefix@/share/@PACKAGE@/config_plugins` directory (usually
+`/usr/share/bind10/config_plugins`). It is loaded automatically at startup.
+
+The entrypoint of a plugin is function called `load()`. It should return a
+tuple, first value should be the module specification (usually instance of
+`isc.config.module_spec.ModuleSpec`, loaded by `module_spec_from_file()`).
+
+The second value is a callable object. It will be called whenever the
+configuration of module needs to be checked. The only parameter will be the new
+config (as python dictionary). To accept the new configuration, return None. If
+you return a string, it is taken as an error message. If you raise an
+exception, the config is rejected as well, however it is not considered a
+graceful rejection, but a failure of the module.
+
+So, this is how a plugin could look like:
+
+ from isc.config.module_spec import module_spec_from_file
+
+ def check(config):
+ if config['bogosity'] > 1:
+ return "Too bogus to live with"
+ else:
+ return None
+
+ def load():
+ return (module_spec_from_file('module_spec.spec'), check)
diff --git a/src/bin/cfgmgr/plugins/b10logging.py b/src/bin/cfgmgr/plugins/b10logging.py
new file mode 100644
index 0000000..e288c6d
--- /dev/null
+++ b/src/bin/cfgmgr/plugins/b10logging.py
@@ -0,0 +1,109 @@
+# Copyright (C) 2011 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# This is the configuration plugin for logging options
+# The name is 'b10logging' because logging.py is an existing module
+#
+# For a technical background, see
+# http://bind10.isc.org/wiki/LoggingCppApiDesign
+#
+
+from isc.config.module_spec import module_spec_from_file
+from isc.util.file import path_search
+from bind10_config import PLUGIN_PATHS
+spec = module_spec_from_file(path_search('logging.spec', PLUGIN_PATHS))
+
+ALLOWED_SEVERITIES = [ 'default',
+ 'debug',
+ 'info',
+ 'warn',
+ 'error',
+ 'fatal',
+ 'none' ]
+ALLOWED_DESTINATIONS = [ 'console',
+ 'file',
+ 'syslog' ]
+ALLOWED_STREAMS = [ 'stdout',
+ 'stderr' ]
+
+def check(config):
+ # Check the data layout first
+ errors=[]
+ if not spec.validate_config(False, config, errors):
+ return ' '.join(errors)
+ # The 'layout' is ok, now check for specific values
+ if 'loggers' in config:
+ for logger in config['loggers']:
+ # name should always be present
+ name = logger['name']
+ # report an error if name starts with * but not *.,
+ # or if * is not the first character.
+ # TODO: we might want to also warn or error if the
+ # logger name is not an existing module, but we can't
+ # really tell that from here at this point
+ star_pos = name.find('*')
+ if star_pos > 0 or\
+ name == '*.' or\
+ (star_pos == 0 and len(name) > 1 and name[1] != '.'):
+ errors.append("Bad logger name: '" + name + "': * can "
+ "only be used instead of the full "
+ "first-level name, e.g. '*' or "
+ "'*.subsystem'")
+
+ if 'severity' in logger and\
+ logger['severity'].lower() not in ALLOWED_SEVERITIES:
+ errors.append("bad severity value for logger " + name +
+ ": " + logger['severity'])
+ if 'output_options' in logger:
+ for output_option in logger['output_options']:
+ if 'destination' in output_option:
+ destination = output_option['destination'].lower()
+ if destination not in ALLOWED_DESTINATIONS:
+ errors.append("bad destination for logger " +
+ name + ": " + output_option['destination'])
+ else:
+ # if left to default, output is stdout, and
+ # it will not show in the updated config,
+ # so 1. we only need to check it if present,
+ # and 2. if destination is changed, so should
+ # output. So first check checks 'in', and the
+ # others 'not in' for 'output'
+ if destination == "console" and\
+ 'output' in output_option and\
+ output_option['output'] not in ALLOWED_STREAMS:
+ errors.append("bad output for logger " + name +
+ ": " + output_option['output'] +
+ ", must be stdout or stderr")
+ elif destination == "file" and\
+ ('output' not in output_option or\
+ output_option['output'] == ""):
+ errors.append("destination set to file but "
+ "output not set to any "
+ "filename for logger "
+ + name)
+ elif destination == "syslog" and\
+ 'output' not in output_option or\
+ output_option['output'] == "":
+ errors.append("destination set to syslog but "
+ "output not set to any facility"
+ " for logger " + name)
+
+ if errors:
+ return ', '.join(errors)
+ return None
+
+def load():
+ return (spec, check)
+
diff --git a/src/bin/cfgmgr/plugins/logging.spec b/src/bin/cfgmgr/plugins/logging.spec
new file mode 100644
index 0000000..e377b0e
--- /dev/null
+++ b/src/bin/cfgmgr/plugins/logging.spec
@@ -0,0 +1,81 @@
+{
+ "module_spec": {
+ "module_name": "Logging",
+ "module_description": "Logging options",
+ "config_data": [
+ {
+ "item_name": "loggers",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "logger",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "name",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ },
+ { "item_name": "severity",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "INFO"
+ },
+ { "item_name": "debuglevel",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0
+ },
+ { "item_name": "additive",
+ "item_type": "boolean",
+ "item_optional": false,
+ "item_default": false
+ },
+ { "item_name": "output_options",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "output_option",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "destination",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "console"
+ },
+ { "item_name": "output",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "stdout"
+ },
+ { "item_name": "flush",
+ "item_type": "boolean",
+ "item_optional": false,
+ "item_default": false
+ },
+ { "item_name": "maxsize",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0
+ },
+ { "item_name": "maxver",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ],
+ "commands": []
+ }
+}
diff --git a/src/bin/cfgmgr/plugins/tests/Makefile.am b/src/bin/cfgmgr/plugins/tests/Makefile.am
new file mode 100644
index 0000000..07b7a85
--- /dev/null
+++ b/src/bin/cfgmgr/plugins/tests/Makefile.am
@@ -0,0 +1,27 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
+PYTESTS = tsig_keys_test.py logging_test.py
+
+EXTRA_DIST = $(PYTESTS)
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs
+endif
+
+# test using command-line arguments, so use check-local target instead of TESTS
+check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
+ for pytest in $(PYTESTS) ; do \
+ echo Running test: $$pytest ; \
+ env B10_TEST_PLUGIN_DIR=$(abs_srcdir)/..:$(abs_builddir)/.. \
+ env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/dns/python/.libs \
+ $(LIBRARY_PATH_PLACEHOLDER) \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+ done
+
diff --git a/src/bin/cfgmgr/plugins/tests/logging_test.py b/src/bin/cfgmgr/plugins/tests/logging_test.py
new file mode 100644
index 0000000..818a596
--- /dev/null
+++ b/src/bin/cfgmgr/plugins/tests/logging_test.py
@@ -0,0 +1,135 @@
+# Copyright (C) 2011 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.
+
+# Make sure we can load the module, put it into path
+import sys
+import os
+sys.path.extend(os.environ["B10_TEST_PLUGIN_DIR"].split(':'))
+
+import b10logging
+import unittest
+
+class LoggingConfCheckTest(unittest.TestCase):
+ def test_load(self):
+ """
+ Checks the entry point returns the correct values.
+ """
+ (spec, check) = b10logging.load()
+ # It returns the checking function
+ self.assertEqual(check, b10logging.check)
+ # The plugin stores it's spec
+ self.assertEqual(spec, b10logging.spec)
+
+ def test_logger_conf(self):
+ self.assertEqual(None,
+ b10logging.check({'loggers':
+ [{'name': '*',
+ 'severity': 'DEBUG',
+ 'debuglevel': 50,
+ 'output_options':
+ [{'destination': 'file',
+ 'output': '/some/file'
+ }]
+ },
+ {'name': 'b10-resolver',
+ 'severity': 'WARN',
+ 'additive': True,
+ 'output_options':
+ [{'destination': 'console',
+ 'output': 'stderr',
+ 'flush': True
+ }]
+ },
+ {'name': 'b10-resolver.resolver',
+ 'severity': 'ERROR',
+ 'output_options': []
+ },
+ {'name': '*.cache',
+ 'severity': 'INFO'
+ }
+ ]}))
+ def do_bad_name_test(self, name):
+ err_str = "Bad logger name: '" + name + "': * can only be "\
+ "used instead of the full first-level name, e.g. "\
+ "'*' or '*.subsystem'"
+ self.assertEqual(err_str,
+ b10logging.check({'loggers':
+ [{'name': name,
+ 'severity': 'DEBUG'},
+ ]}))
+
+ def test_logger_bad_name(self):
+ self.do_bad_name_test("*.")
+ self.do_bad_name_test("*foo")
+ self.do_bad_name_test("*foo.lib")
+ self.do_bad_name_test("foo*")
+ self.do_bad_name_test("foo*.lib")
+
+ def test_logger_bad_severity(self):
+ self.assertEqual('bad severity value for logger *: BADVAL',
+ b10logging.check({'loggers':
+ [{'name': '*',
+ 'severity': 'BADVAL'}]}))
+
+ def test_logger_bad_destination(self):
+ self.assertEqual('bad destination for logger *: baddest',
+ b10logging.check({'loggers':
+ [{'name': '*',
+ 'severity': 'INFO',
+ 'output_options': [
+ { 'destination': 'baddest' }
+ ]}]}))
+
+ def test_logger_bad_console_output(self):
+ self.assertEqual('bad output for logger *: bad_output, must be stdout or stderr',
+ b10logging.check({'loggers':
+ [{'name': '*',
+ 'severity': 'INFO',
+ 'output_options': [
+ { 'destination': 'console',
+ 'output': 'bad_output'
+ }
+ ]}]}))
+
+ def test_logger_bad_file_output(self):
+ self.assertEqual('destination set to file but output not set to any filename for logger *',
+ b10logging.check({'loggers':
+ [{'name': '*',
+ 'severity': 'INFO',
+ 'output_options': [
+ { 'destination': 'file' }
+ ]}]}))
+
+ def test_logger_bad_syslog_output(self):
+ self.assertEqual('destination set to syslog but output not set to any facility for logger *',
+ b10logging.check({'loggers':
+ [{'name': '*',
+ 'severity': 'INFO',
+ 'output_options': [
+ { 'destination': 'syslog' }
+ ]}]}))
+
+ def test_logger_bad_type(self):
+ self.assertEqual('123 should be a string',
+ b10logging.check({'loggers':
+ [{'name': 123,
+ 'severity': 'INFO'}]}))
+ self.assertEqual('123 should be a string',
+ b10logging.check({'loggers':
+ [{'name': 'bind10',
+ 'severity': 123}]}))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/bin/cfgmgr/plugins/tests/tsig_keys_test.py b/src/bin/cfgmgr/plugins/tests/tsig_keys_test.py
new file mode 100644
index 0000000..808f28a
--- /dev/null
+++ b/src/bin/cfgmgr/plugins/tests/tsig_keys_test.py
@@ -0,0 +1,103 @@
+# Copyright (C) 2011 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.
+
+# Make sure we can load the module, put it into path
+import sys
+import os
+sys.path.extend(os.environ["B10_TEST_PLUGIN_DIR"].split(':'))
+
+import tsig_keys
+import unittest
+import isc.config.module_spec
+
+class TSigKeysTest(unittest.TestCase):
+ def test_load(self):
+ """
+ Checks the entry point returns the correct values.
+ """
+ (spec, check) = tsig_keys.load()
+ # It returns the checking function
+ self.assertEqual(check, tsig_keys.check)
+ # The plugin stores it's spec
+ self.assertEqual(spec, tsig_keys.spec)
+
+ def test_spec(self):
+ """
+ Checks the spec is looking sane (doesn't do really deep check here).
+ """
+ spec = tsig_keys.spec
+ # In python, we don't generally check the type of something, because
+ # of the duck typing.
+ # But this is unittest, so we check it does what we intend and
+ # supplying that's behaving the same but is different is not our
+ # intention
+ self.assertTrue(isinstance(spec, isc.config.module_spec.ModuleSpec))
+ # Correct name
+ self.assertEqual("tsig_keys", spec.get_module_name())
+ # There are no commands, nobody would handle them anyway
+ self.assertEqual([], spec.get_commands_spec())
+ # There's some nonempty configuration
+ self.assertNotEqual({}, spec.get_config_spec())
+
+ def test_missing_keys(self):
+ """
+ Test that missing keys doesn't kill us. There are just no keys there.
+ """
+ self.assertEqual(None, tsig_keys.check({}))
+
+ def test_data_empty(self):
+ """Check we accept valid config with empty set of tsig keys."""
+ self.assertEqual(None, tsig_keys.check({'keys': []}))
+
+ def test_keys_valid(self):
+ """
+ Check we accept some valid keys (we don't check all the algorithms,
+ that's the job of isc.dns.TSIGKey).
+ """
+ self.assertEqual(None, tsig_keys.check({'keys':
+ ['testkey:QklORCAxMCBpcyBjb29sCg==',
+ 'test.key:QklORCAxMCBpcyBjb29sCg==:hmac-sha1']}))
+
+ def test_keys_same_name(self):
+ """
+ Test we reject when we have multiple keys with the same name.
+ """
+ self.assertEqual("Multiple TSIG keys with name 'test.key.'",
+ tsig_keys.check({'keys':
+ ['test.key:QklORCAxMCBpcyBjb29sCg==',
+ 'test.key:b3RoZXIK']}))
+
+ def test_invalid_key(self):
+ """
+ Test we reject invalid key.
+ """
+ self.assertEqual("TSIG: Invalid TSIG key string: invalid.key",
+ tsig_keys.check({'keys': ['invalid.key']}))
+ self.assertEqual(
+ "TSIG: Unexpected end of input in BASE decoder",
+ tsig_keys.check({'keys': ['invalid.key:123']}))
+
+ def test_bad_format(self):
+ """
+ Test we fail on bad format. We don't really care much how here, though,
+ as this should not get in trough config manager anyway.
+ """
+ self.assertNotEqual(None, tsig_keys.check({'bad_name': {}}))
+ self.assertNotEqual(None, tsig_keys.check({'keys': 'not_list'}))
+ self.assertNotEqual(None, tsig_keys.check({'keys': 42}))
+ self.assertNotEqual(None, tsig_keys.check({'keys': {}}))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/bin/cfgmgr/plugins/tsig_keys.py b/src/bin/cfgmgr/plugins/tsig_keys.py
new file mode 100644
index 0000000..d57e645
--- /dev/null
+++ b/src/bin/cfgmgr/plugins/tsig_keys.py
@@ -0,0 +1,50 @@
+# Copyright (C) 2011 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# This is the plugin for tsig_keys configuration section. The TSIG keyring
+# lives there (eg. all the shared secrets, with some exceptions where there
+# are some TSIG keys elsewhere, but these should be removed soon). We do
+# sanity checking of user configuration here, simply by trying to construct
+# all the keys here.
+
+from isc.config.module_spec import module_spec_from_file
+from isc.util.file import path_search
+from pydnspp import TSIGKey, InvalidParameter
+from bind10_config import PLUGIN_PATHS
+spec = module_spec_from_file(path_search('tsig_keys.spec', PLUGIN_PATHS))
+
+def check(config):
+ # Check the data layout first
+ errors=[]
+ if not spec.validate_config(False, config, errors):
+ return ' '.join(errors)
+ # Get the list of keys, if any
+ keys = config.get('keys', [])
+ # Run through them, check they can be constructed and there are no
+ # duplicates
+ keyNames = set()
+ for key in keys:
+ try:
+ name = str(TSIGKey(key).get_key_name())
+ except InvalidParameter as e:
+ return "TSIG: " + str(e)
+ if name in keyNames:
+ return "Multiple TSIG keys with name '" + name + "'"
+ keyNames.add(name)
+ # No error found, so let's assume it's OK
+ return None
+
+def load():
+ return (spec, check)
diff --git a/src/bin/cfgmgr/plugins/tsig_keys.spec b/src/bin/cfgmgr/plugins/tsig_keys.spec
new file mode 100644
index 0000000..e558dd2
--- /dev/null
+++ b/src/bin/cfgmgr/plugins/tsig_keys.spec
@@ -0,0 +1,21 @@
+{
+ "module_spec": {
+ "module_name": "tsig_keys",
+ "module_description": "The TSIG keyring is stored here",
+ "config_data": [
+ {
+ "item_name": "keys",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "key",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ }
+ }
+ ],
+ "commands": []
+ }
+}
diff --git a/src/bin/cfgmgr/tests/Makefile.am b/src/bin/cfgmgr/tests/Makefile.am
index 16e6223..bd67241 100644
--- a/src/bin/cfgmgr/tests/Makefile.am
+++ b/src/bin/cfgmgr/tests/Makefile.am
@@ -1,7 +1,14 @@
PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
PYTESTS = b10-cfgmgr_test.py
-EXTRA_DIST = $(PYTESTS)
+EXTRA_DIST = $(PYTESTS) testdata/plugins/testplugin.py
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
@@ -12,6 +19,13 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
- env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cfgmgr \
+ env TESTDATA_PATH=$(abs_srcdir)/testdata \
+ $(LIBRARY_PATH_PLACEHOLDER) \
+ env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/python/isc/config \
$(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
done
+
+CLEANDIRS = testdata/plugins/__pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
index 8847b18..ea5fc8b 100644
--- a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
+++ b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
@@ -20,15 +20,18 @@
import unittest
import os
import sys
+import bind10_config
+from isc.testutils.parse_args import OptsError, TestOptParser
class MyConfigManager:
- def __init__(self, path):
+ def __init__(self, path, filename):
self._path = path
self.read_config_called = False
self.notify_boss_called = False
self.run_called = False
self.write_config_called = False
self.running = True
+ self.virtual_modules = []
def read_config(self):
self.read_config_called = True
@@ -42,6 +45,24 @@ class MyConfigManager:
def write_config(self):
self.write_config_called = True
+ def set_virtual_module(self, spec, function):
+ self.virtual_modules.append((spec, function))
+
+class TestPlugins(unittest.TestCase):
+ def test_load_plugins(self):
+ """Test we can successfully find and load the mock plugin."""
+ # Let it load the plugin
+ b = __import__("b10-cfgmgr")
+ # The parameters aren't important for this test
+ cm = MyConfigManager(None, None)
+ b.load_plugins(os.environ['TESTDATA_PATH'] + os.sep + 'plugins', cm)
+ # Check exactly one plugin was loaded and the right data were fed into
+ # the cm
+ self.assertEqual(len(cm.virtual_modules), 1)
+ (spec, check) = cm.virtual_modules[0]
+ self.assertEqual(spec.get_module_name(), "mock_config_plugin")
+ self.assertEqual(check(None), "Mock config plugin")
+
class TestConfigManagerStartup(unittest.TestCase):
def test_cfgmgr(self):
# some creative module use;
@@ -49,13 +70,24 @@ class TestConfigManagerStartup(unittest.TestCase):
# this also gives us the chance to override the imported
# module ConfigManager in it.
b = __import__("b10-cfgmgr")
+ orig_load = b.load_plugins
+ b.PLUGIN_PATHS = ["/plugin/path"]
+ self.loaded_plugins = False
+ def load_plugins(path, cm):
+ # Check it's called with proper arguments
+ self.assertEqual(path, "/plugin/path")
+ self.assertTrue(isinstance(cm, MyConfigManager))
+ self.loaded_plugins = True
+ b.load_plugins = load_plugins
b.ConfigManager = MyConfigManager
b.main()
+ b.load_plugins = orig_load
self.assertTrue(b.cm.read_config_called)
self.assertTrue(b.cm.notify_boss_called)
self.assertTrue(b.cm.run_called)
+ self.assertTrue(self.loaded_plugins)
# if there are no changes, config is not written
self.assertFalse(b.cm.write_config_called)
@@ -79,15 +111,81 @@ class TestConfigManagerStartup(unittest.TestCase):
env_var = os.environ["B10_FROM_SOURCE"]
os.environ["B10_FROM_SOURCE"] = tmp_env_var
+ bind10_config.reload()
b = __import__("b10-cfgmgr", globals(), locals())
+ b.PLUGIN_PATH = [] # It's enough to test plugins in one test
b.ConfigManager = MyConfigManager
self.assertEqual(tmp_env_var, b.DATA_PATH)
if env_var != None:
os.environ["B10_FROM_SOURCE"] = env_var
+ bind10_config.reload()
sys.modules.pop("b10-cfgmgr")
+class TestParseArgs(unittest.TestCase):
+ """
+ Test for the parsing of command line arguments. We provide a different
+ array to parse instead.
+ """
+
+ def test_defaults(self):
+ """
+ Test the default values when no options are provided.
+ """
+ # Pass it empty array, not our arguments
+ b = __import__("b10-cfgmgr")
+ parsed = b.parse_options([], TestOptParser)
+ self.assertEqual(b.DATA_PATH, parsed.data_path)
+ self.assertEqual(b.DEFAULT_CONFIG_FILE, parsed.config_file)
+
+ def test_wrong_args(self):
+ """
+ Test it fails when we pass invalid option.
+ """
+ b = __import__("b10-cfgmgr")
+ self.assertRaises(OptsError, b.parse_options, ['--wrong-option'],
+ TestOptParser)
+
+ def test_not_arg(self):
+ """
+ Test it fails when there's an argument that's not option
+ (eg. without -- at the beginning).
+ """
+ b = __import__("b10-cfgmgr")
+ self.assertRaises(OptsError, b.parse_options, ['not-option'],
+ TestOptParser)
+
+ def test_datapath(self):
+ """
+ Test overwriting the data path.
+ """
+ b = __import__("b10-cfgmgr")
+ parsed = b.parse_options(['--data-path=/path'], TestOptParser)
+ self.assertEqual('/path', parsed.data_path)
+ self.assertEqual(b.DEFAULT_CONFIG_FILE, parsed.config_file)
+ parsed = b.parse_options(['-p', '/path'], TestOptParser)
+ self.assertEqual('/path', parsed.data_path)
+ self.assertEqual(b.DEFAULT_CONFIG_FILE, parsed.config_file)
+ self.assertRaises(OptsError, b.parse_options, ['-p'], TestOptParser)
+ self.assertRaises(OptsError, b.parse_options, ['--data-path'],
+ TestOptParser)
+
+ def test_db_filename(self):
+ """
+ Test setting the configuration database file.
+ """
+ b = __import__("b10-cfgmgr")
+ parsed = b.parse_options(['--config-filename=filename'],
+ TestOptParser)
+ self.assertEqual(b.DATA_PATH, parsed.data_path)
+ self.assertEqual("filename", parsed.config_file)
+ parsed = b.parse_options(['-c', 'filename'], TestOptParser)
+ self.assertEqual(b.DATA_PATH, parsed.data_path)
+ self.assertEqual("filename", parsed.config_file)
+ self.assertRaises(OptsError, b.parse_options, ['-c'], TestOptParser)
+ self.assertRaises(OptsError, b.parse_options, ['--config-filename'],
+ TestOptParser)
if __name__ == '__main__':
unittest.main()
diff --git a/src/bin/cfgmgr/tests/testdata/plugins/testplugin.py b/src/bin/cfgmgr/tests/testdata/plugins/testplugin.py
new file mode 100644
index 0000000..a50eefe
--- /dev/null
+++ b/src/bin/cfgmgr/tests/testdata/plugins/testplugin.py
@@ -0,0 +1,34 @@
+# Copyright (C) 2011 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.
+
+# A test plugin. It does mostly nothing, just provides a function we can
+# recognize from the test. However, it looks like a real plugin, with the
+# (almost) correct interface, even when it's not used.
+
+class MockSpec:
+ """Mock spec, contains no data, it can only provide it's name.
+ It'll not really be used for anything."""
+ def get_module_name(self):
+ return "mock_config_plugin"
+
+def mock_check_config(config):
+ """Mock function to check config. Does nothing, only returns
+ an "error" string to indicate it's this one."""
+ return "Mock config plugin"
+
+def load():
+ """When a plugin is loaded, this is called to provide the spec and
+ checking function."""
+ return (MockSpec(), mock_check_config)
diff --git a/src/bin/cmdctl/Makefile.am b/src/bin/cmdctl/Makefile.am
index 04cf5e2..fcd23f8 100644
--- a/src/bin/cmdctl/Makefile.am
+++ b/src/bin/cmdctl/Makefile.am
@@ -3,6 +3,7 @@ SUBDIRS = . tests
pkglibexecdir = $(libexecdir)/@PACKAGE@
pkglibexec_SCRIPTS = b10-cmdctl
+pyexec_DATA = cmdctl_messages.py
b10_cmdctldir = $(pkgdatadir)
@@ -18,10 +19,10 @@ b10_cmdctl_DATA += cmdctl.spec
EXTRA_DIST = $(CMDCTL_CONFIGURATIONS)
-CLEANFILES= b10-cmdctl cmdctl.pyc cmdctl.spec
+CLEANFILES= b10-cmdctl cmdctl.pyc cmdctl.spec cmdctl_messages.py cmdctl_messages.pyc
man_MANS = b10-cmdctl.8
-EXTRA_DIST += $(man_MANS) b10-cmdctl.xml
+EXTRA_DIST += $(man_MANS) b10-cmdctl.xml cmdctl_messages.mes
if ENABLE_MAN
@@ -33,20 +34,29 @@ endif
cmdctl.spec: cmdctl.spec.pre
$(SED) -e "s|@@SYSCONFDIR@@|$(sysconfdir)|" cmdctl.spec.pre >$@
+cmdctl_messages.py: cmdctl_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/cmdctl/cmdctl_messages.mes
+
# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-b10-cmdctl: cmdctl.py
+b10-cmdctl: cmdctl.py cmdctl_messages.py
$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" cmdctl.py >$@
chmod a+x $@
if INSTALL_CONFIGURATIONS
-# TODO: permissions handled later
+# Below we intentionally use ${INSTALL} -m 640 instead of $(INSTALL_DATA)
+# because these file will contain sensitive information.
install-data-local:
$(mkinstalldirs) $(DESTDIR)/@sysconfdir@/@PACKAGE@
for f in $(CMDCTL_CONFIGURATIONS) ; do \
if test ! -f $(DESTDIR)$(sysconfdir)/@PACKAGE@/$$f; then \
- $(INSTALL_DATA) $(srcdir)/$$f $(DESTDIR)$(sysconfdir)/@PACKAGE@/ ; \
+ ${INSTALL} -m 640 $(srcdir)/$$f $(DESTDIR)$(sysconfdir)/@PACKAGE@/ ; \
fi ; \
done
endif
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/cmdctl/cmdctl.py.in b/src/bin/cmdctl/cmdctl.py.in
index b996596..778d38f 100755
--- a/src/bin/cmdctl/cmdctl.py.in
+++ b/src/bin/cmdctl/cmdctl.py.in
@@ -1,7 +1,6 @@
#!@PYTHON@
# Copyright (C) 2010 Internet Systems Consortium.
-# Copyright (C) 2010 CZ NIC
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -48,6 +47,18 @@ import isc.net.parse
from optparse import OptionParser, OptionValueError
from hashlib import sha1
from isc.util import socketserver_mixin
+from cmdctl_messages import *
+
+# TODO: these debug-levels are hard-coded here; we are planning on
+# creating a general set of debug levels, see ticket #1074. When done,
+# we should remove these values and use the general ones in the
+# logger.debug calls
+
+# Debug level for communication with BIND10
+DBG_CMDCTL_MESSAGING = 30
+
+isc.log.init("b10-cmdctl")
+logger = isc.log.Logger("cmdctl")
try:
import threading
@@ -174,7 +185,8 @@ class SecureHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
if not user_name:
return False, ["need user name"]
if not self.server.get_user_info(user_name):
- return False, ["user doesn't exist"]
+ logger.info(CMDCTL_NO_SUCH_USER, user_name)
+ return False, ["username or password error"]
user_pwd = user_info.get('password')
if not user_pwd:
@@ -182,7 +194,8 @@ class SecureHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
local_info = self.server.get_user_info(user_name)
pwd_hashval = sha1((user_pwd + local_info[1]).encode())
if pwd_hashval.hexdigest() != local_info[0]:
- return False, ["password doesn't match"]
+ logger.info(CMDCTL_BAD_PASSWORD, user_name)
+ return False, ["username or password error"]
return True, None
@@ -239,7 +252,8 @@ class CommandControl():
self._cc = isc.cc.Session()
self._module_cc = isc.config.ModuleCCSession(SPECFILE_LOCATION,
self.config_handler,
- self.command_handler)
+ self.command_handler,
+ None, True)
self._module_name = self._module_cc.get_module_spec().get_module_name()
self._cmdctl_config_data = self._module_cc.get_full_config()
self._module_cc.start()
@@ -282,7 +296,7 @@ class CommandControl():
errstr = 'unknown config item: ' + key
if errstr != None:
- self.log_info('Fail to apply config data, ' + errstr)
+ logger.error(CMDCTL_BAD_CONFIG_DATA, errstr);
return ccsession.create_answer(1, errstr)
return ccsession.create_answer(0)
@@ -388,8 +402,8 @@ class CommandControl():
'''Send the command from bindctl to proper module. '''
errstr = 'unknown error'
answer = None
- if self._verbose:
- self.log_info("Begin send command '%s' to module '%s'" %(command_name, module_name))
+ logger.debug(DBG_CMDCTL_MESSAGING, CMDCTL_SEND_COMMAND,
+ command_name, module_name)
if module_name == self._module_name:
# Process the command sent to cmdctl directly.
@@ -397,15 +411,14 @@ class CommandControl():
else:
msg = ccsession.create_command(command_name, params)
seq = self._cc.group_sendmsg(msg, module_name)
+ logger.debug(DBG_CMDCTL_MESSAGING, CMDCTL_COMMAND_SENT,
+ command_name, module_name)
#TODO, it may be blocked, msqg need to add a new interface waiting in timeout.
try:
answer, env = self._cc.group_recvmsg(False, seq)
except isc.cc.session.SessionTimeout:
errstr = "Module '%s' not responding" % module_name
- if self._verbose:
- self.log_info("Finish send command '%s' to module '%s'" % (command_name, module_name))
-
if answer:
try:
rcode, arg = ccsession.parse_answer(answer)
@@ -416,16 +429,13 @@ class CommandControl():
else:
return rcode, {}
else:
- # TODO: exception
errstr = str(answer['result'][1])
except ccsession.ModuleCCSessionError as mcse:
errstr = str("Error in ccsession answer:") + str(mcse)
- self.log_info(errstr)
+
+ logger.error(CMDCTL_COMMAND_ERROR, command_name, module_name, errstr)
return 1, {'error': errstr}
- def log_info(self, msg):
- sys.stdout.write("[b10-cmdctl] %s\n" % str(msg))
-
def get_cmdctl_config_data(self):
''' If running in source code tree, use keyfile, certificate
and user accounts file in source code. '''
@@ -482,14 +492,15 @@ class SecureHTTPServer(socketserver_mixin.NoPollMixIn,
for row in reader:
self._user_infos[row[0]] = [row[1], row[2]]
except (IOError, IndexError) as e:
- self.log_info("Fail to read user database, %s" % e)
+ logger.error(CMDCTL_USER_DATABASE_READ_ERROR,
+ accounts_file, e)
finally:
if csvfile:
csvfile.close()
self._accounts_file = accounts_file
if len(self._user_infos) == 0:
- self.log_info("Fail to get user information, will deny any user")
+ logger.error(CMDCTL_NO_USER_ENTRIES_READ)
def get_user_info(self, username):
'''Get user's salt and hashed string. If the user
@@ -521,7 +532,7 @@ class SecureHTTPServer(socketserver_mixin.NoPollMixIn,
ssl_version = ssl.PROTOCOL_SSLv23)
return ssl_sock
except (ssl.SSLError, CmdctlException) as err :
- self.log_info("Deny client's connection because %s" % str(err))
+ logger.info(CMDCTL_SSL_SETUP_FAILURE_USER_DENIED, err)
self.close_request(sock)
# raise socket error to finish the request
raise socket.error
@@ -548,9 +559,6 @@ class SecureHTTPServer(socketserver_mixin.NoPollMixIn,
def send_command_to_module(self, module_name, command_name, params):
return self.cmdctl.send_command_with_check(module_name, command_name, params)
- def log_info(self, msg):
- sys.stdout.write("[b10-cmdctl] %s\n" % str(msg))
-
httpd = None
def signal_handler(signal, frame):
@@ -608,15 +616,13 @@ if __name__ == '__main__':
run(options.addr, options.port, options.idle_timeout, options.verbose)
result = 0
except isc.cc.SessionError as err:
- sys.stderr.write("[b10-cmdctl] Error creating b10-cmdctl, "
- "is the command channel daemon running?\n")
+ logger.fatal(CMDCTL_CC_SESSION_ERROR, err)
except isc.cc.SessionTimeout:
- sys.stderr.write("[b10-cmdctl] Error creating b10-cmdctl, "
- "is the configuration manager running?\n")
+ logger.fatal(CMDCTL_CC_SESSION_TIMEOUT)
except KeyboardInterrupt:
- sys.stderr.write("[b10-cmdctl] exit from Cmdctl\n")
+ logger.info(CMDCTL_STOPPED_BY_KEYBOARD)
except CmdctlException as err:
- sys.stderr.write("[b10-cmdctl] " + str(err) + "\n")
+ logger.fatal(CMDCTL_UNCAUGHT_EXCEPTION, err);
if httpd:
httpd.shutdown()
diff --git a/src/bin/cmdctl/cmdctl_messages.mes b/src/bin/cmdctl/cmdctl_messages.mes
new file mode 100644
index 0000000..55b941f
--- /dev/null
+++ b/src/bin/cmdctl/cmdctl_messages.mes
@@ -0,0 +1,81 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# No namespace declaration - these constants go in the global namespace
+# of the cmdctl_messages python module.
+
+% CMDCTL_BAD_CONFIG_DATA error in config data: %1
+There was an error reading the updated configuration data. The specific
+error is printed.
+
+% CMDCTL_BAD_PASSWORD bad password for user: %1
+A login attempt was made to b10-cmdctl, but the password was wrong.
+Users can be managed with the tool b10-cmdctl-usermgr.
+
+% CMDCTL_CC_SESSION_ERROR error reading from cc channel: %1
+There was a problem reading from the command and control channel. The
+most likely cause is that the message bus daemon is not running.
+
+% CMDCTL_CC_SESSION_TIMEOUT timeout on cc channel
+A timeout occurred when waiting for essential data from the cc session.
+This usually occurs when b10-cfgmgr is not running or not responding.
+Since we are waiting for essential information, this is a fatal error,
+and the cmdctl daemon will now shut down.
+
+% CMDCTL_COMMAND_ERROR error in command %1 to module %2: %3
+An error was encountered sending the given command to the given module.
+Either there was a communication problem with the module, or the module
+was not able to process the command, and sent back an error. The
+specific error is printed in the message.
+
+% CMDCTL_COMMAND_SENT command '%1' to module '%2' was sent
+This debug message indicates that the given command has been sent to
+the given module.
+
+% CMDCTL_NO_SUCH_USER username not found in user database: %1
+A login attempt was made to b10-cmdctl, but the username was not known.
+Users can be added with the tool b10-cmdctl-usermgr.
+
+% CMDCTL_NO_USER_ENTRIES_READ failed to read user information, all users will be denied
+The b10-cmdctl daemon was unable to find any user data in the user
+database file. Either it was unable to read the file (in which case
+this message follows a message CMDCTL_USER_DATABASE_READ_ERROR
+containing a specific error), or the file was empty. Users can be added
+with the tool b10-cmdctl-usermgr.
+
+% CMDCTL_SEND_COMMAND sending command %1 to module %2
+This debug message indicates that the given command is being sent to
+the given module.
+
+% CMDCTL_SSL_SETUP_FAILURE_USER_DENIED failed to create an SSL connection (user denied): %1
+The user was denied because the SSL connection could not successfully
+be set up. The specific error is given in the log message. Possible
+causes may be that the ssl request itself was bad, or the local key or
+certificate file could not be read.
+
+% CMDCTL_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
+There was a keyboard interrupt signal to stop the cmdctl daemon. The
+daemon will now shut down.
+
+% CMDCTL_UNCAUGHT_EXCEPTION uncaught exception: %1
+The b10-cdmctl daemon encountered an uncaught exception and
+will now shut down. This is indicative of a programming error and
+should not happen under normal circumstances. The exception message
+is printed.
+
+% CMDCTL_USER_DATABASE_READ_ERROR failed to read user database file %1: %2
+The b10-cmdctl daemon was unable to read the user database file. The
+file may be unreadable for the daemon, or it may be corrupted. In the
+latter case, it can be recreated with b10-cmdctl-usermgr. The specific
+error is printed in the log message.
diff --git a/src/bin/cmdctl/tests/Makefile.am b/src/bin/cmdctl/tests/Makefile.am
index 6a4d7d4..e4ec9d4 100644
--- a/src/bin/cmdctl/tests/Makefile.am
+++ b/src/bin/cmdctl/tests/Makefile.am
@@ -2,6 +2,13 @@ PYCOVERAGE_RUN=@PYCOVERAGE_RUN@
PYTESTS = cmdctl_test.py
EXTRA_DIST = $(PYTESTS)
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
if ENABLE_PYTHON_COVERAGE
@@ -11,6 +18,7 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
+ $(LIBRARY_PATH_PLACEHOLDER) \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cmdctl \
CMDCTL_SPEC_PATH=$(abs_top_builddir)/src/bin/cmdctl \
CMDCTL_SRC_PATH=$(abs_top_srcdir)/src/bin/cmdctl \
diff --git a/src/bin/cmdctl/tests/cmdctl_test.py b/src/bin/cmdctl/tests/cmdctl_test.py
index 5463c36..e77c529 100644
--- a/src/bin/cmdctl/tests/cmdctl_test.py
+++ b/src/bin/cmdctl/tests/cmdctl_test.py
@@ -173,7 +173,7 @@ class TestSecureHTTPRequestHandler(unittest.TestCase):
self.handler.server._user_infos['root'] = ['aa', 'aaa']
ret, msg = self.handler._check_user_name_and_pwd()
self.assertFalse(ret)
- self.assertEqual(msg, ['password doesn\'t match'])
+ self.assertEqual(msg, ['username or password error'])
def test_check_user_name_and_pwd_2(self):
user_info = {'username':'root', 'password':'abc123'}
@@ -214,7 +214,7 @@ class TestSecureHTTPRequestHandler(unittest.TestCase):
ret, msg = self.handler._check_user_name_and_pwd()
self.assertFalse(ret)
- self.assertEqual(msg, ['user doesn\'t exist'])
+ self.assertEqual(msg, ['username or password error'])
def test_do_POST(self):
self.handler.headers = {}
diff --git a/src/bin/dhcp6/.gitignore b/src/bin/dhcp6/.gitignore
new file mode 100644
index 0000000..6a6060b
--- /dev/null
+++ b/src/bin/dhcp6/.gitignore
@@ -0,0 +1,9 @@
+*~
+Makefile
+Makefile.in
+*.o
+.deps
+.libs
+b10-dhcp6
+spec_config.h
+spec_config.h.pre
diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
new file mode 100644
index 0000000..8d341cb
--- /dev/null
+++ b/src/bin/dhcp6/Makefile.am
@@ -0,0 +1,52 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/cc -I$(top_builddir)/src/lib/cc
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/asiolink
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+
+CLEANFILES = *.gcno *.gcda spec_config.h
+
+man_MANS = b10-dhcp6.8
+EXTRA_DIST = $(man_MANS) dhcp6.spec
+
+#if ENABLE_MAN
+#b10-dhcp6.8: b10-dhcp6.xml
+# xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-dhcp6.xml
+#endif
+
+spec_config.h: spec_config.h.pre
+ $(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
+
+BUILT_SOURCES = spec_config.h
+pkglibexec_PROGRAMS = b10-dhcp6
+b10_dhcp6_SOURCES = main.cc
+b10_dhcp6_SOURCES += dhcp6.h
+b10_dhcp6_LDADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/liblog.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
+b10_dhcp6_LDADD += $(SQLITE_LIBS)
+
+# TODO: config.h.in is wrong because doesn't honor pkgdatadir
+# and can't use @datadir@ because doesn't expand default ${prefix}
+b10_dhcp6dir = $(pkgdatadir)
+b10_dhcp6_DATA = dhcp6.spec
+
diff --git a/src/bin/dhcp6/b10-dhcp6.8 b/src/bin/dhcp6/b10-dhcp6.8
new file mode 100644
index 0000000..14a5621
--- /dev/null
+++ b/src/bin/dhcp6/b10-dhcp6.8
@@ -0,0 +1,50 @@
+'\" t
+.\" Title: b10-dhpc6
+.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
+.\" Date: March 8, 2011
+.\" Manual: BIND10
+.\" Source: BIND10
+.\" Language: English
+.\"
+.TH "B10\-DHCP6" "8" "March 8, 2011" "BIND10" "BIND10"
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+b10-dhcp6 \- DHCPv6 daemon in BIND10 architecture
+.SH "SYNOPSIS"
+.HP \w'\fBb10\-dhcp6\fR\ 'u
+\fBb10\-dhcp6\fR [\fB\-u\ \fR\fB\fIusername\fR\fR] [\fB\-v\fR]
+.SH "DESCRIPTION"
+.PP
+The
+\fBb10\-dhcp6\fR
+daemon will provide DHCPv6 server implementation when it becomes functional.
+.PP
+.SH "SEE ALSO"
+.PP
+
+\fBb10-cfgmgr\fR(8),
+\fBb10-loadzone\fR(8),
+\fBb10-msgq\fR(8),
+\fBb10-stats\fR(8),
+\fBb10-zonemgr\fR(8),
+\fBbind10\fR(8),
+BIND 10 Guide\&.
+.SH "HISTORY"
+.PP
+The
+\fBb10\-dhcp6\fR
+daemon was first coded in June 2011\&.
+.SH "COPYRIGHT"
+.br
+Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC")
+.br
diff --git a/src/bin/dhcp6/dhcp6.h b/src/bin/dhcp6/dhcp6.h
new file mode 100644
index 0000000..322b06c
--- /dev/null
+++ b/src/bin/dhcp6/dhcp6.h
@@ -0,0 +1,213 @@
+/* dhcp6.h
+
+ DHCPv6 Protocol structures... */
+
+/*
+ * Copyright (c) 2006-2011 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND 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.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info at isc.org>
+ * https://www.isc.org/
+ */
+
+
+/* DHCPv6 Option codes: */
+
+#define D6O_CLIENTID 1 /* RFC3315 */
+#define D6O_SERVERID 2
+#define D6O_IA_NA 3
+#define D6O_IA_TA 4
+#define D6O_IAADDR 5
+#define D6O_ORO 6
+#define D6O_PREFERENCE 7
+#define D6O_ELAPSED_TIME 8
+#define D6O_RELAY_MSG 9
+/* Option code 10 unassigned. */
+#define D6O_AUTH 11
+#define D6O_UNICAST 12
+#define D6O_STATUS_CODE 13
+#define D6O_RAPID_COMMIT 14
+#define D6O_USER_CLASS 15
+#define D6O_VENDOR_CLASS 16
+#define D6O_VENDOR_OPTS 17
+#define D6O_INTERFACE_ID 18
+#define D6O_RECONF_MSG 19
+#define D6O_RECONF_ACCEPT 20
+#define D6O_SIP_SERVERS_DNS 21 /* RFC3319 */
+#define D6O_SIP_SERVERS_ADDR 22 /* RFC3319 */
+#define D6O_NAME_SERVERS 23 /* RFC3646 */
+#define D6O_DOMAIN_SEARCH 24 /* RFC3646 */
+#define D6O_IA_PD 25 /* RFC3633 */
+#define D6O_IAPREFIX 26 /* RFC3633 */
+#define D6O_NIS_SERVERS 27 /* RFC3898 */
+#define D6O_NISP_SERVERS 28 /* RFC3898 */
+#define D6O_NIS_DOMAIN_NAME 29 /* RFC3898 */
+#define D6O_NISP_DOMAIN_NAME 30 /* RFC3898 */
+#define D6O_SNTP_SERVERS 31 /* RFC4075 */
+#define D6O_INFORMATION_REFRESH_TIME 32 /* RFC4242 */
+#define D6O_BCMCS_SERVER_D 33 /* RFC4280 */
+#define D6O_BCMCS_SERVER_A 34 /* RFC4280 */
+/* 35 is unassigned */
+#define D6O_GEOCONF_CIVIC 36 /* RFC4776 */
+#define D6O_REMOTE_ID 37 /* RFC4649 */
+#define D6O_SUBSCRIBER_ID 38 /* RFC4580 */
+#define D6O_CLIENT_FQDN 39 /* RFC4704 */
+#define D6O_PANA_AGENT 40 /* paa-option */
+#define D6O_NEW_POSIX_TIMEZONE 41 /* RFC4833 */
+#define D6O_NEW_TZDB_TIMEZONE 42 /* RFC4833 */
+#define D6O_ERO 43 /* RFC4994 */
+#define D6O_LQ_QUERY 44 /* RFC5007 */
+#define D6O_CLIENT_DATA 45 /* RFC5007 */
+#define D6O_CLT_TIME 46 /* RFC5007 */
+#define D6O_LQ_RELAY_DATA 47 /* RFC5007 */
+#define D6O_LQ_CLIENT_LINK 48 /* RFC5007 */
+
+/*
+ * Status Codes, from RFC 3315 section 24.4, and RFC 3633, 5007.
+ */
+#define STATUS_Success 0
+#define STATUS_UnspecFail 1
+#define STATUS_NoAddrsAvail 2
+#define STATUS_NoBinding 3
+#define STATUS_NotOnLink 4
+#define STATUS_UseMulticast 5
+#define STATUS_NoPrefixAvail 6
+#define STATUS_UnknownQueryType 7
+#define STATUS_MalformedQuery 8
+#define STATUS_NotConfigured 9
+#define STATUS_NotAllowed 10
+
+/*
+ * DHCPv6 message types, defined in section 5.3 of RFC 3315
+ */
+#define DHCPV6_SOLICIT 1
+#define DHCPV6_ADVERTISE 2
+#define DHCPV6_REQUEST 3
+#define DHCPV6_CONFIRM 4
+#define DHCPV6_RENEW 5
+#define DHCPV6_REBIND 6
+#define DHCPV6_REPLY 7
+#define DHCPV6_RELEASE 8
+#define DHCPV6_DECLINE 9
+#define DHCPV6_RECONFIGURE 10
+#define DHCPV6_INFORMATION_REQUEST 11
+#define DHCPV6_RELAY_FORW 12
+#define DHCPV6_RELAY_REPL 13
+#define DHCPV6_LEASEQUERY 14
+#define DHCPV6_LEASEQUERY_REPLY 15
+
+extern const char *dhcpv6_type_names[];
+extern const int dhcpv6_type_name_max;
+
+/* DUID type definitions (RFC3315 section 9).
+ */
+#define DUID_LLT 1
+#define DUID_EN 2
+#define DUID_LL 3
+
+/* Offsets into IA_*'s where Option spaces commence. */
+#define IA_NA_OFFSET 12 /* IAID, T1, T2, all 4 octets each */
+#define IA_TA_OFFSET 4 /* IAID only, 4 octets */
+#define IA_PD_OFFSET 12 /* IAID, T1, T2, all 4 octets each */
+
+/* Offset into IAADDR's where Option spaces commence. */
+#define IAADDR_OFFSET 24
+
+/* Offset into IAPREFIX's where Option spaces commence. */
+#define IAPREFIX_OFFSET 25
+
+/* Offset into LQ_QUERY's where Option spaces commence. */
+#define LQ_QUERY_OFFSET 17
+
+/*
+ * DHCPv6 well-known multicast addressess, from section 5.1 of RFC 3315
+ */
+#define All_DHCP_Relay_Agents_and_Servers "FF02::1:2"
+#define All_DHCP_Servers "FF05::1:3"
+
+/*
+ * DHCPv6 Retransmission Constants (RFC3315 section 5.5, RFC 5007)
+ */
+
+#define SOL_MAX_DELAY 1
+#define SOL_TIMEOUT 1
+#define SOL_MAX_RT 120
+#define REQ_TIMEOUT 1
+#define REQ_MAX_RT 30
+#define REQ_MAX_RC 10
+#define CNF_MAX_DELAY 1
+#define CNF_TIMEOUT 1
+#define CNF_MAX_RT 4
+#define CNF_MAX_RD 10
+#define REN_TIMEOUT 10
+#define REN_MAX_RT 600
+#define REB_TIMEOUT 10
+#define REB_MAX_RT 600
+#define INF_MAX_DELAY 1
+#define INF_TIMEOUT 1
+#define INF_MAX_RT 120
+#define REL_TIMEOUT 1
+#define REL_MAX_RC 5
+#define DEC_TIMEOUT 1
+#define DEC_MAX_RC 5
+#define REC_TIMEOUT 2
+#define REC_MAX_RC 8
+#define HOP_COUNT_LIMIT 32
+#define LQ6_TIMEOUT 1
+#define LQ6_MAX_RT 10
+#define LQ6_MAX_RC 5
+
+/*
+ * Normal packet format, defined in section 6 of RFC 3315
+ */
+struct dhcpv6_packet {
+ unsigned char msg_type;
+ unsigned char transaction_id[3];
+ unsigned char options[FLEXIBLE_ARRAY_MEMBER];
+};
+
+/* Offset into DHCPV6 Reply packets where Options spaces commence. */
+#define REPLY_OPTIONS_INDEX 4
+
+/*
+ * Relay packet format, defined in section 7 of RFC 3315
+ */
+struct dhcpv6_relay_packet {
+ unsigned char msg_type;
+ unsigned char hop_count;
+ unsigned char link_address[16];
+ unsigned char peer_address[16];
+ unsigned char options[FLEXIBLE_ARRAY_MEMBER];
+};
+
+/* Leasequery query-types (RFC 5007) */
+
+#define LQ6QT_BY_ADDRESS 1
+#define LQ6QT_BY_CLIENTID 2
+
+/*
+ * DUID time starts 2000-01-01.
+ * This constant is the number of seconds since 1970-01-01,
+ * when the Unix epoch began.
+ */
+#define DUID_TIME_EPOCH 946684800
+
+/* Information-Request Time option (RFC 4242) */
+
+#define IRT_DEFAULT 86400
+#define IRT_MINIMUM 600
+
diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec
new file mode 100644
index 0000000..0e7e852
--- /dev/null
+++ b/src/bin/dhcp6/dhcp6.spec
@@ -0,0 +1,14 @@
+{
+ "module_spec": {
+ "module_name": "dhcp6",
+ "module_description": "DHCPv6 daemon",
+ "config_data": [
+ { "item_name": "interface",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "eth0"
+ }
+ ],
+ "commands": []
+ }
+}
diff --git a/src/bin/dhcp6/main.cc b/src/bin/dhcp6/main.cc
new file mode 100644
index 0000000..75af3d9
--- /dev/null
+++ b/src/bin/dhcp6/main.cc
@@ -0,0 +1,122 @@
+// Copyright (C) 2009-2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <cassert>
+#include <iostream>
+
+#include <exceptions/exceptions.h>
+#include <cc/session.h>
+#include <config/ccsession.h>
+
+#include <util/buffer.h>
+#include <log/dummylog.h>
+
+#include <dhcp6/spec_config.h>
+
+
+using namespace std;
+using namespace isc::util;
+using namespace isc::data;
+using namespace isc::cc;
+using namespace isc::config;
+using namespace isc::util;
+
+namespace {
+
+bool verbose_mode = false;
+
+void
+usage() {
+ cerr << "Usage: b10-dhcp6 [-u user] [-v]"
+ << endl;
+ cerr << "\t-u: change process UID to the specified user" << endl;
+ cerr << "\t-v: verbose output" << endl;
+ exit(1);
+}
+} // end of anonymous namespace
+
+int
+main(int argc, char* argv[]) {
+ int ch;
+ const char* uid = NULL;
+ bool cache = true;
+
+ while ((ch = getopt(argc, argv, ":nu:v")) != -1) {
+ switch (ch) {
+ case 'n':
+ cache = false;
+ break;
+ case 'u':
+ uid = optarg;
+ break;
+ case 'v':
+ verbose_mode = true;
+ isc::log::denabled = true;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ if (argc - optind > 0) {
+ usage();
+ }
+
+ int ret = 0;
+
+ // XXX: we should eventually pass io_service here.
+#if 0
+ Session* cc_session = NULL;
+ Session* xfrin_session = NULL;
+ Session* statistics_session = NULL;
+ bool xfrin_session_established = false; // XXX (see Trac #287)
+ bool statistics_session_established = false; // XXX (see Trac #287)
+ ModuleCCSession* config_session = NULL;
+#endif
+ try {
+ string specfile;
+ if (getenv("B10_FROM_BUILD")) {
+ specfile = string(getenv("B10_FROM_BUILD")) +
+ "/src/bin/auth/dhcp6.spec";
+ } else {
+ specfile = string(DHCP6_SPECFILE_LOCATION);
+ }
+
+ // auth_server = new AuthSrv(cache, xfrout_client);
+ // auth_server->setVerbose(verbose_mode);
+ cout << "[b10-dhcp6] Initiating DHCPv6 operation." << endl;
+
+ } catch (const std::exception& ex) {
+ cerr << "[b10-dhcp6] Server failed: " << ex.what() << endl;
+ ret = 1;
+ }
+
+ while (true) {
+ sleep(10);
+ cout << "[b10-dhcp6] I'm alive." << endl;
+ }
+
+ return (ret);
+}
diff --git a/src/bin/dhcp6/spec_config.h.pre.in b/src/bin/dhcp6/spec_config.h.pre.in
new file mode 100644
index 0000000..42775b2
--- /dev/null
+++ b/src/bin/dhcp6/spec_config.h.pre.in
@@ -0,0 +1,15 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#define DHCP6_SPECFILE_LOCATION "@prefix@/share/@PACKAGE@/dhcp6.spec"
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
new file mode 100644
index 0000000..a35284f
--- /dev/null
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -0,0 +1,22 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
+#PYTESTS = args_test.py bind10_test.py
+# NOTE: this has a generated test found in the builddir
+PYTESTS = dhcp6_test.py
+EXTRA_DIST = $(PYTESTS)
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
+# test using command-line arguments, so use check-local target instead of TESTS
+check-local:
+ for pytest in $(PYTESTS) ; do \
+ echo Running test: $$pytest ; \
+ env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/bind10 \
+ $(LIBRARY_PATH_PLACEHOLDER) \
+ BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+ done
diff --git a/src/bin/dhcp6/tests/dhcp6_test.py b/src/bin/dhcp6/tests/dhcp6_test.py
new file mode 100644
index 0000000..61ec009
--- /dev/null
+++ b/src/bin/dhcp6/tests/dhcp6_test.py
@@ -0,0 +1,65 @@
+# Copyright (C) 2011 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.
+
+from bind10 import ProcessInfo, parse_args, dump_pid, unlink_pid_file, _BASETIME
+
+import unittest
+import sys
+import os
+import signal
+import socket
+from isc.net.addr import IPAddr
+import time
+import isc
+
+class TestDhcpv6Daemon(unittest.TestCase):
+ def setUp(self):
+ # redirect stdout to a pipe so we can check that our
+ # process spawning is doing the right thing with stdout
+ self.old_stdout = os.dup(sys.stdout.fileno())
+ self.pipes = os.pipe()
+ os.dup2(self.pipes[1], sys.stdout.fileno())
+ os.close(self.pipes[1])
+ # note that we use dup2() to restore the original stdout
+ # to the main program ASAP in each test... this prevents
+ # hangs reading from the child process (as the pipe is only
+ # open in the child), and also insures nice pretty output
+
+ def tearDown(self):
+ # clean up our stdout munging
+ os.dup2(self.old_stdout, sys.stdout.fileno())
+ os.close(self.pipes[0])
+
+ def test_alive(self):
+ """
+ Simple test. Checks that b10-dhcp6 can be started and prints out info
+ about starting DHCPv6 operation.
+ """
+ pi = ProcessInfo('Test Process', [ '../b10-dhcp6' , '-v' ])
+ pi.spawn()
+ time.sleep(1)
+ os.dup2(self.old_stdout, sys.stdout.fileno())
+ self.assertNotEqual(pi.process, None)
+ self.assertTrue(type(pi.pid) is int)
+ output = os.read(self.pipes[0], 4096)
+ self.assertEqual( str(output).count("[b10-dhcp6] Initiating DHCPv6 operation."), 1)
+
+ # kill this process
+ # XXX: b10-dhcp6 is too dumb to understand 'shutdown' command for now,
+ # so let's just kill the bastard
+ os.kill(pi.pid, signal.SIGTERM)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/bin/host/Makefile.am b/src/bin/host/Makefile.am
index 0758cb9..ec34ce7 100644
--- a/src/bin/host/Makefile.am
+++ b/src/bin/host/Makefile.am
@@ -10,17 +10,20 @@ endif
CLEANFILES = *.gcno *.gcda
-bin_PROGRAMS = host
-host_SOURCES = host.cc
-host_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
-host_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-
-#man_MANS = host.1
-#EXTRA_DIST = $(man_MANS) host.xml
-#
-#if ENABLE_MAN
-#
-#host.1: host.xml
-# xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/host.xml
-#
-#endif
+bin_PROGRAMS = b10-host
+b10_host_SOURCES = host.cc
+b10_host_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
+b10_host_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+
+man_MANS = b10-host.1
+EXTRA_DIST = $(man_MANS) b10-host.xml
+
+.PHONY: man
+if ENABLE_MAN
+
+man: b10-host.1
+
+b10-host.1: b10-host.xml
+ xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-host.xml
+
+endif
diff --git a/src/bin/host/README b/src/bin/host/README
index d493a95..5cc4068 100644
--- a/src/bin/host/README
+++ b/src/bin/host/README
@@ -1,14 +1,4 @@
-Rewriting host(1) in C++ from scratch using BIND 10's libdns.
+Rewriting host(1) in C++ from scratch using BIND 10's libdns++.
-Initial functionality:
-
- host _hostname_ [server]
-
-By default, it looks up the A, AAAA, and MX record sets.
-
-Note it doesn't use /etc/resolv.conf at this time.
-The default name server used is 127.0.0.1.
-
- -r disable recursive processing
- -t _type_ specific query type
- -v enable verbose output mode, including elapsed time
+The bugs and incompatibilities are listed in the manual page
+and in the source code.
diff --git a/src/bin/host/b10-host.1 b/src/bin/host/b10-host.1
new file mode 100644
index 0000000..ed0068b
--- /dev/null
+++ b/src/bin/host/b10-host.1
@@ -0,0 +1,122 @@
+'\" t
+.\" Title: b10-host
+.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
+.\" Date: May 4, 2011
+.\" Manual: BIND10
+.\" Source: BIND10
+.\" Language: English
+.\"
+.TH "B10\-HOST" "1" "May 4, 2011" "BIND10" "BIND10"
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+b10-host \- DNS lookup utility
+.SH "SYNOPSIS"
+.HP \w'\fBb10\-host\fR\ 'u
+\fBb10\-host\fR [\fB\-a\fR] [\fB\-c\ \fR\fB\fIclass\fR\fR] [\fB\-d\fR] [\fB\-p\ \fR\fB\fIport\fR\fR] [\fB\-r\fR] [\fB\-t\ \fR\fB\fItype\fR\fR] [\fB\-v\fR] [\fIname\fR] [\fB\fIserver\fR\fR]
+.SH "DESCRIPTION"
+.PP
+The
+\fBb10\-host\fR
+utility does DNS lookups\&. Its initial goal is to be a
+\fBhost\fR(1)
+clone, but also add a few features useful for BIND 10 development testing\&.
+.PP
+By default, it looks up the A, AAAA, and MX record sets for the
+\fIname\fR\&. Optionally, you may select a name server to query against by adding the
+\fIserver\fR
+argument\&.
+.SH "OPTIONS"
+.PP
+The arguments are as follows:
+.PP
+\fB\-a\fR
+.RS 4
+Enable verbose mode and do a query for type ANY\&. (If the
+\fB\-t\fR
+option is also set, then the ANY query is not done, but it still uses verbose mode\&.)
+.RE
+.PP
+\fB\-c \fR\fB\fIclass\fR\fR
+.RS 4
+Define the class for the query\&. The default is IN (Internet)\&.
+.RE
+.PP
+\fB\-d\fR
+.RS 4
+Enable verbose output mode, including elapsed time in milliseconds\&. Verbose mode shows the header, question, answer, authority, and additional sections (if provided)\&. (Same as
+\fB\-v\fR\&.)
+.RE
+.PP
+\fB\-p \fR\fB\fIport\fR\fR
+.RS 4
+Select an alternative port for the query\&. This may be a number or a service name\&. The default is 53 (domain)\&. This is not a standard feature of
+\fBhost\fR(1)\&.
+.RE
+.PP
+\fB\-r\fR
+.RS 4
+Disable recursive processing by not setting the Recursion Desired flag in the query\&.
+.RE
+.PP
+\fB\-t \fR\fB\fItype\fR\fR
+.RS 4
+Select a specific resource record type for the query\&. By default, it looks up the A, AAAA, and MX record sets\&.
+(This overrides the
+\fB\-a\fR
+option\&.)
+.RE
+.PP
+\fB\-v\fR
+.RS 4
+Same as
+\fB\-d\fR
+option\&.
+.RE
+.SH "COMPATIBILITY / BUGS"
+.PP
+
+\fBb10\-host\fR
+does not do reverse lookups by default yet (by detecting if name is a IPv4 or IPv6 address)\&.
+.PP
+Unknown
+\fB\-c\fR
+class or
+\fB\-t\fR
+type causes
+\fBb10\-host\fR
+to Abort\&.
+.PP
+Not all types are supported yet for formatting\&. Not all switches are supported yet\&.
+.PP
+It doesn\'t use
+/etc/resolv\&.conf
+at this time\&. The default name server used is 127\&.0\&.0\&.1\&.
+.PP
+
+\fBb10\-host\fR
+does not do reverse lookups by default yet (by detecting if name is a IPv4 or IPv6 address)\&.
+.PP
+
+\fB\-p\fR
+is not a standard feature\&.
+.SH "HISTORY"
+.PP
+The C++ version of
+\fBb10\-host\fR
+was started in October 2009 by Jeremy C\&. Reed of ISC\&. Its usage and output were based on the standard
+\fBhost\fR
+command\&.
+.SH "COPYRIGHT"
+.br
+Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC")
+.br
diff --git a/src/bin/host/b10-host.xml b/src/bin/host/b10-host.xml
new file mode 100644
index 0000000..7da07dd
--- /dev/null
+++ b/src/bin/host/b10-host.xml
@@ -0,0 +1,201 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+ [<!ENTITY mdash "—">]>
+<!--
+ - Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<!-- $Id$ -->
+<refentry>
+
+ <refentryinfo>
+ <date>May 4, 2011</date>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>b10-host</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo>BIND10</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>b10-host</refname>
+ <refpurpose>DNS lookup utility</refpurpose>
+ </refnamediv>
+
+ <docinfo>
+ <copyright>
+ <year>2011</year>
+ <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+ </copyright>
+ </docinfo>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>b10-host</command>
+ <arg><option>-a</option></arg>
+ <arg><option>-c <replaceable>class</replaceable></option></arg>
+ <arg><option>-d</option></arg>
+ <arg><option>-p <replaceable>port</replaceable></option></arg>
+ <arg><option>-r</option></arg>
+ <arg><option>-t <replaceable>type</replaceable></option></arg>
+ <arg><option>-v</option></arg>
+ <arg><replaceable>name</replaceable></arg>
+ <arg><option><replaceable>server</replaceable></option></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>DESCRIPTION</title>
+ <para>
+ The <command>b10-host</command> utility does DNS lookups.
+ Its initial goal is to be a
+ <citerefentry><refentrytitle>host</refentrytitle>
+ <manvolnum>1</manvolnum></citerefentry>
+ clone, but also add a few features useful for BIND 10 development
+ testing.
+ </para>
+
+ <para>
+ By default, it looks up the A, AAAA, and MX record sets for the
+ <replaceable>name</replaceable>.
+ Optionally, you may select a name server to query against by adding
+ the <replaceable>server</replaceable> argument.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>OPTIONS</title>
+
+ <para>The arguments are as follows:</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><option>-a</option></term>
+ <listitem><para>
+ Enable verbose mode and do a query for type ANY.
+ (If the <option>-t</option> option is also set, then the
+ ANY query is not done, but it still uses verbose mode.)
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-c <replaceable>class</replaceable></option></term>
+ <listitem><para>
+ Define the class for the query.
+ The default is IN (Internet).
+<!-- TODO: bug if class is unknown causes seg fault and possible core dump -->
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-d</option></term>
+ <listitem><para>
+ Enable verbose output mode, including elapsed time in
+ milliseconds.
+ Verbose mode shows the header, question, answer, authority,
+ and additional sections (if provided).
+ (Same as <option>-v</option>.)
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-p <replaceable>port</replaceable></option></term>
+ <listitem><para>
+ Select an alternative port for the query.
+ This may be a number or a service name.
+ The default is 53 (domain).
+ This is not a standard feature of
+ <citerefentry><refentrytitle>host</refentrytitle>
+ <manvolnum>1</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-r</option></term>
+ <listitem><para>
+ Disable recursive processing by not setting the
+ Recursion Desired flag in the query.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-t <replaceable>type</replaceable></option></term>
+ <listitem><para>
+ Select a specific resource record type for the query.
+ By default, it looks up the A, AAAA, and MX record sets.
+<!-- TODO: bug if class is unknown causes seg fault and possible core dump -->
+ (This overrides the <option>-a</option> option.)
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-v</option></term>
+ <listitem><para>
+ Same as <option>-d</option> option.
+ </para></listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>COMPATIBILITY / BUGS</title>
+ <para>
+ <command>b10-host</command> does not do reverse lookups by
+ default yet (by detecting if name is a IPv4 or IPv6 address).
+ </para>
+
+ <para>
+ Unknown <option>-c</option> class or <option>-t</option> type
+ causes <command>b10-host</command> to Abort.
+ </para>
+
+ <para>
+ Not all types are supported yet for formatting.
+ Not all switches are supported yet.
+ </para>
+
+ <para>
+ It doesn't use <filename>/etc/resolv.conf</filename> at this time.
+ The default name server used is 127.0.0.1.
+ </para>
+
+ <para>
+ <command>b10-host</command> does not do reverse lookups by
+ default yet (by detecting if name is a IPv4 or IPv6 address).
+ </para>
+
+ <para>
+ <option>-p</option> is not a standard feature.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>HISTORY</title>
+ <para>
+ The C++ version of <command>b10-host</command> was started in
+ October 2009 by Jeremy C. Reed of ISC.
+ Its usage and output were based on the standard <command>host</command>
+ command.
+ </para>
+ </refsect1>
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->
diff --git a/src/bin/host/host.cc b/src/bin/host/host.cc
index c513b5a..f0df0c8 100644
--- a/src/bin/host/host.cc
+++ b/src/bin/host/host.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -24,7 +24,8 @@
#include <string>
#include <iostream>
-#include <dns/buffer.h>
+#include <util/buffer.h>
+
#include <dns/name.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
@@ -37,18 +38,22 @@
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
namespace {
char* dns_type = NULL; // not set, so A, AAAA, MX
const char* server = "127.0.0.1";
const char* server_port = "53";
-int verbose = 0;
-int first_time = 1;
-bool recursive_bit = true;
+const char* dns_class = "IN";
+bool verbose = false;
+bool dns_any = false;
+int first_time = 1;
+bool recursive_bit = true;
struct timeval before_time, after_time;
int
-host_lookup(const char* const name, const char* const type) {
+host_lookup(const char* const name, const char* const dns_class,
+ const char* const type, bool any) {
Message msg(Message::RENDER);
@@ -62,8 +67,8 @@ host_lookup(const char* const name, const char* const type) {
}
msg.addQuestion(Question(Name(name),
- RRClass::IN(), // IN class only for now
- RRType(type))); // if NULL then:
+ RRClass(dns_class),
+ any ? RRType::ANY() : RRType(type))); // if NULL then:
OutputBuffer obuffer(512);
MessageRenderer renderer(obuffer);
@@ -125,18 +130,29 @@ host_lookup(const char* const name, const char* const type) {
rmsg.fromWire(ibuffer);
if (!verbose) {
+ string description = "";
for (RRsetIterator it =
rmsg.beginSection(Message::SECTION_ANSWER);
it != rmsg.endSection(Message::SECTION_ANSWER);
++it) {
- if ((*it)->getType() != RRType::A()) {
- continue;
+
+ if ((*it)->getType() == RRType::A()) {
+ description = "has address";
+ }
+ else if ((*it)->getType() == RRType::AAAA()) {
+ description = "has IPv6 address";
+ }
+ else if ((*it)->getType() == RRType::MX()) {
+ description = "mail is handled by";
+ }
+ else if ((*it)->getType() == RRType::TXT()) {
+ description = "descriptive text";
}
RdataIteratorPtr rit = (*it)->getRdataIterator();
for (; !rit->isLast(); rit->next()) {
// instead of using my name, maybe use returned label?
- cout << name << " has address " <<
+ cout << name << " " << description << " " <<
(*rit).getCurrent().toText() << endl;
}
}
@@ -157,13 +173,19 @@ host_lookup(const char* const name, const char* const type) {
// TODO: if NXDOMAIN, host(1) doesn't show HEADER
// Host hsdjkfhksjhdfkj not found: 3(NXDOMAIN)
- // TODO: figure out the new libdns way to test if NXDOMAIN
+ // TODO: test if NXDOMAIN
std::cout << "Received " << cc <<
" bytes in " << elapsed_time << " ms\n";
// TODO: " bytes from 127.0.0.1#53 in 0 ms
} //verbose
+/*
+TODO: handle InvalidRRClass
+TODO: handle invalid type exception
+ } catch (InvalidType ivt) {
+ std::cerr << "invalid type:" << ivt.what();
+*/
} catch (const exception& ex) {
std::cerr << "parse failed for " <<
string(name) << "/" << type << ": " << ex.what() << std::endl;
@@ -182,26 +204,36 @@ int
main(int argc, char* argv[]) {
int c;
- while ((c = getopt(argc, argv, "p:rt:v")) != -1)
+ while ((c = getopt(argc, argv, "ac:dp:rt:v")) != -1)
switch (c) {
+ case 'a':
+ dns_any = true;
+ verbose = true;
+ break;
+ case 'c':
+ dns_class = optarg;
+ break;
+ // p for port is a non-standard switch
+ case 'p':
+ server_port = optarg;
+ break;
case 'r':
recursive_bit = false;
break;
case 't':
dns_type = optarg;
break;
- case 'p':
- server_port = optarg;
- break;
+ case 'd':
+ // drop through to v, because debug and verbose are equivalent
case 'v':
- verbose = 1;
+ verbose = true;
break;
}
argc -= optind;
argv += optind;
if (argc < 1) {
- cout << "Usage: host [-vr] [-t type] hostname [server]\n";
+ cout << "Usage: host [-adprv] [-c class] [-t type] hostname [server]\n";
exit(1);
}
@@ -210,12 +242,13 @@ main(int argc, char* argv[]) {
}
if (dns_type == NULL) {
- host_lookup(argv[0], "A");
+ host_lookup(argv[0], dns_class, "A", dns_any);
// TODO: don't do next if A doesn't exist
- host_lookup(argv[0], "AAAA");
- host_lookup(argv[0], "MX");
+ host_lookup(argv[0], dns_class, "AAAA", dns_any);
+ host_lookup(argv[0], dns_class, "MX", dns_any);
} else {
- host_lookup(argv[0], dns_type);
+ // -t overrides -a, regardless of order
+ host_lookup(argv[0], dns_class, dns_type, false);
}
return (0);
}
diff --git a/src/bin/loadzone/run_loadzone.sh.in b/src/bin/loadzone/run_loadzone.sh.in
old mode 100644
new mode 100755
index b7ac19f..95de396
--- a/src/bin/loadzone/run_loadzone.sh.in
+++ b/src/bin/loadzone/run_loadzone.sh.in
@@ -21,6 +21,14 @@ export PYTHON_EXEC
PYTHONPATH=@abs_top_builddir@/src/lib/python
export PYTHONPATH
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@
+if test $SET_ENV_LIBRARY_PATH = yes; then
+ @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@
+ export @ENV_LIBRARY_PATH@
+fi
+
BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
export BIND10_MSGQ_SOCKET_FILE
diff --git a/src/bin/loadzone/tests/correct/Makefile.am b/src/bin/loadzone/tests/correct/Makefile.am
index a90cab2..3507bfa 100644
--- a/src/bin/loadzone/tests/correct/Makefile.am
+++ b/src/bin/loadzone/tests/correct/Makefile.am
@@ -13,8 +13,15 @@ EXTRA_DIST += ttl2.db
EXTRA_DIST += ttlext.db
EXTRA_DIST += example.db
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
# TODO: maybe use TESTS?
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
echo Running test: correct_test.sh
- $(SHELL) $(abs_builddir)/correct_test.sh
+ $(LIBRARY_PATH_PLACEHOLDER) $(SHELL) $(abs_builddir)/correct_test.sh
diff --git a/src/bin/loadzone/tests/error/Makefile.am b/src/bin/loadzone/tests/error/Makefile.am
index bbeec07..87bb1cf 100644
--- a/src/bin/loadzone/tests/error/Makefile.am
+++ b/src/bin/loadzone/tests/error/Makefile.am
@@ -12,8 +12,15 @@ EXTRA_DIST += keyerror3.db
EXTRA_DIST += originerr1.db
EXTRA_DIST += originerr2.db
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
# TODO: use TESTS ?
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
echo Running test: error_test.sh
- $(SHELL) $(abs_builddir)/error_test.sh
+ $(LIBRARY_PATH_PLACEHOLDER) $(SHELL) $(abs_builddir)/error_test.sh
diff --git a/src/bin/msgq/Makefile.am b/src/bin/msgq/Makefile.am
index 61d4f23..0eebf00 100644
--- a/src/bin/msgq/Makefile.am
+++ b/src/bin/msgq/Makefile.am
@@ -20,3 +20,8 @@ endif
b10-msgq: msgq.py
$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" msgq.py >$@
chmod a+x $@
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/msgq/tests/Makefile.am b/src/bin/msgq/tests/Makefile.am
index 0bbb964..50c1e6e 100644
--- a/src/bin/msgq/tests/Makefile.am
+++ b/src/bin/msgq/tests/Makefile.am
@@ -2,6 +2,13 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
PYTESTS = msgq_test.py
EXTRA_DIST = $(PYTESTS)
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
if ENABLE_PYTHON_COVERAGE
@@ -11,6 +18,7 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
+ $(LIBRARY_PATH_PLACEHOLDER) \
env PYTHONPATH=$(abs_top_builddir)/src/bin/msgq:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
BIND10_TEST_SOCKET_FILE=$(builddir)/test_msgq_socket.sock \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
diff --git a/src/bin/msgq/tests/msgq_test.py b/src/bin/msgq/tests/msgq_test.py
index 59fcf41..fe4f7d4 100644
--- a/src/bin/msgq/tests/msgq_test.py
+++ b/src/bin/msgq/tests/msgq_test.py
@@ -117,7 +117,7 @@ class SendNonblock(unittest.TestCase):
Tests that the whole thing will not get blocked if someone does not read.
"""
- def terminate_check(self, task, timeout = 10):
+ def terminate_check(self, task, timeout=30):
"""
Runs task in separate process (task is a function) and checks
it terminates sooner than timeout.
@@ -132,7 +132,7 @@ class SendNonblock(unittest.TestCase):
task()
# If we got here, then everything worked well and in time
# In that case, we terminate successfully
- sys.exit(0) # needs exit code
+ os._exit(0) # needs exit code
else:
(pid, status) = os.waitpid(task_pid, 0)
self.assertEqual(0, status,
@@ -194,7 +194,7 @@ class SendNonblock(unittest.TestCase):
length = len(data)
queue_pid = os.fork()
if queue_pid == 0:
- signal.alarm(30)
+ signal.alarm(120)
msgq.setup_poller()
msgq.register_socket(queue)
msgq.run()
@@ -202,7 +202,7 @@ class SendNonblock(unittest.TestCase):
try:
def killall(signum, frame):
os.kill(queue_pid, signal.SIGTERM)
- sys.exit(1)
+ os._exit(1)
signal.signal(signal.SIGALRM, killall)
msg = msgq.preparemsg({"type" : "ping"}, data)
now = time.clock()
diff --git a/src/bin/resolver/Makefile.am b/src/bin/resolver/Makefile.am
index 54e15bd..3f5f049 100644
--- a/src/bin/resolver/Makefile.am
+++ b/src/bin/resolver/Makefile.am
@@ -4,6 +4,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/cc -I$(top_builddir)/src/lib/cc
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiodns
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/asiodns
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink
AM_CPPFLAGS += -I$(top_builddir)/src/lib/asiolink
AM_CPPFLAGS += $(BOOST_INCLUDES)
@@ -16,10 +18,12 @@ endif
pkglibexecdir = $(libexecdir)/@PACKAGE@
-CLEANFILES = *.gcno *.gcda resolver.spec spec_config.h
+CLEANFILES = *.gcno *.gcda
+CLEANFILES += resolver.spec spec_config.h
+CLEANFILES += resolver_messages.cc resolver_messages.h
man_MANS = b10-resolver.8
-EXTRA_DIST = $(man_MANS) b10-resolver.xml
+EXTRA_DIST = $(man_MANS) b10-resolver.xml resolver_messages.mes
if ENABLE_MAN
@@ -34,17 +38,31 @@ resolver.spec: resolver.spec.pre
spec_config.h: spec_config.h.pre
$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
-BUILT_SOURCES = spec_config.h
+# Define rule to build logging source files from message file
+resolver_messages.h resolver_messages.cc: resolver_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/resolver/resolver_messages.mes
+
+
+BUILT_SOURCES = spec_config.h resolver_messages.cc resolver_messages.h
+
pkglibexec_PROGRAMS = b10-resolver
b10_resolver_SOURCES = resolver.cc resolver.h
+b10_resolver_SOURCES += resolver_log.cc resolver_log.h
b10_resolver_SOURCES += response_scrubber.cc response_scrubber.h
b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/change_user.h
b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/common.h
b10_resolver_SOURCES += main.cc
+
+nodist_b10_resolver_SOURCES = resolver_messages.cc resolver_messages.h
+
+
b10_resolver_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
b10_resolver_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
b10_resolver_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/util/libutil.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/acl/libdnsacl.la
b10_resolver_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
b10_resolver_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
b10_resolver_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
b10_resolver_LDADD += $(top_builddir)/src/lib/log/liblog.la
diff --git a/src/bin/resolver/main.cc b/src/bin/resolver/main.cc
index bbb7d13..d9c30b9 100644
--- a/src/bin/resolver/main.cc
+++ b/src/bin/resolver/main.cc
@@ -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 <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
@@ -25,11 +27,13 @@
#include <boost/foreach.hpp>
+#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
+#include <dns/rcode.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
@@ -48,14 +52,16 @@
#include <cache/resolver_cache.h>
#include <nsas/nameserver_address_store.h>
-#include <log/dummylog.h>
+#include <log/logger_support.h>
+#include <log/logger_level.h>
+#include "resolver_log.h"
using namespace std;
using namespace isc::cc;
using namespace isc::config;
using namespace isc::data;
-using isc::log::dlog;
-using namespace asiolink;
+using namespace isc::asiodns;
+using namespace isc::asiolink;
namespace {
@@ -74,7 +80,7 @@ my_command_handler(const string& command, ConstElementPtr args) {
ConstElementPtr answer = createAnswer();
if (command == "print_message") {
- cout << args << endl;
+ LOG_INFO(resolver_logger, RESOLVER_PRINT_COMMAND).arg(args);
/* let's add that message to our answer as well */
answer = createAnswer(0, args);
} else if (command == "shutdown") {
@@ -95,7 +101,7 @@ usage() {
int
main(int argc, char* argv[]) {
- isc::log::dprefix = "b10-resolver";
+ bool verbose = false;
int ch;
const char* uid = NULL;
@@ -105,7 +111,7 @@ main(int argc, char* argv[]) {
uid = optarg;
break;
case 'v':
- isc::log::denabled = true;
+ verbose = true;
break;
case '?':
default:
@@ -117,13 +123,18 @@ main(int argc, char* argv[]) {
usage();
}
- if (isc::log::denabled) { // Show the command line
- string cmdline("Command line:");
- for (int i = 0; i < argc; ++ i) {
- cmdline = cmdline + " " + argv[i];
- }
- dlog(cmdline);
+ // Until proper logging comes along, initialize the logging with the
+ // temporary initLogger() code. If verbose, we'll use maximum verbosity.
+ isc::log::initLogger("b10-resolver",
+ (verbose ? isc::log::DEBUG : isc::log::INFO),
+ isc::log::MAX_DEBUG_LEVEL, NULL);
+
+ // Print the starting message
+ string cmdline = argv[0];
+ for (int i = 1; i < argc; ++ i) {
+ cmdline = cmdline + " " + argv[i];
}
+ LOG_INFO(resolver_logger, RESOLVER_STARTING).arg(cmdline);
int ret = 0;
@@ -139,7 +150,7 @@ main(int argc, char* argv[]) {
}
resolver = boost::shared_ptr<Resolver>(new Resolver());
- dlog("Server created.");
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_CREATED);
SimpleCallback* checkin = resolver->getCheckinProvider();
DNSLookup* lookup = resolver->getDNSLookupProvider();
@@ -180,6 +191,7 @@ main(int argc, char* argv[]) {
isc::dns::RRClass::IN(),
"2001:500:3::42"));
isc::dns::MessagePtr priming_result(new isc::dns::Message(isc::dns::Message::RENDER));
+ priming_result->setRcode(isc::dns::Rcode::NOERROR());
priming_result->addQuestion(root_question);
priming_result->addRRset(isc::dns::Message::SECTION_ANSWER, root_ns_rrset);
priming_result->addRRset(isc::dns::Message::SECTION_ADDITIONAL, root_a_rrset);
@@ -191,15 +203,14 @@ main(int argc, char* argv[]) {
DNSService dns_service(io_service, checkin, lookup, answer);
resolver->setDNSService(dns_service);
- dlog("IOService created.");
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_SERVICE_CREATED);
cc_session = new Session(io_service.get_io_service());
- dlog("Configuration session channel created.");
-
config_session = new ModuleCCSession(specfile, *cc_session,
my_config_handler,
- my_command_handler);
- dlog("Configuration channel established.");
+ my_command_handler,
+ true, true);
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_CONFIG_CHANNEL);
// FIXME: This does not belong here, but inside Boss
if (uid != NULL) {
@@ -207,17 +218,22 @@ main(int argc, char* argv[]) {
}
resolver->setConfigSession(config_session);
- dlog("Config loaded");
+ // Install all initial configurations. If loading configuration
+ // fails, it will be logged, but we start the server anyway, giving
+ // the user a second chance to correct the configuration.
+ resolver->updateConfig(config_session->getFullConfig());
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_CONFIG_LOADED);
- dlog("Server started.");
+ LOG_INFO(resolver_logger, RESOLVER_STARTED);
io_service.run();
} catch (const std::exception& ex) {
- dlog(string("Server failed: ") + ex.what(),true);
+ LOG_FATAL(resolver_logger, RESOLVER_FAILED).arg(ex.what());
ret = 1;
}
delete config_session;
delete cc_session;
+ LOG_INFO(resolver_logger, RESOLVER_SHUTDOWN);
return (ret);
}
diff --git a/src/bin/resolver/resolver.cc b/src/bin/resolver/resolver.cc
index 7656e2b..fb9621b 100644
--- a/src/bin/resolver/resolver.cc
+++ b/src/bin/resolver/resolver.cc
@@ -14,24 +14,32 @@
#include <config.h>
+#include <stdint.h>
#include <netinet/in.h>
#include <algorithm>
#include <vector>
#include <cassert>
-#include <asiolink/asiolink.h>
-
+#include <boost/shared_ptr.hpp>
#include <boost/foreach.hpp>
-#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <acl/dns.h>
+#include <acl/loader.h>
+
+#include <asiodns/asiodns.h>
+#include <asiolink/asiolink.h>
#include <config/ccsession.h>
#include <exceptions/exceptions.h>
+#include <util/buffer.h>
+
#include <dns/opcode.h>
#include <dns/rcode.h>
-#include <dns/buffer.h>
#include <dns/exceptions.h>
#include <dns/name.h>
#include <dns/question.h>
@@ -39,22 +47,28 @@
#include <dns/rrttl.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
+
+#include <server_common/client.h>
#include <server_common/portconfig.h>
#include <resolve/recursive_query.h>
-#include <log/dummylog.h>
-
-#include <resolver/resolver.h>
+#include "resolver.h"
+#include "resolver_log.h"
using namespace std;
+using boost::shared_ptr;
using namespace isc;
+using namespace isc::util;
+using namespace isc::acl;
+using isc::acl::dns::RequestACL;
using namespace isc::dns;
using namespace isc::data;
using namespace isc::config;
-using isc::log::dlog;
-using namespace asiolink;
+using namespace isc::asiodns;
+using namespace isc::asiolink;
+using namespace isc::server_common;
using namespace isc::server_common::portconfig;
class ResolverImpl {
@@ -69,6 +83,9 @@ public:
client_timeout_(4000),
lookup_timeout_(30000),
retries_(3),
+ // we apply "reject all" (implicit default of the loader) ACL by
+ // default:
+ query_acl_(acl::dns::getRequestLoader().load(Element::fromJSON("[]"))),
rec_query_(NULL)
{}
@@ -81,7 +98,7 @@ public:
isc::cache::ResolverCache& cache)
{
assert(!rec_query_); // queryShutdown must be called first
- dlog("Query setup");
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_QUERY_SETUP);
rec_query_ = new RecursiveQuery(dnss,
nsas, cache,
upstream_,
@@ -97,7 +114,8 @@ public:
// (this is not a safety check, just to prevent logging of
// actions that are not performed
if (rec_query_) {
- dlog("Query shutdown");
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT,
+ RESOLVER_QUERY_SHUTDOWN);
delete rec_query_;
rec_query_ = NULL;
}
@@ -109,13 +127,12 @@ public:
upstream_ = upstream;
if (dnss) {
if (!upstream_.empty()) {
- dlog("Setting forward addresses:");
BOOST_FOREACH(const AddressPair& address, upstream) {
- dlog(" " + address.first + ":" +
- boost::lexical_cast<string>(address.second));
+ LOG_INFO(resolver_logger, RESOLVER_FORWARD_ADDRESS)
+ .arg(address.first).arg(address.second);
}
} else {
- dlog("No forward addresses, running in recursive mode");
+ LOG_INFO(resolver_logger, RESOLVER_RECURSIVE);
}
}
}
@@ -126,13 +143,12 @@ public:
upstream_root_ = upstream_root;
if (dnss) {
if (!upstream_root_.empty()) {
- dlog("Setting root addresses:");
BOOST_FOREACH(const AddressPair& address, upstream_root) {
- dlog(" " + address.first + ":" +
- boost::lexical_cast<string>(address.second));
+ LOG_INFO(resolver_logger, RESOLVER_SET_ROOT_ADDRESS)
+ .arg(address.first).arg(address.second);
}
} else {
- dlog("No root addresses");
+ LOG_WARN(resolver_logger, RESOLVER_NO_ROOT_ADDRESS);
}
}
}
@@ -140,10 +156,20 @@ public:
void resolve(const isc::dns::QuestionPtr& question,
const isc::resolve::ResolverInterface::CallbackPtr& callback);
- void processNormalQuery(const Question& question,
- MessagePtr answer_message,
- OutputBufferPtr buffer,
- DNSServer* server);
+ enum NormalQueryResult { RECURSION, DROPPED, ERROR };
+ NormalQueryResult processNormalQuery(const IOMessage& io_message,
+ MessagePtr query_message,
+ MessagePtr answer_message,
+ OutputBufferPtr buffer,
+ DNSServer* server);
+
+ const RequestACL& getQueryACL() const {
+ return (*query_acl_);
+ }
+
+ void setQueryACL(shared_ptr<const RequestACL> new_acl) {
+ query_acl_ = new_acl;
+ }
/// Currently non-configurable, but will be.
static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
@@ -168,6 +194,8 @@ public:
unsigned retries_;
private:
+ /// ACL on incoming queries
+ shared_ptr<const RequestACL> query_acl_;
/// Object to handle upstream queries
RecursiveQuery* rec_query_;
@@ -182,8 +210,6 @@ class QuestionInserter {
public:
QuestionInserter(MessagePtr message) : message_(message) {}
void operator()(const QuestionPtr question) {
- dlog(string("Adding question ") + question->getName().toText() +
- " to message");
message_->addQuestion(question);
}
MessagePtr message_;
@@ -230,10 +256,6 @@ makeErrorMessage(MessagePtr message, MessagePtr answer_message,
message->setRcode(rcode);
MessageRenderer renderer(*buffer);
message->toWire(renderer);
-
- dlog(string("Sending an error response (") +
- boost::lexical_cast<string>(renderer.getLength()) + " bytes):\n" +
- message->toText());
}
// This is a derived class of \c DNSLookup, to serve as a
@@ -295,7 +317,7 @@ public:
edns_response->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
answer_message->setEDNS(edns_response);
}
-
+
if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
if (edns) {
renderer.setLengthLimit(edns->getUDPSize());
@@ -308,9 +330,9 @@ public:
answer_message->toWire(renderer);
- dlog(string("sending a response (") +
- boost::lexical_cast<string>(renderer.getLength()) + "bytes): \n" +
- answer_message->toText());
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_DETAIL,
+ RESOLVER_DNS_MESSAGE_SENT)
+ .arg(renderer.getLength()).arg(*answer_message);
}
};
@@ -331,9 +353,13 @@ private:
Resolver::Resolver() :
impl_(new ResolverImpl()),
+ dnss_(NULL),
checkin_(new ConfigCheck(this)),
dns_lookup_(new MessageLookup(this)),
- dns_answer_(new MessageAnswer)
+ dns_answer_(new MessageAnswer),
+ nsas_(NULL),
+ cache_(NULL),
+ configured_(false)
{}
Resolver::~Resolver() {
@@ -344,7 +370,7 @@ Resolver::~Resolver() {
}
void
-Resolver::setDNSService(asiolink::DNSService& dnss) {
+Resolver::setDNSService(isc::asiodns::DNSService& dnss) {
dnss_ = &dnss;
}
@@ -386,21 +412,25 @@ Resolver::processMessage(const IOMessage& io_message,
OutputBufferPtr buffer,
DNSServer* server)
{
- dlog("Got a DNS message");
InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
// First, check the header part. If we fail even for the base header,
// just drop the message.
+
+ // In the following code, the debug output is such that there should only be
+ // one debug message if packet processing failed. There could be two if
+ // it succeeded.
try {
query_message->parseHeader(request_buffer);
// Ignore all responses.
if (query_message->getHeaderFlag(Message::HEADERFLAG_QR)) {
- dlog("Received unexpected response, ignoring");
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_UNEXPECTED_RESPONSE);
server->resume(false);
return;
}
} catch (const Exception& ex) {
- dlog(string("DNS packet exception: ") + ex.what(),true);
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_HEADER_ERROR)
+ .arg(ex.what());
server->resume(false);
return;
}
@@ -409,65 +439,63 @@ Resolver::processMessage(const IOMessage& io_message,
try {
query_message->fromWire(request_buffer);
} catch (const DNSProtocolError& error) {
- dlog(string("returning ") + error.getRcode().toText() + ": " +
- error.what());
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_PROTOCOL_ERROR)
+ .arg(error.what()).arg(error.getRcode());
makeErrorMessage(query_message, answer_message,
buffer, error.getRcode());
server->resume(true);
return;
} catch (const Exception& ex) {
- dlog(string("returning SERVFAIL: ") + ex.what());
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_MESSAGE_ERROR)
+ .arg(ex.what()).arg(Rcode::SERVFAIL());
makeErrorMessage(query_message, answer_message,
buffer, Rcode::SERVFAIL());
server->resume(true);
return;
- } // other exceptions will be handled at a higher layer.
+ } // Other exceptions will be handled at a higher layer.
- dlog("received a message:\n" + query_message->toText());
+ // Note: there appears to be no LOG_DEBUG for a successfully-received
+ // message. This is not an oversight - it is handled below. In the
+ // meantime, output the full message for debug purposes (if requested).
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_DETAIL,
+ RESOLVER_DNS_MESSAGE_RECEIVED).arg(*query_message);
// Perform further protocol-level validation.
- bool sendAnswer = true;
+ bool send_answer = true;
if (query_message->getOpcode() == Opcode::NOTIFY()) {
+
makeErrorMessage(query_message, answer_message,
buffer, Rcode::NOTAUTH());
- dlog("Notify arrived, but we are not authoritative");
+ // Notify arrived, but we are not authoritative.
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS,
+ RESOLVER_NOTIFY_RECEIVED);
} else if (query_message->getOpcode() != Opcode::QUERY()) {
- dlog("Unsupported opcode (got: " + query_message->getOpcode().toText() +
- ", expected: " + Opcode::QUERY().toText());
+ // Unsupported opcode.
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS,
+ RESOLVER_UNSUPPORTED_OPCODE).arg(query_message->getOpcode());
makeErrorMessage(query_message, answer_message,
buffer, Rcode::NOTIMP());
} else if (query_message->getRRCount(Message::SECTION_QUESTION) != 1) {
- dlog("The query contained " +
- boost::lexical_cast<string>(query_message->getRRCount(
- Message::SECTION_QUESTION) + " questions, exactly one expected"));
- makeErrorMessage(query_message, answer_message,
- buffer, Rcode::FORMERR());
+ // Not one question
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS,
+ RESOLVER_NOT_ONE_QUESTION)
+ .arg(query_message->getRRCount(Message::SECTION_QUESTION));
+ makeErrorMessage(query_message, answer_message, buffer,
+ Rcode::FORMERR());
} else {
- ConstQuestionPtr question = *query_message->beginQuestion();
- const RRType &qtype = question->getType();
- if (qtype == RRType::AXFR()) {
- if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
- makeErrorMessage(query_message, answer_message,
- buffer, Rcode::FORMERR());
- } else {
- makeErrorMessage(query_message, answer_message,
- buffer, Rcode::NOTIMP());
- }
- } else if (qtype == RRType::IXFR()) {
- makeErrorMessage(query_message, answer_message,
- buffer, Rcode::NOTIMP());
- } else {
+ const ResolverImpl::NormalQueryResult result =
+ impl_->processNormalQuery(io_message, query_message,
+ answer_message, buffer, server);
+ if (result == ResolverImpl::RECURSION) {
// The RecursiveQuery object will post the "resume" event to the
// DNSServer when an answer arrives, so we don't have to do it now.
- sendAnswer = false;
- impl_->processNormalQuery(*question, answer_message,
- buffer, server);
+ return;
+ } else if (result == ResolverImpl::DROPPED) {
+ send_answer = false;
}
}
- if (sendAnswer) {
- server->resume(true);
- }
+ server->resume(send_answer);
}
void
@@ -477,19 +505,84 @@ ResolverImpl::resolve(const QuestionPtr& question,
rec_query_->resolve(question, callback);
}
-void
-ResolverImpl::processNormalQuery(const Question& question,
+ResolverImpl::NormalQueryResult
+ResolverImpl::processNormalQuery(const IOMessage& io_message,
+ MessagePtr query_message,
MessagePtr answer_message,
OutputBufferPtr buffer,
DNSServer* server)
{
- dlog("Processing normal query");
- rec_query_->resolve(question, answer_message, buffer, server);
+ const ConstQuestionPtr question = *query_message->beginQuestion();
+ const RRType qtype = question->getType();
+ const RRClass qclass = question->getClass();
+
+ // Apply query ACL
+ const Client client(io_message);
+ const BasicAction query_action(
+ getQueryACL().execute(acl::dns::RequestContext(
+ client.getRequestSourceIPAddress())));
+ if (query_action == isc::acl::REJECT) {
+ LOG_INFO(resolver_logger, RESOLVER_QUERY_REJECTED)
+ .arg(question->getName()).arg(qtype).arg(qclass).arg(client);
+ makeErrorMessage(query_message, answer_message, buffer,
+ Rcode::REFUSED());
+ return (ERROR);
+ } else if (query_action == isc::acl::DROP) {
+ LOG_INFO(resolver_logger, RESOLVER_QUERY_DROPPED)
+ .arg(question->getName()).arg(qtype).arg(qclass).arg(client);
+ return (DROPPED);
+ }
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_QUERY_ACCEPTED)
+ .arg(question->getName()).arg(qtype).arg(question->getClass())
+ .arg(client);
+
+ // ACL passed. Reject inappropriate queries for the resolver.
+ if (qtype == RRType::AXFR()) {
+ if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
+ // Can't process AXFR request receoved over UDP
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_AXFR_UDP);
+ makeErrorMessage(query_message, answer_message, buffer,
+ Rcode::FORMERR());
+ } else {
+ // ... or over TCP for that matter
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_AXFR_TCP);
+ makeErrorMessage(query_message, answer_message, buffer,
+ Rcode::NOTIMP());
+ }
+ return (ERROR);
+ } else if (qtype == RRType::IXFR()) {
+ // Can't process IXFR request
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_IXFR);
+ makeErrorMessage(query_message, answer_message, buffer,
+ Rcode::NOTIMP());
+ return (ERROR);
+ } else if (qclass != RRClass::IN()) {
+ // Non-IN message received, refuse it.
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_NON_IN_PACKET)
+ .arg(question->getClass());
+ makeErrorMessage(query_message, answer_message, buffer,
+ Rcode::REFUSED());
+ return (ERROR);
+ }
+
+ // Everything is okay. Start resolver.
+ if (upstream_.empty()) {
+ // Processing normal query
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_NORMAL_QUERY);
+ rec_query_->resolve(*question, answer_message, buffer, server);
+ } else {
+ // Processing forward query
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_FORWARD_QUERY);
+ rec_query_->forward(query_message, answer_message, buffer, server);
+ }
+
+ return (RECURSION);
}
ConstElementPtr
Resolver::updateConfig(ConstElementPtr config) {
- dlog("New config comes: " + config->toWire());
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_CONFIG, RESOLVER_CONFIG_UPDATED)
+ .arg(*config);
try {
// Parse forward_addresses
@@ -502,6 +595,10 @@ Resolver::updateConfig(ConstElementPtr config) {
ConstElementPtr listenAddressesE(config->get("listen_on"));
AddressList listenAddresses(parseAddresses(listenAddressesE,
"listen_on"));
+ const ConstElementPtr query_acl_cfg(config->get("query_acl"));
+ const shared_ptr<const RequestACL> query_acl =
+ query_acl_cfg ? acl::dns::getRequestLoader().load(query_acl_cfg) :
+ shared_ptr<RequestACL>();
bool set_timeouts(false);
int qtimeout = impl_->query_timeout_;
int ctimeout = impl_->client_timeout_;
@@ -516,6 +613,8 @@ Resolver::updateConfig(ConstElementPtr config) {
// check for us
qtimeout = qtimeoutE->intValue();
if (qtimeout < -1) {
+ LOG_ERROR(resolver_logger, RESOLVER_QUERY_TIME_SMALL)
+ .arg(qtimeout);
isc_throw(BadValue, "Query timeout too small");
}
set_timeouts = true;
@@ -523,6 +622,8 @@ Resolver::updateConfig(ConstElementPtr config) {
if (ctimeoutE) {
ctimeout = ctimeoutE->intValue();
if (ctimeout < -1) {
+ LOG_ERROR(resolver_logger, RESOLVER_CLIENT_TIME_SMALL)
+ .arg(ctimeout);
isc_throw(BadValue, "Client timeout too small");
}
set_timeouts = true;
@@ -530,12 +631,19 @@ Resolver::updateConfig(ConstElementPtr config) {
if (ltimeoutE) {
ltimeout = ltimeoutE->intValue();
if (ltimeout < -1) {
+ LOG_ERROR(resolver_logger, RESOLVER_LOOKUP_TIME_SMALL)
+ .arg(ltimeout);
isc_throw(BadValue, "Lookup timeout too small");
}
set_timeouts = true;
}
if (retriesE) {
+ // Do the assignment from "retriesE->intValue()" to "retries"
+ // _after_ the comparison (as opposed to before it for the timeouts)
+ // because "retries" is unsigned.
if (retriesE->intValue() < 0) {
+ LOG_ERROR(resolver_logger, RESOLVER_NEGATIVE_RETRIES)
+ .arg(retriesE->intValue());
isc_throw(BadValue, "Negative number of retries");
}
retries = retriesE->intValue();
@@ -561,14 +669,21 @@ Resolver::updateConfig(ConstElementPtr config) {
setTimeouts(qtimeout, ctimeout, ltimeout, retries);
need_query_restart = true;
}
+ if (query_acl) {
+ setQueryACL(query_acl);
+ }
if (need_query_restart) {
impl_->queryShutdown();
impl_->querySetup(*dnss_, *nsas_, *cache_);
}
+ setConfigured();
return (isc::config::createAnswer());
+
} catch (const isc::Exception& error) {
- dlog(string("error in config: ") + error.what(),true);
+
+ // Configuration error
+ LOG_ERROR(resolver_logger, RESOLVER_CONFIG_ERROR).arg(error.what());
return (isc::config::createAnswer(1, error.what()));
}
}
@@ -608,10 +723,10 @@ Resolver::setListenAddresses(const AddressList& addresses) {
void
Resolver::setTimeouts(int query_timeout, int client_timeout,
int lookup_timeout, unsigned retries) {
- dlog("Setting query timeout to " + boost::lexical_cast<string>(query_timeout) +
- ", client timeout to " + boost::lexical_cast<string>(client_timeout) +
- ", lookup timeout to " + boost::lexical_cast<string>(lookup_timeout) +
- " and retry count to " + boost::lexical_cast<string>(retries));
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_CONFIG, RESOLVER_SET_PARAMS)
+ .arg(query_timeout).arg(client_timeout).arg(lookup_timeout)
+ .arg(retries);
+
impl_->query_timeout_ = query_timeout;
impl_->client_timeout_ = client_timeout;
impl_->lookup_timeout_ = lookup_timeout;
@@ -642,3 +757,18 @@ AddressList
Resolver::getListenAddresses() const {
return (impl_->listen_);
}
+
+const RequestACL&
+Resolver::getQueryACL() const {
+ return (impl_->getQueryACL());
+}
+
+void
+Resolver::setQueryACL(shared_ptr<const RequestACL> new_acl) {
+ if (!new_acl) {
+ isc_throw(InvalidParameter, "NULL pointer is passed to setQueryACL");
+ }
+
+ LOG_INFO(resolver_logger, RESOLVER_SET_QUERY_ACL);
+ impl_->setQueryACL(new_acl);
+}
diff --git a/src/bin/resolver/resolver.h b/src/bin/resolver/resolver.h
index d851656..4b9c773 100644
--- a/src/bin/resolver/resolver.h
+++ b/src/bin/resolver/resolver.h
@@ -19,10 +19,21 @@
#include <vector>
#include <utility>
+#include <boost/shared_ptr.hpp>
+
#include <cc/data.h>
#include <config/ccsession.h>
+#include <acl/dns.h>
+#include <dns/message.h>
+#include <util/buffer.h>
-#include <asiolink/asiolink.h>
+#include <asiodns/dns_server.h>
+#include <asiodns/dns_service.h>
+#include <asiodns/dns_lookup.h>
+#include <asiodns/dns_answer.h>
+#include <asiolink/io_message.h>
+#include <asiolink/io_service.h>
+#include <asiolink/simple_callback.h>
#include <nsas/nameserver_address_store.h>
#include <cache/resolver_cache.h>
@@ -74,11 +85,11 @@ public:
/// shall return to the client
/// \param buffer Pointer to an \c OutputBuffer for the resposne
/// \param server Pointer to the \c DNSServer
- void processMessage(const asiolink::IOMessage& io_message,
+ void processMessage(const isc::asiolink::IOMessage& io_message,
isc::dns::MessagePtr query_message,
isc::dns::MessagePtr answer_message,
- isc::dns::OutputBufferPtr buffer,
- asiolink::DNSServer* server);
+ isc::util::OutputBufferPtr buffer,
+ isc::asiodns::DNSServer* server);
/// \brief Set and get the config session
isc::config::ModuleCCSession* getConfigSession() const;
@@ -88,16 +99,16 @@ public:
isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr config);
/// \brief Assign an ASIO IO Service queue to this Resolver object
- void setDNSService(asiolink::DNSService& dnss);
-
+ void setDNSService(isc::asiodns::DNSService& dnss);
+
/// \brief Assign a NameserverAddressStore to this Resolver object
void setNameserverAddressStore(isc::nsas::NameserverAddressStore &nsas);
-
+
/// \brief Assign a cache to this Resolver object
void setCache(isc::cache::ResolverCache& cache);
/// \brief Return this object's ASIO IO Service queue
- asiolink::DNSService& getDNSService() const { return (*dnss_); }
+ isc::asiodns::DNSService& getDNSService() const { return (*dnss_); }
/// \brief Returns this object's NSAS
isc::nsas::NameserverAddressStore& getNameserverAddressStore() const {
@@ -108,15 +119,22 @@ public:
isc::cache::ResolverCache& getResolverCache() const {
return *cache_;
};
-
+
/// \brief Return pointer to the DNS Lookup callback function
- asiolink::DNSLookup* getDNSLookupProvider() { return (dns_lookup_); }
+ isc::asiodns::DNSLookup* getDNSLookupProvider() { return (dns_lookup_); }
/// \brief Return pointer to the DNS Answer callback function
- asiolink::DNSAnswer* getDNSAnswerProvider() { return (dns_answer_); }
+ isc::asiodns::DNSAnswer* getDNSAnswerProvider() { return (dns_answer_); }
/// \brief Return pointer to the Checkin callback function
- asiolink::SimpleCallback* getCheckinProvider() { return (checkin_); }
+ isc::asiolink::SimpleCallback* getCheckinProvider() { return (checkin_); }
+
+ /**
+ * \brief Tell the Resolver that is has already been configured
+ * so that it will only set some defaults the first time
+ * (used by updateConfig() and tests)
+ */
+ void setConfigured() { configured_ = true; };
/**
* \brief Specify the list of upstream servers.
@@ -221,18 +239,41 @@ public:
*/
int getRetries() const;
+ /// Get the query ACL.
+ ///
+ /// \exception None
+ const isc::acl::dns::RequestACL& getQueryACL() const;
+
+ /// Set the new query ACL.
+ ///
+ /// This method replaces the existing query ACL completely.
+ /// Normally this method will be called via the configuration handler,
+ /// but is publicly available for convenience of tests (and other
+ /// experimental purposes).
+ /// \c new_acl must not be a NULL pointer.
+ ///
+ /// \exception InvalidParameter The given pointer is NULL
+ ///
+ /// \param new_acl The new ACL to replace the existing one.
+ void setQueryACL(boost::shared_ptr<const isc::acl::dns::RequestACL>
+ new_acl);
+
private:
ResolverImpl* impl_;
- asiolink::DNSService* dnss_;
- asiolink::SimpleCallback* checkin_;
- asiolink::DNSLookup* dns_lookup_;
- asiolink::DNSAnswer* dns_answer_;
+ isc::asiodns::DNSService* dnss_;
+ isc::asiolink::SimpleCallback* checkin_;
+ isc::asiodns::DNSLookup* dns_lookup_;
+ isc::asiodns::DNSAnswer* dns_answer_;
isc::nsas::NameserverAddressStore* nsas_;
isc::cache::ResolverCache* cache_;
+ // This value is initally false, and will be set to true
+ // when the initial configuration is done (updateConfig
+ // should act a tiny bit different on the very first call)
+ bool configured_;
};
#endif // __RESOLVER_H
-// Local Variables:
+// Local Variables:
// mode: c++
-// End:
+// End:
diff --git a/src/bin/resolver/resolver.spec.pre.in b/src/bin/resolver/resolver.spec.pre.in
index 9df1e75..076ef85 100644
--- a/src/bin/resolver/resolver.spec.pre.in
+++ b/src/bin/resolver/resolver.spec.pre.in
@@ -113,6 +113,41 @@
}
]
}
+ },
+ {
+ "item_name": "query_acl",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [
+ {
+ "action": "ACCEPT",
+ "from": "127.0.0.1"
+ },
+ {
+ "action": "ACCEPT",
+ "from": "::1"
+ }
+ ],
+ "list_item_spec": {
+ "item_name": "rule",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ {
+ "item_name": "action",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ },
+ {
+ "item_name": "from",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ }
+ ]
+ }
}
],
"commands": [
diff --git a/src/bin/resolver/resolver_log.cc b/src/bin/resolver/resolver_log.cc
new file mode 100644
index 0000000..4af0159
--- /dev/null
+++ b/src/bin/resolver/resolver_log.cc
@@ -0,0 +1,19 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// Defines the logger used by the NSAS
+
+#include "resolver_log.h"
+
+isc::log::Logger resolver_logger("resolver");
diff --git a/src/bin/resolver/resolver_log.h b/src/bin/resolver/resolver_log.h
new file mode 100644
index 0000000..8378b98
--- /dev/null
+++ b/src/bin/resolver/resolver_log.h
@@ -0,0 +1,49 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __RESOLVER_LOG__H
+#define __RESOLVER_LOG__H
+
+#include <log/macros.h>
+#include "resolver_messages.h"
+
+/// \brief Resolver Logging
+///
+/// Defines the levels used to output debug messages in the resolver. Note that
+/// higher numbers equate to more verbose (and detailed) output.
+
+// Initialization
+const int RESOLVER_DBG_INIT = 10;
+
+// Configuration messages
+const int RESOLVER_DBG_CONFIG = 30;
+
+// Trace sending and receiving of messages
+const int RESOLVER_DBG_IO = 50;
+
+// Trace processing of messages
+const int RESOLVER_DBG_PROCESS = 70;
+
+// Detailed message information
+const int RESOLVER_DBG_DETAIL = 90;
+
+
+/// \brief Resolver Logger
+///
+/// Define the logger used to log messages. We could define it in multiple
+/// modules, but defining in a single module and linking to it saves time and
+/// space.
+extern isc::log::Logger resolver_logger;
+
+#endif // __RESOLVER_LOG__H
diff --git a/src/bin/resolver/resolver_messages.mes b/src/bin/resolver/resolver_messages.mes
new file mode 100644
index 0000000..05b9a8b
--- /dev/null
+++ b/src/bin/resolver/resolver_messages.mes
@@ -0,0 +1,223 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# No namespace declaration - these constants go in the global namespace
+# along with the resolver methods.
+
+% RESOLVER_AXFR_TCP AXFR request received over TCP
+A debug message, the resolver received a NOTIFY message over TCP. The server
+cannot process it and will return an error message to the sender with the
+RCODE set to NOTIMP.
+
+% RESOLVER_AXFR_UDP AXFR request received over UDP
+A debug message, the resolver received a NOTIFY message over UDP. The server
+cannot process it (and in any case, an AXFR request should be sent over TCP)
+and will return an error message to the sender with the RCODE set to FORMERR.
+
+% RESOLVER_CLIENT_TIME_SMALL client timeout of %1 is too small
+An error indicating that the configuration value specified for the query
+timeout is too small.
+
+% RESOLVER_CONFIG_CHANNEL configuration channel created
+A debug message, output when the resolver has successfully established a
+connection to the configuration channel.
+
+% RESOLVER_CONFIG_ERROR error in configuration: %1
+An error was detected in a configuration update received by the resolver. This
+may be in the format of the configuration message (in which case this is a
+programming error) or it may be in the data supplied (in which case it is
+a user error). The reason for the error, given as a parameter in the message,
+will give more details.
+
+% RESOLVER_CONFIG_LOADED configuration loaded
+A debug message, output when the resolver configuration has been successfully
+loaded.
+
+% RESOLVER_CONFIG_UPDATED configuration updated: %1
+A debug message, the configuration has been updated with the specified
+information.
+
+% RESOLVER_CREATED main resolver object created
+A debug message, output when the Resolver() object has been created.
+
+% RESOLVER_DNS_MESSAGE_RECEIVED DNS message received: %1
+A debug message, this always precedes some other logging message and is the
+formatted contents of the DNS packet that the other message refers to.
+
+% RESOLVER_DNS_MESSAGE_SENT DNS message of %1 bytes sent: %2
+A debug message, this contains details of the response sent back to the querying
+system.
+
+% RESOLVER_FAILED resolver failed, reason: %1
+This is an error message output when an unhandled exception is caught by the
+resolver. All it can do is to shut down.
+
+% RESOLVER_FORWARD_ADDRESS setting forward address %1(%2)
+This message may appear multiple times during startup, and it lists the
+forward addresses used by the resolver when running in forwarding mode.
+
+% RESOLVER_FORWARD_QUERY processing forward query
+The received query has passed all checks and is being forwarded to upstream
+servers.
+
+% RESOLVER_HEADER_ERROR message received, exception when processing header: %1
+A debug message noting that an exception occurred during the processing of
+a received packet. The packet has been dropped.
+
+% RESOLVER_IXFR IXFR request received
+The resolver received a NOTIFY message over TCP. The server cannot process it
+and will return an error message to the sender with the RCODE set to NOTIMP.
+
+% RESOLVER_LOOKUP_TIME_SMALL lookup timeout of %1 is too small
+An error indicating that the configuration value specified for the lookup
+timeout is too small.
+
+% RESOLVER_MESSAGE_ERROR error parsing received message: %1 - returning %2
+A debug message noting that the resolver received a message and the
+parsing of the body of the message failed due to some error (although
+the parsing of the header succeeded). The message parameters give a
+textual description of the problem and the RCODE returned.
+
+% RESOLVER_NEGATIVE_RETRIES negative number of retries (%1) specified in the configuration
+An error message indicating that the resolver configuration has specified a
+negative retry count. Only zero or positive values are valid.
+
+% RESOLVER_NON_IN_PACKET non-IN class request received, returning REFUSED message
+A debug message, the resolver has received a DNS packet that was not IN class.
+The resolver cannot handle such packets, so is returning a REFUSED response to
+the sender.
+
+% RESOLVER_NORMAL_QUERY processing normal query
+The received query has passed all checks and is being processed by the resolver.
+
+% RESOLVER_NOTIFY_RECEIVED NOTIFY arrived but server is not authoritative
+The resolver received a NOTIFY message. As the server is not authoritative it
+cannot process it, so it returns an error message to the sender with the RCODE
+set to NOTAUTH.
+
+% RESOLVER_NOT_ONE_QUESTION query contained %1 questions, exactly one question was expected
+A debug message, the resolver received a query that contained the number of
+entires in the question section detailed in the message. This is a malformed
+message, as a DNS query must contain only one question. The resolver will
+return a message to the sender with the RCODE set to FORMERR.
+
+% RESOLVER_NO_ROOT_ADDRESS no root addresses available
+A warning message during startup, indicates that no root addresses have been
+set. This may be because the resolver will get them from a priming query.
+
+% RESOLVER_PARSE_ERROR error parsing received message: %1 - returning %2
+A debug message noting that the resolver received a message and the parsing
+of the body of the message failed due to some non-protocol related reason
+(although the parsing of the header succeeded). The message parameters give
+a textual description of the problem and the RCODE returned.
+
+% RESOLVER_PRINT_COMMAND print message command, arguments are: %1
+This message is logged when a "print_message" command is received over the
+command channel.
+
+% RESOLVER_PROTOCOL_ERROR protocol error parsing received message: %1 - returning %2
+A debug message noting that the resolver received a message and the parsing
+of the body of the message failed due to some protocol error (although the
+parsing of the header succeeded). The message parameters give a textual
+description of the problem and the RCODE returned.
+
+% RESOLVER_QUERY_SETUP query setup
+A debug message noting that the resolver is creating a RecursiveQuery object.
+
+% RESOLVER_QUERY_SHUTDOWN query shutdown
+A debug message noting that the resolver is destroying a RecursiveQuery object.
+
+% RESOLVER_QUERY_TIME_SMALL query timeout of %1 is too small
+An error indicating that the configuration value specified for the query
+timeout is too small.
+
+% RESOLVER_RECEIVED_MESSAGE resolver has received a DNS message
+A debug message indicating that the resolver has received a message. Depending
+on the debug settings, subsequent log output will indicate the nature of the
+message.
+
+% RESOLVER_RECURSIVE running in recursive mode
+This is an informational message that appears at startup noting that the
+resolver is running in recursive mode.
+
+% RESOLVER_SERVICE_CREATED service object created
+A debug message, output when the main service object (which handles the
+received queries) is created.
+
+% RESOLVER_SET_PARAMS query timeout: %1, client timeout: %2, lookup timeout: %3, retry count: %4
+A debug message, lists the parameters being set for the resolver. These are:
+query timeout: the timeout (in ms) used for queries originated by the resolver
+to upstream servers. Client timeout: the interval to resolver a query by
+a client: after this time, the resolver sends back a SERVFAIL to the client
+whilst continuing to resolver the query. Lookup timeout: the time at which the
+resolver gives up trying to resolve a query. Retry count: the number of times
+the resolver will retry a query to an upstream server if it gets a timeout.
+
+The client and lookup timeouts require a bit more explanation. The
+resolution of the client query might require a large number of queries to
+upstream nameservers. Even if none of these queries timeout, the total time
+taken to perform all the queries may exceed the client timeout. When this
+happens, a SERVFAIL is returned to the client, but the resolver continues
+with the resolution process. Data received is added to the cache. However,
+there comes a time - the lookup timeout - when even the resolver gives up.
+At this point it will wait for pending upstream queries to complete or
+timeout and drop the query.
+
+% RESOLVER_SET_ROOT_ADDRESS setting root address %1(%2)
+This message may appear multiple times during startup; it lists the root
+addresses used by the resolver.
+
+% RESOLVER_SHUTDOWN resolver shutdown complete
+This information message is output when the resolver has shut down.
+
+% RESOLVER_STARTED resolver started
+This informational message is output by the resolver when all initialization
+has been completed and it is entering its main loop.
+
+% RESOLVER_STARTING starting resolver with command line '%1'
+An informational message, this is output when the resolver starts up.
+
+% RESOLVER_UNEXPECTED_RESPONSE received unexpected response, ignoring
+A debug message noting that the server has received a response instead of a
+query and is ignoring it.
+
+% RESOLVER_UNSUPPORTED_OPCODE opcode %1 not supported by the resolver
+A debug message, the resolver received a message with an unsupported opcode
+(it can only process QUERY opcodes). It will return a message to the sender
+with the RCODE set to NOTIMP.
+
+% RESOLVER_SET_QUERY_ACL query ACL is configured
+A debug message that appears when a new query ACL is configured for the
+resolver.
+
+% RESOLVER_QUERY_ACCEPTED query accepted: '%1/%2/%3' from %4
+A debug message that indicates an incoming query is accepted in terms of
+the query ACL. The log message shows the query in the form of
+<query name>/<query type>/<query class>, and the client that sends the
+query in the form of <Source IP address>#<source port>.
+
+% RESOLVER_QUERY_REJECTED query rejected: '%1/%2/%3' from %4
+An informational message that indicates an incoming query is rejected
+in terms of the query ACL. This results in a response with an RCODE of
+REFUSED. The log message shows the query in the form of <query
+name>/<query type>/<query class>, and the client that sends the
+query in the form of <Source IP address>#<source port>.
+
+% RESOLVER_QUERY_DROPPED query dropped: '%1/%2/%3' from %4
+An informational message that indicates an incoming query is dropped
+in terms of the query ACL. Unlike the RESOLVER_QUERY_REJECTED
+case, the server does not return any response. The log message
+shows the query in the form of <query name>/<query type>/<query
+class>, and the client that sends the query in the form of <Source
+IP address>#<source port>.
diff --git a/src/bin/resolver/response_scrubber.cc b/src/bin/resolver/response_scrubber.cc
index 060a8b1..d35a96f 100644
--- a/src/bin/resolver/response_scrubber.cc
+++ b/src/bin/resolver/response_scrubber.cc
@@ -1,4 +1,3 @@
-
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
@@ -26,7 +25,7 @@ using namespace std;
// Compare addresses etc.
ResponseScrubber::Category ResponseScrubber::addressCheck(
- const asiolink::IOEndpoint& to, const asiolink::IOEndpoint& from)
+ const isc::asiolink::IOEndpoint& to, const isc::asiolink::IOEndpoint& from)
{
if (from.getProtocol() == to.getProtocol()) {
if (from.getAddress() == to.getAddress()) {
@@ -50,7 +49,7 @@ ResponseScrubber::Category ResponseScrubber::addressCheck(
unsigned int
ResponseScrubber::scrubSection(Message& message,
const vector<const Name*>& names,
- const NameComparisonResult::NameRelation connection,
+ const NameComparisonResult::NameRelation connection,
const Message::Section section)
{
unsigned int count = 0; // Count of RRsets removed
@@ -74,30 +73,32 @@ ResponseScrubber::scrubSection(Message& message,
// Start looking at the remaining entries in the section.
removed = false;
- for (; (i != message.endSection(section)) && (!removed); ++i) {
+ for (; i != message.endSection(section); ++i) {
// Loop through the list of names given and see if any are in the
// given relationship with the QNAME of this RRset
- bool nomatch = true;
+ bool match = false;
for (vector<const Name*>::const_iterator n = names.begin();
- ((n != names.end()) && nomatch); ++n) {
+ n != names.end(); ++n) {
NameComparisonResult result = (*i)->getName().compare(**n);
NameComparisonResult::NameRelation relationship =
result.getRelation();
if ((relationship == NameComparisonResult::EQUAL) ||
(relationship == connection)) {
-
+
// RRset in the specified relationship, so a match has
// been found
- nomatch = false;
+ match = true;
+ break;
}
}
// Remove the RRset if there was no match to one of the given names.
- if (nomatch) {
+ if (!match) {
message.removeRRset(section, i);
++count; // One more RRset removed
removed = true; // Something was removed
+ break; // It invalidated the iterators, start again
} else {
// There was a match so this is one more entry we can skip next
@@ -107,7 +108,7 @@ ResponseScrubber::scrubSection(Message& message,
}
}
- return count;
+ return (count);
}
// Perform the scrubbing of all sections of the message.
@@ -126,7 +127,7 @@ ResponseScrubber::scrubAllSections(Message& message, const Name& bailiwick) {
count += scrubSection(message, bailiwick_names,
NameComparisonResult::SUBDOMAIN, Message::SECTION_ADDITIONAL);
- return count;
+ return (count);
}
// Scrub across sections.
@@ -171,7 +172,6 @@ ResponseScrubber::scrubCrossSections(isc::dns::Message& message) {
// superdomain of the names in the question/answer section.
return (scrubSection(message, source,
NameComparisonResult::SUPERDOMAIN, Message::SECTION_AUTHORITY));
-
}
// Scrub a message
@@ -183,7 +183,7 @@ ResponseScrubber::scrub(const isc::dns::MessagePtr& message,
unsigned int sections_removed = scrubAllSections(*message, bailiwick);
sections_removed += scrubCrossSections(*message);
- return sections_removed;
+ return (sections_removed);
}
diff --git a/src/bin/resolver/response_scrubber.h b/src/bin/resolver/response_scrubber.h
index 680aa5a..c59ac15 100644
--- a/src/bin/resolver/response_scrubber.h
+++ b/src/bin/resolver/response_scrubber.h
@@ -282,8 +282,8 @@ public:
///
/// \return SUCCESS if the two endpoints match, otherwise an error status
/// indicating what was incorrect.
- static Category addressCheck(const asiolink::IOEndpoint& to,
- const asiolink::IOEndpoint& from);
+ static Category addressCheck(const isc::asiolink::IOEndpoint& to,
+ const isc::asiolink::IOEndpoint& from);
/// \brief Check QID
///
diff --git a/src/bin/resolver/tests/Makefile.am b/src/bin/resolver/tests/Makefile.am
index b85c223..97a2ba6 100644
--- a/src/bin/resolver/tests/Makefile.am
+++ b/src/bin/resolver/tests/Makefile.am
@@ -1,6 +1,7 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
+AM_CPPFLAGS += -I$(top_builddir)/src/bin/resolver
AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(top_srcdir)/src/lib/testutils/testdata\"
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/testutils/testdata\"
AM_CPPFLAGS += $(BOOST_INCLUDES)
@@ -16,23 +17,29 @@ CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
TESTS += run_unittests
+
run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += ../resolver.h ../resolver.cc
+run_unittests_SOURCES += ../resolver_log.h ../resolver_log.cc
run_unittests_SOURCES += ../response_scrubber.h ../response_scrubber.cc
run_unittests_SOURCES += resolver_unittest.cc
run_unittests_SOURCES += resolver_config_unittest.cc
run_unittests_SOURCES += response_scrubber_unittest.cc
run_unittests_SOURCES += run_unittests.cc
+
+nodist_run_unittests_SOURCES = ../resolver_messages.h ../resolver_messages.cc
+
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(SQLITE_LIBS)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
-run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
+run_unittests_LDADD += $(top_builddir)/src/lib/acl/libdnsacl.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
@@ -41,6 +48,9 @@ run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
+run_unittests_LDADD += $(top_builddir)/src/lib/acl/libacl.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
# Note the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS
diff --git a/src/bin/resolver/tests/resolver_config_unittest.cc b/src/bin/resolver/tests/resolver_config_unittest.cc
index 1d6415b..698e535 100644
--- a/src/bin/resolver/tests/resolver_config_unittest.cc
+++ b/src/bin/resolver/tests/resolver_config_unittest.cc
@@ -12,13 +12,27 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <config.h>
+
#include <string>
+#include <boost/scoped_ptr.hpp>
+
#include <gtest/gtest.h>
#include <cc/data.h>
+#include <config/ccsession.h>
+
+#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_socket.h>
+#include <asiolink/io_message.h>
+
+#include <acl/acl.h>
+
+#include <server_common/client.h>
#include <resolver/resolver.h>
@@ -27,23 +41,41 @@
#include <testutils/portconfig.h>
using namespace std;
+using boost::scoped_ptr;
+using namespace isc::acl;
+using isc::acl::dns::RequestContext;
using namespace isc::data;
using namespace isc::testutils;
-using namespace asiolink;
+using namespace isc::asiodns;
+using namespace isc::asiolink;
+using namespace isc::server_common;
using isc::UnitTestUtil;
namespace {
class ResolverConfig : public ::testing::Test {
- public:
- IOService ios;
- DNSService dnss;
- Resolver server;
- ResolverConfig() :
- dnss(ios, NULL, NULL, NULL)
- {
- server.setDNSService(dnss);
- }
- void invalidTest(const string &JSON, const string& name);
+protected:
+ IOService ios;
+ DNSService dnss;
+ Resolver server;
+ scoped_ptr<const IOEndpoint> endpoint;
+ scoped_ptr<const IOMessage> query_message;
+ scoped_ptr<const Client> client;
+ scoped_ptr<const RequestContext> request;
+ ResolverConfig() : dnss(ios, NULL, NULL, NULL) {
+ server.setDNSService(dnss);
+ server.setConfigured();
+ }
+ const RequestContext& createRequest(const string& source_addr) {
+ endpoint.reset(IOEndpoint::create(IPPROTO_UDP, IOAddress(source_addr),
+ 53210));
+ query_message.reset(new IOMessage(NULL, 0,
+ IOSocket::getDummyUDPSocket(),
+ *endpoint));
+ client.reset(new Client(*query_message));
+ request.reset(new RequestContext(client->getRequestSourceIPAddress()));
+ return (*request);
+ }
+ void invalidTest(const string &JSON, const string& name);
};
TEST_F(ResolverConfig, forwardAddresses) {
@@ -72,14 +104,14 @@ TEST_F(ResolverConfig, forwardAddresses) {
TEST_F(ResolverConfig, forwardAddressConfig) {
// Try putting there some address
- ElementPtr config(Element::fromJSON("{"
- "\"forward_addresses\": ["
- " {"
- " \"address\": \"192.0.2.1\","
- " \"port\": 53"
- " }"
- "]"
- "}"));
+ ConstElementPtr config(Element::fromJSON("{"
+ "\"forward_addresses\": ["
+ " {"
+ " \"address\": \"192.0.2.1\","
+ " \"port\": 53"
+ " }"
+ "]"
+ "}"));
ConstElementPtr result(server.updateConfig(config));
EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
EXPECT_TRUE(server.isForwarding());
@@ -99,14 +131,14 @@ TEST_F(ResolverConfig, forwardAddressConfig) {
TEST_F(ResolverConfig, rootAddressConfig) {
// Try putting there some address
- ElementPtr config(Element::fromJSON("{"
- "\"root_addresses\": ["
- " {"
- " \"address\": \"192.0.2.1\","
- " \"port\": 53"
- " }"
- "]"
- "}"));
+ ConstElementPtr config(Element::fromJSON("{"
+ "\"root_addresses\": ["
+ " {"
+ " \"address\": \"192.0.2.1\","
+ " \"port\": 53"
+ " }"
+ "]"
+ "}"));
ConstElementPtr result(server.updateConfig(config));
EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
ASSERT_EQ(1, server.getRootAddresses().size());
@@ -182,12 +214,12 @@ TEST_F(ResolverConfig, timeouts) {
}
TEST_F(ResolverConfig, timeoutsConfig) {
- ElementPtr config = Element::fromJSON("{"
- "\"timeout_query\": 1000,"
- "\"timeout_client\": 2000,"
- "\"timeout_lookup\": 3000,"
- "\"retries\": 4"
- "}");
+ ConstElementPtr config = Element::fromJSON("{"
+ "\"timeout_query\": 1000,"
+ "\"timeout_client\": 2000,"
+ "\"timeout_lookup\": 3000,"
+ "\"retries\": 4"
+ "}");
ConstElementPtr result(server.updateConfig(config));
EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
EXPECT_EQ(1000, server.getQueryTimeout());
@@ -223,4 +255,140 @@ TEST_F(ResolverConfig, invalidTimeoutsConfig) {
"}", "Negative number of retries");
}
+TEST_F(ResolverConfig, defaultQueryACL) {
+ // If no configuration is loaded, the default ACL should reject everything.
+ EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("192.0.2.1")));
+ EXPECT_EQ(REJECT, server.getQueryACL().execute(
+ createRequest("2001:db8::1")));
+
+ // The following would be allowed if the server had loaded the default
+ // configuration from the spec file. In this context it should not have
+ // happened, and they should be rejected just like the above cases.
+ EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("127.0.0.1")));
+ EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("::1")));
+}
+
+TEST_F(ResolverConfig, emptyQueryACL) {
+ // Explicitly configured empty ACL should have the same effect.
+ ConstElementPtr config(Element::fromJSON("{ \"query_acl\": [] }"));
+ ConstElementPtr result(server.updateConfig(config));
+ EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
+ EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("192.0.2.1")));
+ EXPECT_EQ(REJECT, server.getQueryACL().execute(
+ createRequest("2001:db8::1")));
+}
+
+TEST_F(ResolverConfig, queryACLIPv4) {
+ // A simple "accept" query for a specific IPv4 address
+ ConstElementPtr config(Element::fromJSON(
+ "{ \"query_acl\": "
+ " [ {\"action\": \"ACCEPT\","
+ " \"from\": \"192.0.2.1\"} ] }"));
+ ConstElementPtr result(server.updateConfig(config));
+ EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
+ EXPECT_EQ(ACCEPT, server.getQueryACL().execute(createRequest("192.0.2.1")));
+ EXPECT_EQ(REJECT, server.getQueryACL().execute(
+ createRequest("2001:db8::1")));
+}
+
+TEST_F(ResolverConfig, queryACLIPv6) {
+ // same for IPv6
+ ConstElementPtr config(Element::fromJSON(
+ "{ \"query_acl\": "
+ " [ {\"action\": \"ACCEPT\","
+ " \"from\": \"2001:db8::1\"} ] }"));
+ ConstElementPtr result(server.updateConfig(config));
+ EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
+ EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("192.0.2.1")));
+ EXPECT_EQ(ACCEPT, server.getQueryACL().execute(
+ createRequest("2001:db8::1")));
+}
+
+TEST_F(ResolverConfig, multiEntryACL) {
+ // A bit more complicated one: mixture of IPv4 and IPv6 with 3 rules
+ // in total. We shouldn't have to check so many variations of rules
+ // as it should have been tested in the underlying ACL module. All we
+ // have to do to check is a reasonably complicated ACL configuration is
+ // loaded as expected.
+ ConstElementPtr config(Element::fromJSON(
+ "{ \"query_acl\": "
+ " [ {\"action\": \"ACCEPT\","
+ " \"from\": \"192.0.2.1\"},"
+ " {\"action\": \"REJECT\","
+ " \"from\": \"192.0.2.0/24\"},"
+ " {\"action\": \"DROP\","
+ " \"from\": \"2001:db8::1\"},"
+ "] }"));
+ ConstElementPtr result(server.updateConfig(config));
+ EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
+ EXPECT_EQ(ACCEPT, server.getQueryACL().execute(createRequest("192.0.2.1")));
+ EXPECT_EQ(REJECT, server.getQueryACL().execute(createRequest("192.0.2.2")));
+ EXPECT_EQ(DROP, server.getQueryACL().execute(
+ createRequest("2001:db8::1")));
+ EXPECT_EQ(REJECT, server.getQueryACL().execute(
+ createRequest("2001:db8::2"))); // match the default rule
+}
+
+
+int
+getResultCode(ConstElementPtr result) {
+ int rcode;
+ isc::config::parseAnswer(rcode, result);
+ return (rcode);
+}
+
+TEST_F(ResolverConfig, queryACLActionOnly) {
+ // "action only" rule will be accepted by the loader, which can
+ // effectively change the default action.
+ ConstElementPtr config(Element::fromJSON(
+ "{ \"query_acl\": "
+ " [ {\"action\": \"ACCEPT\","
+ " \"from\": \"192.0.2.1\"},"
+ " {\"action\": \"DROP\"} ] }"));
+ EXPECT_EQ(0, getResultCode(server.updateConfig(config)));
+ EXPECT_EQ(ACCEPT, server.getQueryACL().execute(createRequest("192.0.2.1")));
+
+ // We reject non matching queries by default, but the last resort
+ // rule should have changed the action in that case to "DROP".
+ EXPECT_EQ(DROP, server.getQueryACL().execute(createRequest("192.0.2.2")));
+}
+
+TEST_F(ResolverConfig, badQueryACL) {
+ // Most of these cases shouldn't happen in practice because the syntax
+ // check should be performed before updateConfig(). But we check at
+ // least the server code won't crash even if an unexpected input is given.
+
+ // ACL must be a list
+ EXPECT_EQ(1, getResultCode(
+ server.updateConfig(
+ Element::fromJSON("{ \"query_acl\": 1 }"))));
+ // Each rule must have "action" and "from"
+ EXPECT_EQ(1, getResultCode(
+ server.updateConfig(
+ Element::fromJSON("{ \"query_acl\":"
+ " [ {\"from\": \"192.0.2.1\"} ] }"))));
+ // invalid "action"
+ EXPECT_EQ(1, getResultCode(
+ server.updateConfig(
+ Element::fromJSON("{ \"query_acl\":"
+ " [ {\"action\": 1,"
+ " \"from\": \"192.0.2.1\"}]}"))));
+ EXPECT_EQ(1, getResultCode(
+ server.updateConfig(
+ Element::fromJSON("{ \"query_acl\":"
+ " [ {\"action\": \"BADACTION\","
+ " \"from\": \"192.0.2.1\"}]}"))));
+ // invalid "from"
+ EXPECT_EQ(1, getResultCode(
+ server.updateConfig(
+ Element::fromJSON("{ \"query_acl\":"
+ " [ {\"action\": \"ACCEPT\","
+ " \"from\": 53}]}"))));
+ EXPECT_EQ(1, getResultCode(
+ server.updateConfig(
+ Element::fromJSON("{ \"query_acl\":"
+ " [ {\"action\": \"ACCEPT\","
+ " \"from\": \"1922.0.2.1\"}]}"))));
+}
+
}
diff --git a/src/bin/resolver/tests/resolver_unittest.cc b/src/bin/resolver/tests/resolver_unittest.cc
index 97edf12..71474dd 100644
--- a/src/bin/resolver/tests/resolver_unittest.cc
+++ b/src/bin/resolver/tests/resolver_unittest.cc
@@ -12,14 +12,22 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <string>
+
+#include <exceptions/exceptions.h>
+
#include <dns/name.h>
+#include <cc/data.h>
#include <resolver/resolver.h>
#include <dns/tests/unittest_util.h>
#include <testutils/dnsmessage_test.h>
#include <testutils/srv_test.h>
+using namespace std;
using namespace isc::dns;
+using namespace isc::data;
+using isc::acl::dns::RequestACL;
using namespace isc::testutils;
using isc::UnitTestUtil;
@@ -28,7 +36,17 @@ const char* const TEST_PORT = "53535";
class ResolverTest : public SrvTestBase{
protected:
- ResolverTest() : server(){}
+ ResolverTest() : server() {
+ // By default queries from the "default remote address" will be
+ // rejected, so we'll need to add an explicit ACL entry to allow that.
+ server.setConfigured();
+ server.updateConfig(Element::fromJSON(
+ "{ \"query_acl\": "
+ " [ {\"action\": \"ACCEPT\","
+ " \"from\": \"" +
+ string(DEFAULT_REMOTE_ADDRESS) +
+ "\"} ] }"));
+ }
virtual void processMessage() {
server.processMessage(*io_message,
parse_message,
@@ -136,4 +154,45 @@ TEST_F(ResolverTest, notifyFail) {
Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
}
+TEST_F(ResolverTest, setQueryACL) {
+ // valid cases are tested through other tests. We only explicitly check
+ // an invalid case: passing a NULL shared pointer.
+ EXPECT_THROW(server.setQueryACL(boost::shared_ptr<const RequestACL>()),
+ isc::InvalidParameter);
+}
+
+TEST_F(ResolverTest, queryACL) {
+ // The "ACCEPT" cases are covered in other tests. Here we explicitly
+ // test "REJECT" and "DROP" cases.
+
+ // Clear the existing ACL, reverting to the "default reject" rule.
+
+ // AXFR over UDP. This would otherwise result in FORMERR.
+ server.updateConfig(Element::fromJSON("{ \"query_acl\": [] }"));
+ UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
+ Name("example.com"), RRClass::IN(),
+ RRType::AXFR());
+ createRequestPacket(request_message, IPPROTO_UDP);
+ server.processMessage(*io_message, parse_message, response_message,
+ response_obuffer, &dnsserv);
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::REFUSED(),
+ Opcode::QUERY().getCode(), QR_FLAG, 1, 0, 0, 0);
+
+ // Same query, but with an explicit "DROP" ACL entry. There should be
+ // no response.
+ server.updateConfig(Element::fromJSON("{ \"query_acl\": "
+ " [ {\"action\": \"DROP\","
+ " \"from\": \"" +
+ string(DEFAULT_REMOTE_ADDRESS) +
+ "\"} ] }"));
+ parse_message->clear(Message::PARSE);
+ response_message->clear(Message::RENDER);
+ response_obuffer->clear();
+ server.processMessage(*io_message, parse_message, response_message,
+ response_obuffer, &dnsserv);
+ EXPECT_FALSE(dnsserv.hasAnswer());
+}
+
+
}
diff --git a/src/bin/resolver/tests/response_scrubber_unittest.cc b/src/bin/resolver/tests/response_scrubber_unittest.cc
index 1dc6639..1570def 100644
--- a/src/bin/resolver/tests/response_scrubber_unittest.cc
+++ b/src/bin/resolver/tests/response_scrubber_unittest.cc
@@ -41,6 +41,7 @@
// Class for endpoint checks. The family of the endpoint is set in the
// constructor; the address family by the string provided for the address.
+namespace isc {
namespace asiolink {
class GenericEndpoint : public IOEndpoint {
@@ -67,19 +68,26 @@ public:
return address_.getFamily();
}
+ // This is completely dummy and unused. Define it just for build.
+ virtual const struct sockaddr& getSockAddr() const {
+ static struct sockaddr sa;
+ return (sa);
+ }
+
private:
IOAddress address_; // Address of endpoint
uint16_t port_; // Port number of endpoint
short protocol_; // Protocol of the endpoint
};
}
+}
using namespace asio::ip;
using namespace isc::dns;
using namespace rdata;
using namespace isc::dns::rdata::generic;
using namespace isc::dns::rdata::in;
-using namespace asiolink;
+using namespace isc::asiolink;
// Test class
diff --git a/src/bin/resolver/tests/run_unittests.cc b/src/bin/resolver/tests/run_unittests.cc
index 6ae848d..d3bbab7 100644
--- a/src/bin/resolver/tests/run_unittests.cc
+++ b/src/bin/resolver/tests/run_unittests.cc
@@ -13,6 +13,8 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <log/logger_support.h>
+#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
@@ -21,6 +23,7 @@ main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
+ isc::log::initLogger();
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/bin/sockcreator/Makefile.am b/src/bin/sockcreator/Makefile.am
new file mode 100644
index 0000000..1ac4640
--- /dev/null
+++ b/src/bin/sockcreator/Makefile.am
@@ -0,0 +1,18 @@
+SUBDIRS = tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+
+CLEANFILES = *.gcno *.gcda
+
+pkglibexec_PROGRAMS = b10-sockcreator
+
+b10_sockcreator_SOURCES = sockcreator.cc sockcreator.h main.cc
+b10_sockcreator_LDADD = $(top_builddir)/src/lib/util/io/libutil_io.la
diff --git a/src/bin/sockcreator/README b/src/bin/sockcreator/README
new file mode 100644
index 0000000..4dbbee7
--- /dev/null
+++ b/src/bin/sockcreator/README
@@ -0,0 +1,49 @@
+The socket creator
+==================
+
+The only thing we need higher rights than standard user is binding sockets to
+ports lower than 1024. So we will have a separate process that keeps the
+rights, while the rests drop them for security reasons.
+
+This process is the socket creator. Its goal is to be as simple as possible
+and to contain as little code as possible to minimise the amount of code
+running with higher privileges (to minimize the number of bugs and make
+checking/auditing it easier). It uses low-level OS API instead of some
+fancy library for that reason. It has only fixed-length reads so there's no
+place for buffer overruns.
+
+Protocol
+--------
+
+It talks with whoever started it by its stdin/stdout. It reads simple
+binary protocol from stdin and does what the commands ask. Command is a single
+byte (usually from the printable range, so it is easier to debug and guess
+what it does), followed by parameters.
+
+Note that as send_fd and recv_fd works only with unix domain socket, it's stdio
+must be a socket, not pipe.
+
+* 'T': It has no parameters. It asks the socket creator to terminate.
+
+* 'S' 'U|T' '4|6' port address: Asks it to create a port. First parameter
+ tels the socket type (either UDP or TCP). The second one is address family
+ (either IPv4 or IPv6). Then there's 2 bytes of the port number, in the
+ network byte order. The last one is either 4 or 16 bytes of address, as
+ they would be passed to bind (note that both parameters are already prepared,
+ like hton called on them).
+
+ The answer to this is either 'S' directly followed by the socket (using
+ sendmsg) if it is successful. If it fails, 'E' is returned instead, followed
+ by either 'S' or 'B' (either socket() or bind() call failed). Then there is
+ one int (architecture-dependent length and endianess), which is the errno
+ value after the failure.
+
+The creator may also send these messages at any time (but not in the middle
+of another message):
+
+* 'F': A fatal error has been detected. It is followed by one byte of error
+ condition code and then the creator terminates with non-zero status.
+
+ The conditions are:
+ * 'I': Invalid input (eg. someone sent a wrong letter and it does not
+ understand it).
diff --git a/src/bin/sockcreator/main.cc b/src/bin/sockcreator/main.cc
new file mode 100644
index 0000000..37da303
--- /dev/null
+++ b/src/bin/sockcreator/main.cc
@@ -0,0 +1,26 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "sockcreator.h"
+
+using namespace isc::socket_creator;
+
+int
+main() {
+ /*
+ * TODO Maybe use some OS-specific caps interface and drop everything
+ * but ability to bind ports? It would be nice.
+ */
+ return run(0, 1); // Read commands from stdin, output to stdout
+}
diff --git a/src/bin/sockcreator/sockcreator.cc b/src/bin/sockcreator/sockcreator.cc
new file mode 100644
index 0000000..6b50813
--- /dev/null
+++ b/src/bin/sockcreator/sockcreator.cc
@@ -0,0 +1,151 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "sockcreator.h"
+
+#include <util/io/fd.h>
+
+#include <unistd.h>
+#include <cerrno>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+using namespace isc::util::io;
+
+namespace isc {
+namespace socket_creator {
+
+int
+get_sock(const int type, struct sockaddr *bind_addr, const socklen_t addr_len)
+{
+ int sock(socket(bind_addr->sa_family, type, 0));
+ if (sock == -1) {
+ return -1;
+ }
+ if (bind(sock, bind_addr, addr_len) == -1) {
+ return -2;
+ }
+ return sock;
+}
+
+// These are macros so they can exit the function
+#define READ(WHERE, HOW_MANY) do { \
+ size_t how_many = (HOW_MANY); \
+ if (read_data(input_fd, (WHERE), how_many) < how_many) { \
+ return 1; \
+ } \
+ } while (0)
+
+#define WRITE(WHAT, HOW_MANY) do { \
+ if (!write_data(output_fd, (WHAT), (HOW_MANY))) { \
+ return 2; \
+ } \
+ } while (0)
+
+#define DEFAULT \
+ default: /* Unrecognized part of protocol */ \
+ WRITE("FI", 2); \
+ return 3;
+
+int
+run(const int input_fd, const int output_fd, const get_sock_t get_sock,
+ const send_fd_t send_fd)
+{
+ for (;;) {
+ // Read the command
+ char command;
+ READ(&command, 1);
+ switch (command) {
+ case 'T': // The "terminate" command
+ return 0;
+ case 'S': { // Create a socket
+ // Read what type of socket they want
+ char type[2];
+ READ(type, 2);
+ // Read the address they ask for
+ struct sockaddr *addr(NULL);
+ size_t addr_len(0);
+ struct sockaddr_in addr_in;
+ struct sockaddr_in6 addr_in6;
+ switch (type[1]) { // The address family
+ /*
+ * Here are some casts. They are required by C++ and
+ * the low-level interface (they are implicit in C).
+ */
+ case '4':
+ addr = static_cast<struct sockaddr *>(
+ static_cast<void *>(&addr_in));
+ addr_len = sizeof addr_in;
+ memset(&addr_in, 0, sizeof addr_in);
+ addr_in.sin_family = AF_INET;
+ READ(static_cast<char *>(static_cast<void *>(
+ &addr_in.sin_port)), 2);
+ READ(static_cast<char *>(static_cast<void *>(
+ &addr_in.sin_addr.s_addr)), 4);
+ break;
+ case '6':
+ addr = static_cast<struct sockaddr *>(
+ static_cast<void *>(&addr_in6));
+ addr_len = sizeof addr_in6;
+ memset(&addr_in6, 0, sizeof addr_in6);
+ addr_in6.sin6_family = AF_INET6;
+ READ(static_cast<char *>(static_cast<void *>(
+ &addr_in6.sin6_port)), 2);
+ READ(static_cast<char *>(static_cast<void *>(
+ &addr_in6.sin6_addr.s6_addr)), 16);
+ break;
+ DEFAULT
+ }
+ int sock_type;
+ switch (type[0]) { // Translate the type
+ case 'T':
+ sock_type = SOCK_STREAM;
+ break;
+ case 'U':
+ sock_type = SOCK_DGRAM;
+ break;
+ DEFAULT
+ }
+ int result(get_sock(sock_type, addr, addr_len));
+ if (result >= 0) { // We got the socket
+ WRITE("S", 1);
+ // FIXME: Check the output and write a test for it
+ send_fd(output_fd, result);
+ } else {
+ WRITE("E", 1);
+ switch (result) {
+ case -1:
+ WRITE("S", 1);
+ break;
+ case -2:
+ WRITE("B", 1);
+ break;
+ default:
+ return 4;
+ }
+ int error(errno);
+ WRITE(static_cast<char *>(static_cast<void *>(&error)),
+ sizeof error);
+ }
+ break;
+ }
+ DEFAULT
+ }
+ }
+}
+
+} // End of the namespaces
+}
diff --git a/src/bin/sockcreator/sockcreator.h b/src/bin/sockcreator/sockcreator.h
new file mode 100644
index 0000000..ddf9a09
--- /dev/null
+++ b/src/bin/sockcreator/sockcreator.h
@@ -0,0 +1,100 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/**
+ * \file sockcreator.h
+ * \short Socket creator functionality.
+ *
+ * This module holds the functionality of the socket creator. It is
+ * a separate module from main to ease up the tests.
+ */
+
+#ifndef __SOCKCREATOR_H
+#define __SOCKCREATOR_H 1
+
+#include <util/io/fd_share.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+namespace isc {
+namespace socket_creator {
+
+/**
+ * \short Create a socket and bind it.
+ *
+ * This is just a bundle of socket() and bind() calls. The sa_family of
+ * bind_addr is used to determine the domain of the socket.
+ *
+ * \return The file descriptor of the newly created socket, if everything
+ * goes well. A negative number is returned if an error occurs -
+ * -1 if the socket() call fails or -2 if bind() fails. In case of error,
+ * errno is set (or better, left intact from socket() or bind()).
+ * \param type The type of socket to create (SOCK_STREAM, SOCK_DGRAM, etc).
+ * \param bind_addr The address to bind.
+ * \param addr_len The actual length of bind_addr.
+ */
+int
+get_sock(const int type, struct sockaddr *bind_addr, const socklen_t addr_len);
+
+/**
+ * Type of the get_sock function, to pass it as parameter.
+ */
+typedef
+int
+(*get_sock_t)(const int, struct sockaddr *, const socklen_t);
+
+/**
+ * Type of the send_fd() function, so it can be passed as a parameter.
+ */
+typedef
+int
+(*send_fd_t)(const int, const int);
+
+/**
+ * \short Infinite loop parsing commands and returning the sockets.
+ *
+ * This reads commands and socket descriptions from the input_fd
+ * file descriptor, creates sockets and writes the results (socket or
+ * error) to output_fd.
+ *
+ * Current errors are:
+ * - 1: Read error
+ * - 2: Write error
+ * - 3: Protocol error (unknown command, etc)
+ * - 4: Some internal inconsistency detected
+ *
+ * It terminates either if a command asks it to or when unrecoverable
+ * error happens.
+ *
+ * \return Like a return value of a main - 0 means everything OK, anything
+ * else is error.
+ * \param input_fd Here is where it reads the commads.
+ * \param output_fd Here is where it writes the results.
+ * \param get_sock_fun The function that is used to create the sockets.
+ * This should be left on the default value, the parameter is here
+ * for testing purposes.
+ * \param send_fd_fun The function that is used to send the socket over
+ * a file descriptor. This should be left on the default value, it is
+ * here for testing purposes.
+ */
+int
+run(const int input_fd, const int output_fd,
+ const get_sock_t get_sock_fun = get_sock,
+ const send_fd_t send_fd_fun = isc::util::io::send_fd);
+
+} // End of the namespaces
+}
+
+#endif // __SOCKCREATOR_H
diff --git a/src/bin/sockcreator/tests/Makefile.am b/src/bin/sockcreator/tests/Makefile.am
new file mode 100644
index 0000000..223e761
--- /dev/null
+++ b/src/bin/sockcreator/tests/Makefile.am
@@ -0,0 +1,24 @@
+CLEANFILES = *.gcno *.gcda
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = ../sockcreator.cc ../sockcreator.h
+run_unittests_SOURCES += sockcreator_tests.cc
+run_unittests_SOURCES += run_unittests.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/sockcreator/tests/run_unittests.cc b/src/bin/sockcreator/tests/run_unittests.cc
new file mode 100644
index 0000000..1287164
--- /dev/null
+++ b/src/bin/sockcreator/tests/run_unittests.cc
@@ -0,0 +1,23 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+
+int
+main(int argc, char *argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return isc::util::unittests::run_all();
+}
diff --git a/src/bin/sockcreator/tests/sockcreator_tests.cc b/src/bin/sockcreator/tests/sockcreator_tests.cc
new file mode 100644
index 0000000..73cbf48
--- /dev/null
+++ b/src/bin/sockcreator/tests/sockcreator_tests.cc
@@ -0,0 +1,273 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "../sockcreator.h"
+
+#include <util/unittests/fork.h>
+#include <util/io/fd.h>
+
+#include <gtest/gtest.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <cstring>
+#include <cerrno>
+
+using namespace isc::socket_creator;
+using namespace isc::util::unittests;
+using namespace isc::util::io;
+
+namespace {
+
+/*
+ * Generic version of the creation of socket test. It just tries to
+ * create the socket and checks the result is not negative (eg.
+ * it is valid descriptor) and that it can listen.
+ *
+ * This is a macro so ASSERT_* does abort the TEST, not just the
+ * function inside.
+ */
+#define TEST_ANY_CREATE(SOCK_TYPE, ADDR_TYPE, ADDR_FAMILY, FAMILY_FIELD, \
+ ADDR_SET, CHECK_SOCK) \
+ do { \
+ /*
+ * This should create an address that binds on all interfaces
+ * and lets the OS choose a free port.
+ */ \
+ struct ADDR_TYPE addr; \
+ memset(&addr, 0, sizeof addr); \
+ ADDR_SET(addr); \
+ addr.FAMILY_FIELD = ADDR_FAMILY; \
+ struct sockaddr *addr_ptr = static_cast<struct sockaddr *>( \
+ static_cast<void *>(&addr)); \
+ \
+ int socket = get_sock(SOCK_TYPE, addr_ptr, sizeof addr); \
+ /* Provide even nice error message. */ \
+ ASSERT_GE(socket, 0) << "Couldn't create a socket of type " \
+ #SOCK_TYPE " and family " #ADDR_FAMILY ", failed with " \
+ << socket << " and error " << strerror(errno); \
+ CHECK_SOCK(ADDR_TYPE, socket); \
+ EXPECT_EQ(0, close(socket)); \
+ } while (0)
+
+// Just helper macros
+#define INADDR_SET(WHAT) do { WHAT.sin_addr.s_addr = INADDR_ANY; } while (0)
+#define IN6ADDR_SET(WHAT) do { WHAT.sin6_addr = in6addr_loopback; } while (0)
+// If the get_sock returned something useful, listen must work
+#define TCP_CHECK(UNUSED, SOCKET) do { \
+ EXPECT_EQ(0, listen(SOCKET, 1)); \
+ } while (0)
+// More complicated with UDP, so we send a packet to ourselfs and se if it
+// arrives
+#define UDP_CHECK(ADDR_TYPE, SOCKET) do { \
+ struct ADDR_TYPE addr; \
+ memset(&addr, 0, sizeof addr); \
+ struct sockaddr *addr_ptr = static_cast<struct sockaddr *>( \
+ static_cast<void *>(&addr)); \
+ \
+ socklen_t len = sizeof addr; \
+ ASSERT_EQ(0, getsockname(SOCKET, addr_ptr, &len)); \
+ ASSERT_EQ(5, sendto(SOCKET, "test", 5, 0, addr_ptr, sizeof addr)) << \
+ "Send failed with error " << strerror(errno) << " on socket " << \
+ SOCKET; \
+ char buffer[5]; \
+ ASSERT_EQ(5, recv(SOCKET, buffer, 5, 0)) << \
+ "Recv failed with error " << strerror(errno) << " on socket " << \
+ SOCKET; \
+ EXPECT_STREQ("test", buffer); \
+ } while (0)
+
+/*
+ * Several tests to ensure we can create the sockets.
+ */
+TEST(get_sock, udp4_create) {
+ TEST_ANY_CREATE(SOCK_DGRAM, sockaddr_in, AF_INET, sin_family, INADDR_SET,
+ UDP_CHECK);
+}
+
+TEST(get_sock, tcp4_create) {
+ TEST_ANY_CREATE(SOCK_STREAM, sockaddr_in, AF_INET, sin_family, INADDR_SET,
+ TCP_CHECK);
+}
+
+TEST(get_sock, udp6_create) {
+ TEST_ANY_CREATE(SOCK_DGRAM, sockaddr_in6, AF_INET6, sin6_family,
+ IN6ADDR_SET, UDP_CHECK);
+}
+
+TEST(get_sock, tcp6_create) {
+ TEST_ANY_CREATE(SOCK_STREAM, sockaddr_in6, AF_INET6, sin6_family,
+ IN6ADDR_SET, TCP_CHECK);
+}
+
+/*
+ * Try to ask the get_sock function some nonsense and test if it
+ * is able to report error.
+ */
+TEST(get_sock, fail_with_nonsense) {
+ struct sockaddr addr;
+ memset(&addr, 0, sizeof addr);
+ ASSERT_LT(get_sock(0, &addr, sizeof addr), 0);
+}
+
+/*
+ * Helper functions to pass to run during testing.
+ */
+int
+get_sock_dummy(const int type, struct sockaddr *addr, const socklen_t)
+{
+ int result(0);
+ int port(0);
+ /*
+ * We encode the type and address family into the int and return it.
+ * Lets ignore the port and address for now
+ * First bit is 1 if it is known type. Second tells if TCP or UDP.
+ * The familly is similar - third bit is known address family,
+ * the fourth is the family.
+ */
+ switch (type) {
+ case SOCK_STREAM:
+ result += 1;
+ break;
+ case SOCK_DGRAM:
+ result += 3;
+ break;
+ }
+ switch (addr->sa_family) {
+ case AF_INET:
+ result += 4;
+ port = static_cast<struct sockaddr_in *>(
+ static_cast<void *>(addr))->sin_port;
+ break;
+ case AF_INET6:
+ result += 12;
+ port = static_cast<struct sockaddr_in6 *>(
+ static_cast<void *>(addr))->sin6_port;
+ break;
+ }
+ /*
+ * The port should be 0xffff. If it's not, we change the result.
+ * The port of 0xbbbb means bind should fail and 0xcccc means
+ * socket should fail.
+ */
+ if (port != 0xffff) {
+ errno = 0;
+ if (port == 0xbbbb) {
+ return -2;
+ } else if (port == 0xcccc) {
+ return -1;
+ } else {
+ result += 16;
+ }
+ }
+ return result;
+}
+
+int
+send_fd_dummy(const int destination, const int what)
+{
+ /*
+ * Make sure it is 1 byte so we know the length. We do not use more during
+ * the test anyway.
+ */
+ char fd_data(what);
+ if (!write_data(destination, &fd_data, 1)) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * Generic test that it works, with various inputs and outputs.
+ * It uses different functions to create the socket and send it and pass
+ * data to it and check it returns correct data back, to see if the run()
+ * parses the commands correctly.
+ */
+void run_test(const char *input_data, const size_t input_size,
+ const char *output_data, const size_t output_size,
+ bool should_succeed = true)
+{
+ // Prepare the input feeder and output checker processes
+ int input_fd(0), output_fd(0);
+ pid_t input(provide_input(&input_fd, input_data, input_size)),
+ output(check_output(&output_fd, output_data, output_size));
+ ASSERT_NE(-1, input) << "Couldn't start input feeder";
+ ASSERT_NE(-1, output) << "Couldn't start output checker";
+ // Run the body
+ int result(run(input_fd, output_fd, get_sock_dummy, send_fd_dummy));
+ // Close the pipes
+ close(input_fd);
+ close(output_fd);
+ // Did it run well?
+ if (should_succeed) {
+ EXPECT_EQ(0, result);
+ } else {
+ EXPECT_NE(0, result);
+ }
+ // Check the subprocesses say everything is OK too
+ EXPECT_TRUE(process_ok(input));
+ EXPECT_TRUE(process_ok(output));
+}
+
+/*
+ * Check it terminates successfully when asked to.
+ */
+TEST(run, terminate) {
+ run_test("T", 1, NULL, 0);
+}
+
+/*
+ * Check it rejects incorrect input.
+ */
+TEST(run, bad_input) {
+ run_test("XXX", 3, "FI", 2, false);
+}
+
+/*
+ * Check it correctly parses queries to create sockets.
+ */
+TEST(run, sockets) {
+ run_test(
+ "SU4\xff\xff\0\0\0\0" // This has 9 bytes
+ "ST4\xff\xff\0\0\0\0" // This has 9 bytes
+ "ST6\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // This has 21 bytes
+ "SU6\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // This has 21 bytes
+ "T", 61,
+ "S\x07S\x05S\x0dS\x0f", 8);
+}
+
+/*
+ * Check if failures of get_socket are handled correctly.
+ */
+TEST(run, bad_sockets) {
+ // We need to construct the answer, but it depends on int length.
+ size_t int_len(sizeof(int));
+ size_t result_len(4 + 2 * int_len);
+ char result[4 + sizeof(int) * 2];
+ // Both errno parts should be 0
+ memset(result, 0, result_len);
+ // Fill the 2 control parts
+ strcpy(result, "EB");
+ strcpy(result + 2 + int_len, "ES");
+ // Run the test
+ run_test(
+ "SU4\xbb\xbb\0\0\0\0"
+ "SU4\xcc\xcc\0\0\0\0"
+ "T", 19,
+ result, result_len);
+}
+
+}
diff --git a/src/bin/stats/Makefile.am b/src/bin/stats/Makefile.am
index b173813..c8b18c9 100644
--- a/src/bin/stats/Makefile.am
+++ b/src/bin/stats/Makefile.am
@@ -2,35 +2,40 @@ SUBDIRS = tests
pkglibexecdir = $(libexecdir)/@PACKAGE@
-pkglibexec_SCRIPTS = b10-stats
-noinst_SCRIPTS = b10-stats_stub
+pkglibexec_SCRIPTS = b10-stats b10-stats-httpd
b10_statsdir = $(pkgdatadir)
-b10_stats_DATA = stats.spec
+b10_stats_DATA = stats.spec stats-httpd.spec stats-schema.spec
+b10_stats_DATA += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl
-CLEANFILES = stats.spec b10-stats stats.pyc stats.pyo b10-stats_stub stats_stub.pyc stats_stub.pyo
+CLEANFILES = b10-stats stats.pyc
+CLEANFILES += b10-stats-httpd stats_httpd.pyc
-man_MANS = b10-stats.8
-EXTRA_DIST = $(man_MANS) b10-stats.xml
+man_MANS = b10-stats.8 b10-stats-httpd.8
+EXTRA_DIST = $(man_MANS) b10-stats.xml b10-stats-httpd.xml
+EXTRA_DIST += stats.spec stats-httpd.spec stats-schema.spec
+EXTRA_DIST += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl
if ENABLE_MAN
b10-stats.8: b10-stats.xml
xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-stats.xml
-endif
+b10-stats-httpd.8: b10-stats-httpd.xml
+ xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-stats-httpd.xml
-stats.spec: stats.spec.pre
- $(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" stats.spec.pre >$@
+endif
# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
b10-stats: stats.py
- $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
- -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" \
- -e "s|.*#@@REMOVED@@$$||" stats.py >$@
+ $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" stats.py >$@
chmod a+x $@
-b10-stats_stub: stats_stub.py stats.py
- $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
- -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" stats_stub.py >$@
+b10-stats-httpd: stats_httpd.py
+ $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" stats_httpd.py >$@
chmod a+x $@
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/stats/b10-stats-httpd.8 b/src/bin/stats/b10-stats-httpd.8
new file mode 100644
index 0000000..ed4aafa
--- /dev/null
+++ b/src/bin/stats/b10-stats-httpd.8
@@ -0,0 +1,136 @@
+'\" t
+.\" Title: b10-stats-httpd
+.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
+.\" Date: Mar 8, 2011
+.\" Manual: BIND10
+.\" Source: BIND10
+.\" Language: English
+.\"
+.TH "B10\-STATS\-HTTPD" "8" "Mar 8, 2011" "BIND10" "BIND10"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+b10-stats-httpd \- BIND 10 HTTP server for HTTP/XML interface of statistics
+.SH "SYNOPSIS"
+.HP \w'\fBb10\-stats\-httpd\fR\ 'u
+\fBb10\-stats\-httpd\fR [\fB\-v\fR]| [\fB\-\-verbose\fR]
+.SH "DESCRIPTION"
+.PP
+
+\fBb10\-stats\-httpd\fR
+is a standalone HTTP server\&. It is intended for HTTP/XML interface for statistics module\&. This server process runs as a process separated from the process of the BIND 10 Stats daemon (\fBb10\-stats\fR)\&. The server is initially executed by the BIND 10 boss process (\fBbind10\fR) and eventually exited by it\&. The server is intended to be server requests by HTTP clients like web browsers and third\-party modules\&. When the server is asked, it requests BIND 10 statistics data from
+\fBb10\-stats\fR, and it sends the data back in Python dictionary format and the server converts it into XML format\&. The server sends it to the HTTP client\&. The server can send three types of document, which are XML (Extensible Markup Language), XSD (XML Schema definition) and XSL (Extensible Stylesheet Language)\&. The XML document is the statistics data of BIND 10, The XSD document is the data schema of it, and The XSL document is the style sheet to be showed for the web browsers\&. There is different URL for each document\&. But please note that you would be redirected to the URL of XML document if you request the URL of the root document\&. For example, you would be redirected to http://127\&.0\&.0\&.1:8000/bind10/statistics/xml if you request http://127\&.0\&.0\&.1:8000/\&. Please see the manual and the spec file of
+\fBb10\-stats\fR
+for more details about the items of BIND 10 statistics\&. The server uses CC session in communication with
+\fBb10\-stats\fR\&. CC session is provided by
+\fBb10\-msgq\fR
+which is started by
+\fBbind10\fR
+in advance\&. The server is implemented by HTTP\-server libraries included in Python 3\&. The server obtains the configuration from the config manager (\fBb10\-cfgmgr\fR) in runtime\&. Please see below for more details about this spec file and configuration of the server\&.
+.SH "OPTIONS"
+.PP
+The argument is as follow:
+.PP
+\fB\-v\fR, \fB\-\-verbose\fR
+.RS 4
+
+\fBb10\-stats\-httpd\fR
+switches to verbose mode and sends verbose messages to STDOUT\&.
+.RE
+.SH "FILES"
+.PP
+
+/usr/local/share/bind10\-devel/stats\-httpd\&.spec
+\(em the spec file of
+\fBb10\-stats\-httpd\fR\&. This file contains configurable settings of
+\fBb10\-stats\-httpd\fR\&. This setting can be configured in runtime via
+bindctl(1)\&. Please see the manual of
+bindctl(1)
+about how to configure the settings\&.
+.PP
+/usr/local/share/bind10\-devel/stats\-schema\&.spec
+\(em This is a spec file for data schema of of BIND 10 statistics\&. This schema cannot be configured via
+bindctl(1)\&.
+.PP
+
+/usr/local/share/bind10\-devel/stats\-httpd\-xml\&.tpl
+\(em the template file of XML document\&.
+.PP
+
+/usr/local/share/bind10\-devel/stats\-httpd\-xsd\&.tpl
+\(em the template file of XSD document\&.
+.PP
+
+/usr/local/share/bind10\-devel/stats\-httpd\-xsl\&.tpl
+\(em the template file of XSL document\&.
+.SH "CONFIGURATION AND COMMANDS"
+.PP
+The configurable setting in
+stats\-httpd\&.spec
+is:
+.PP
+\fIlisten_on\fR
+.RS 4
+a list of pairs of address and port for
+\fBb10\-stats\-httpd\fR
+to listen HTTP requests on\&. The pair consists of the
+\fIaddress\fR
+string and
+\fIport\fR
+number\&. The default setting is the list of address 127\&.0\&.0\&.1 port 8000\&. If the server is started by the default setting being left, for example, the URL for XML document is http://127\&.0\&.0\&.1:8000/bind10/statistics/xml\&. And also IPv6 addresses can be configured and they works in the runtime environment for dual stack\&. You can change the settings through
+bindctl(8)\&.
+.RE
+.PP
+The commands in
+stats\-httpd\&.spec
+are:
+.PP
+\fBstatus\fR
+.RS 4
+shows the status of
+\fBb10\-stats\-httpd\fR
+with its PID\&.
+.RE
+.PP
+\fBshutdown\fR
+.RS 4
+exits the
+\fBb10\-stats\-httpd\fR
+process\&. (Note that the BIND 10 boss process will restart this service\&.)
+.RE
+.SH "SEE ALSO"
+.PP
+
+\fBb10-stats\fR(8),
+\fBb10-msgq\fR(8),
+\fBb10-cfgmgr\fR(8),
+\fBbind10\fR(8),
+\fBbindctl\fR(1),
+BIND 10 Guide\&.
+.SH "HISTORY"
+.PP
+
+\fBb10\-stats\-httpd\fR
+was designed and implemented by Naoki Kambe of JPRS in Mar 2011\&.
+.SH "COPYRIGHT"
+.br
+Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC")
+.br
diff --git a/src/bin/stats/b10-stats-httpd.xml b/src/bin/stats/b10-stats-httpd.xml
new file mode 100644
index 0000000..34c704f
--- /dev/null
+++ b/src/bin/stats/b10-stats-httpd.xml
@@ -0,0 +1,221 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+ [<!ENTITY mdash "—">]>
+<!--
+ - Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<refentry>
+
+ <refentryinfo>
+ <date>Mar 8, 2011</date>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>b10-stats-httpd</refentrytitle>
+ <manvolnum>8</manvolnum>
+ <refmiscinfo>BIND10</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>b10-stats-httpd</refname>
+ <refpurpose>BIND 10 HTTP server for HTTP/XML interface of statistics</refpurpose>
+ </refnamediv>
+
+ <docinfo>
+ <copyright>
+ <year>2011</year>
+ <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+ </copyright>
+ </docinfo>
+
+ <refsynopsisdiv>
+ <cmdsynopsis> <command>b10-stats-httpd</command>
+ <arg><option>-v</option></arg>|<arg><option>--verbose</option></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>DESCRIPTION</title>
+ <para>
+ <command>b10-stats-httpd</command> is a standalone HTTP server. It is
+ intended for HTTP/XML interface for statistics module. This server
+ process runs as a process separated from the process of the BIND 10 Stats
+ daemon (<command>b10-stats</command>). The server is initially executed
+ by the BIND 10 boss process (<command>bind10</command>) and eventually
+ exited by it. The server is intended to be server requests by HTTP
+ clients like web browsers and third-party modules. When the server is
+ asked, it requests BIND 10 statistics data from
+ <command>b10-stats</command>, and it sends the data back in Python
+ dictionary format and the server converts it into XML format. The server
+ sends it to the HTTP client. The server can send three types of document,
+ which are XML (Extensible Markup Language), XSD (XML Schema definition)
+ and XSL (Extensible Stylesheet Language). The XML document is the
+ statistics data of BIND 10, The XSD document is the data schema of it,
+ and The XSL document is the style sheet to be showed for the web
+ browsers. There is different URL for each document. But please note that
+ you would be redirected to the URL of XML document if you request the URL
+ of the root document. For example, you would be redirected to
+ http://127.0.0.1:8000/bind10/statistics/xml if you request
+ http://127.0.0.1:8000/. Please see the manual and the spec file
+ of <command>b10-stats</command> for more details about the items of BIND
+ 10 statistics. The server uses CC session in communication
+ with <command>b10-stats</command>. CC session is provided
+ by <command>b10-msgq</command> which is started
+ by <command>bind10</command> in advance. The server is implemented by
+ HTTP-server libraries included in Python 3. The server obtains the
+ configuration from the config manager (<command>b10-cfgmgr</command>) in
+ runtime. Please see below for more details about this spec file and
+ configuration of the server.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>OPTIONS</title>
+ <para>The argument is as follow:</para>
+ <variablelist>
+ <varlistentry>
+ <term><option>-v</option>, <option>--verbose</option></term>
+ <listitem>
+ <para>
+ <command>b10-stats-httpd</command> switches to verbose mode and sends
+ verbose messages to STDOUT.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>FILES</title>
+ <para>
+ <filename>/usr/local/share/bind10-devel/stats-httpd.spec</filename>
+ <!--TODO: The filename should be computed from prefix-->
+ — the spec file of <command>b10-stats-httpd</command>. This file
+ contains configurable settings
+ of <command>b10-stats-httpd</command>. This setting can be configured in
+ runtime via
+ <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum>. Please
+ see the manual
+ of <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum> about
+ how to configure the settings.
+ </para>
+ <para><filename>/usr/local/share/bind10-devel/stats-schema.spec</filename>
+ <!--TODO: The filename should be computed from prefix-->
+ — This is a spec file for data schema of
+ of BIND 10 statistics. This schema cannot be configured
+ via <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum>.
+ </para>
+ <para>
+ <filename>/usr/local/share/bind10-devel/stats-httpd-xml.tpl</filename>
+ <!--TODO: The filename should be computed from prefix-->
+ — the template file of XML document.
+ </para>
+ <para>
+ <filename>/usr/local/share/bind10-devel/stats-httpd-xsd.tpl</filename>
+ <!--TODO: The filename should be computed from prefix-->
+ — the template file of XSD document.
+ </para>
+ <para>
+ <filename>/usr/local/share/bind10-devel/stats-httpd-xsl.tpl</filename>
+ <!--TODO: The filename should be computed from prefix-->
+ — the template file of XSL document.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>CONFIGURATION AND COMMANDS</title>
+ <para>
+ The configurable setting in
+ <filename>stats-httpd.spec</filename> is:
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term><varname>listen_on</varname></term>
+ <listitem>
+ <para>
+ a list of pairs of address and port for
+ <command>b10-stats-httpd</command> to listen HTTP requests on. The
+ pair consists of the <varname>address</varname> string
+ and <varname>port</varname> number. The default setting is the list
+ of address 127.0.0.1 port 8000. If the server is started by the
+ default setting being left, for example, the URL for XML document
+ is http://127.0.0.1:8000/bind10/statistics/xml. And also IPv6
+ addresses can be configured and they works in the runtime
+ environment for dual stack. You can change the settings
+ through <refentrytitle>bindctl</refentrytitle><manvolnum>8</manvolnum>.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ <para>
+ The commands in <filename>stats-httpd.spec</filename> are:
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term><command>status</command></term>
+ <listitem>
+ <para>
+ shows the status of <command>b10-stats-httpd</command> with its
+ PID.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><command>shutdown</command></term>
+ <listitem>
+ <para>
+ exits the <command>b10-stats-httpd</command> process. (Note that
+ the BIND 10 boss process will restart this service.)
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>SEE ALSO</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>b10-stats</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum>
+ </citerefentry>,
+ <citetitle>BIND 10 Guide</citetitle>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>HISTORY</title>
+ <para>
+ <command>b10-stats-httpd</command> was designed and implemented by Naoki
+ Kambe of JPRS in Mar 2011.
+ </para>
+ </refsect1>
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->
diff --git a/src/bin/stats/b10-stats.8 b/src/bin/stats/b10-stats.8
index 062ff35..f69e4d3 100644
--- a/src/bin/stats/b10-stats.8
+++ b/src/bin/stats/b10-stats.8
@@ -1,7 +1,7 @@
'\" t
.\" Title: b10-stats
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
+.\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
.\" Date: Oct 15, 2010
.\" Manual: BIND10
.\" Source: BIND10
@@ -9,6 +9,15 @@
.\"
.TH "B10\-STATS" "8" "Oct 15, 2010" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
@@ -35,6 +44,11 @@ with other modules like
\fBbind10\fR,
\fBb10\-auth\fR
and so on\&. It waits for coming data from other modules, then other modules send data to stats module periodically\&. Other modules send stats data to stats module independently from implementation of stats module, so the frequency of sending data may not be constant\&. Stats module collects data and aggregates it\&.
+\fBb10\-stats\fR
+invokes "sendstats" command for
+\fBbind10\fR
+after its initial starting because it\*(Aqs sure to collect statistics data from
+\fBbind10\fR\&.
.SH "OPTIONS"
.PP
The arguments are as follows:
@@ -49,10 +63,17 @@ switches to verbose mode\&. It sends verbose messages to STDOUT\&.
.PP
/usr/local/share/bind10\-devel/stats\&.spec
\(em This is a spec file for
-\fBb10\-stats\fR\&. It contains definitions of statistics items of BIND 10 and commands received vi bindctl\&.
+\fBb10\-stats\fR\&. It contains commands for
+\fBb10\-stats\fR\&. They can be invoked via
+bindctl(1)\&.
+.PP
+/usr/local/share/bind10\-devel/stats\-schema\&.spec
+\(em This is a spec file for data schema of of BIND 10 statistics\&. This schema cannot be configured via
+bindctl(1)\&.
.SH "SEE ALSO"
.PP
+\fBb10-stats-httpd\fR(8),
\fBbind10\fR(8),
\fBbindctl\fR(1),
\fBb10-auth\fR(8),
diff --git a/src/bin/stats/b10-stats.xml b/src/bin/stats/b10-stats.xml
index f622439..f0c472d 100644
--- a/src/bin/stats/b10-stats.xml
+++ b/src/bin/stats/b10-stats.xml
@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "—">]>
<!--
- - Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010,2011 Internet Systems Consortium, Inc. ("ISC")
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
@@ -64,7 +64,9 @@
send stats data to stats module independently from
implementation of stats module, so the frequency of sending data
may not be constant. Stats module collects data and aggregates
- it.
+ it. <command>b10-stats</command> invokes "sendstats" command
+ for <command>bind10</command> after its initial starting because it's
+ sure to collect statistics data from <command>bind10</command>.
</para>
</refsect1>
@@ -87,10 +89,17 @@
<refsect1>
<title>FILES</title>
<para><filename>/usr/local/share/bind10-devel/stats.spec</filename>
+ <!--TODO: The filename should be computed from prefix-->
— This is a spec file for <command>b10-stats</command>. It
- contains definitions of statistics items of BIND 10 and commands
- received via
- <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum>.
+ contains commands for <command>b10-stats</command>. They can be
+ invoked
+ via <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum>.
+ </para>
+ <para><filename>/usr/local/share/bind10-devel/stats-schema.spec</filename>
+ <!--TODO: The filename should be computed from prefix-->
+ — This is a spec file for data schema of
+ of BIND 10 statistics. This schema cannot be configured
+ via <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum>.
</para>
</refsect1>
@@ -98,6 +107,9 @@
<title>SEE ALSO</title>
<para>
<citerefentry>
+ <refentrytitle>b10-stats-httpd</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
<refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
</citerefentry>,
<citerefentry>
diff --git a/src/bin/stats/run_b10-stats.sh.in b/src/bin/stats/run_b10-stats.sh.in
deleted file mode 100644
index 65b9737..0000000
--- a/src/bin/stats/run_b10-stats.sh.in
+++ /dev/null
@@ -1,33 +0,0 @@
-#! /bin/sh
-
-# Copyright (C) 2010 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.
-
-PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
-export PYTHON_EXEC
-
-PYTHONPATH=@abs_top_builddir@/src/lib/python
-export PYTHONPATH
-
-BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
-export BIND10_MSGQ_SOCKET_FILE
-
-B10_FROM_BUILD=@abs_top_builddir@
-export B10_FROM_BUILD
-
-STATS_PATH=@abs_top_builddir@/src/bin/stats
-
-cd ${STATS_PATH}
-exec ${PYTHON_EXEC} -O b10-stats "$@"
diff --git a/src/bin/stats/run_b10-stats_stub.sh.in b/src/bin/stats/run_b10-stats_stub.sh.in
deleted file mode 100644
index 19ade5c..0000000
--- a/src/bin/stats/run_b10-stats_stub.sh.in
+++ /dev/null
@@ -1,33 +0,0 @@
-#! /bin/sh
-
-# Copyright (C) 2010 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.
-
-PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
-export PYTHON_EXEC
-
-PYTHONPATH=@abs_top_builddir@/src/lib/python
-export PYTHONPATH
-
-B10_FROM_BUILD=@abs_top_srcdir@
-export B10_FROM_BUILD
-
-BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
-export BIND10_MSGQ_SOCKET_FILE
-
-STATS_PATH=@abs_top_builddir@/src/bin/stats
-
-cd ${STATS_PATH}
-exec ${PYTHON_EXEC} -O b10-stats_stub "$@"
diff --git a/src/bin/stats/stats-httpd-xml.tpl b/src/bin/stats/stats-httpd-xml.tpl
new file mode 100644
index 0000000..d5846ad
--- /dev/null
+++ b/src/bin/stats/stats-httpd-xml.tpl
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="$xsl_url_path"?>
+<!--
+ - Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<stats:stats_data version="1.0"
+ xmlns:stats="$xsd_namespace"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="$xsd_namespace $xsd_url_path">
+ $xml_string
+</stats:stats_data>
diff --git a/src/bin/stats/stats-httpd-xsd.tpl b/src/bin/stats/stats-httpd-xsd.tpl
new file mode 100644
index 0000000..6ad1280
--- /dev/null
+++ b/src/bin/stats/stats-httpd-xsd.tpl
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<schema targetNamespace="$xsd_namespace"
+ xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:stats="$xsd_namespace">
+ <annotation>
+ <documentation xml:lang="en">XML schema of the statistics
+ data in BIND 10</documentation>
+ </annotation>
+ <element name="stats_data">
+ <annotation>
+ <documentation>A set of statistics data</documentation>
+ </annotation>
+ <complexType>
+ $xsd_string
+ <attribute name="version" type="token" use="optional" default="1.0">
+ <annotation>
+ <documentation>Version number of syntax</documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+</schema>
diff --git a/src/bin/stats/stats-httpd-xsl.tpl b/src/bin/stats/stats-httpd-xsl.tpl
new file mode 100644
index 0000000..01ffdc6
--- /dev/null
+++ b/src/bin/stats/stats-httpd-xsl.tpl
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:stats="$xsd_namespace">
+ <xsl:output method="html" encoding="UTF-8"
+ doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"
+ doctype-system=" http://www.w3.org/TR/html4/loose.dtd " />
+ <xsl:template match="/">
+ <html lang="en">
+ <head>
+ <title>BIND 10 Statistics</title>
+ <style type="text/css"><![CDATA[
+table {
+ border: 1px #000000 solid;
+ border-collapse: collapse;
+}
+td, th {
+ padding: 3px 20px;
+ border: 1px #000000 solid;
+}
+td.title {
+ text-decoration:underline;
+}
+]]>
+ </style>
+ </head>
+ <body>
+ <h1>BIND 10 Statistics</h1>
+ <table>
+ <tr>
+ <th>Title</th>
+ <th>Value</th>
+ </tr>
+ <xsl:apply-templates />
+ </table>
+ </body>
+ </html>
+ </xsl:template>
+ $xsl_string
+</xsl:stylesheet>
diff --git a/src/bin/stats/stats-httpd.spec b/src/bin/stats/stats-httpd.spec
new file mode 100644
index 0000000..6307135
--- /dev/null
+++ b/src/bin/stats/stats-httpd.spec
@@ -0,0 +1,54 @@
+{
+ "module_spec": {
+ "module_name": "StatsHttpd",
+ "module_description": "Stats HTTP daemon",
+ "config_data": [
+ {
+ "item_name": "listen_on",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [
+ {
+ "address": "127.0.0.1",
+ "port": 8000
+ }
+ ],
+ "list_item_spec": {
+ "item_name": "address",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ {
+ "item_name": "address",
+ "item_type": "string",
+ "item_optional": true,
+ "item_default": "127.0.0.1",
+ "item_description": "listen-on address for HTTP"
+ },
+ {
+ "item_name": "port",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 8000,
+ "item_description": "listen-on port for HTTP"
+ }
+ ]
+ },
+ "item_description": "http listen-on address and port"
+ }
+ ],
+ "commands": [
+ {
+ "command_name": "status",
+ "command_description": "Status of the stats httpd",
+ "command_args": []
+ },
+ {
+ "command_name": "shutdown",
+ "command_description": "Shut down the stats httpd",
+ "command_args": []
+ }
+ ]
+ }
+}
diff --git a/src/bin/stats/stats-schema.spec b/src/bin/stats/stats-schema.spec
new file mode 100644
index 0000000..37e9c1a
--- /dev/null
+++ b/src/bin/stats/stats-schema.spec
@@ -0,0 +1,87 @@
+{
+ "module_spec": {
+ "module_name": "Stats",
+ "module_description": "Statistics data schema",
+ "config_data": [
+ {
+ "item_name": "report_time",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "1970-01-01T00:00:00Z",
+ "item_title": "Report time",
+ "item_description": "A date time when stats module reports",
+ "item_format": "date-time"
+ },
+ {
+ "item_name": "bind10.boot_time",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "1970-01-01T00:00:00Z",
+ "item_title": "bind10.BootTime",
+ "item_description": "A date time when bind10 process starts initially",
+ "item_format": "date-time"
+ },
+ {
+ "item_name": "stats.boot_time",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "1970-01-01T00:00:00Z",
+ "item_title": "stats.BootTime",
+ "item_description": "A date time when the stats module starts initially or when the stats module restarts",
+ "item_format": "date-time"
+ },
+ {
+ "item_name": "stats.start_time",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "1970-01-01T00:00:00Z",
+ "item_title": "stats.StartTime",
+ "item_description": "A date time when the stats module starts collecting data or resetting values last time",
+ "item_format": "date-time"
+ },
+ {
+ "item_name": "stats.last_update_time",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "1970-01-01T00:00:00Z",
+ "item_title": "stats.LastUpdateTime",
+ "item_description": "The latest date time when the stats module receives from other modules like auth server or boss process and so on",
+ "item_format": "date-time"
+ },
+ {
+ "item_name": "stats.timestamp",
+ "item_type": "real",
+ "item_optional": false,
+ "item_default": 0.0,
+ "item_title": "stats.Timestamp",
+ "item_description": "A current time stamp since epoch time (1970-01-01T00:00:00Z)",
+ "item_format": "second"
+ },
+ {
+ "item_name": "stats.lname",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "",
+ "item_title": "stats.LocalName",
+ "item_description": "A localname of stats module given via CC protocol"
+ },
+ {
+ "item_name": "auth.queries.tcp",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "auth.queries.tcp",
+ "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially"
+ },
+ {
+ "item_name": "auth.queries.udp",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "auth.queries.udp",
+ "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially"
+ }
+ ],
+ "commands": []
+ }
+}
diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in
old mode 100755
new mode 100644
index 15e2980..969676e
--- a/src/bin/stats/stats.py.in
+++ b/src/bin/stats/stats.py.in
@@ -1,6 +1,6 @@
#!@PYTHON@
-# Copyright (C) 2010 Internet Systems Consortium.
+# Copyright (C) 2010, 2011 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
@@ -15,8 +15,6 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-__version__ = "$Revision$"
-
import sys; sys.path.append ('@@PYTHONPATH@@')
import os
import signal
@@ -26,26 +24,24 @@ from optparse import OptionParser, OptionValueError
from collections import defaultdict
from isc.config.ccsession import ModuleCCSession, create_answer
from isc.cc import Session, SessionError
-# Note: Following lines are removed in b10-stats #@@REMOVED@@
-if __name__ == 'stats': #@@REMOVED@@
- try: #@@REMOVED@@
- from fake_time import time, strftime, gmtime #@@REMOVED@@
- except ImportError: #@@REMOVED@@
- pass #@@REMOVED@@
# for setproctitle
import isc.util.process
isc.util.process.rename()
-# If B10_FROM_BUILD is set in the environment, we use data files
+# If B10_FROM_SOURCE is set in the environment, we use data files
# from a directory relative to that, otherwise we use the ones
# installed on the system
-if "B10_FROM_BUILD" in os.environ:
- SPECFILE_LOCATION = os.environ["B10_FROM_BUILD"] + "/src/bin/stats/stats.spec"
+if "B10_FROM_SOURCE" in os.environ:
+ BASE_LOCATION = os.environ["B10_FROM_SOURCE"] + os.sep + \
+ "src" + os.sep + "bin" + os.sep + "stats"
else:
PREFIX = "@prefix@"
DATAROOTDIR = "@datarootdir@"
- SPECFILE_LOCATION = "@datadir@/@PACKAGE@/stats.spec".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
+ BASE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@"
+ BASE_LOCATION = BASE_LOCATION.replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
+SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats.spec"
+SCHEMA_SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-schema.spec"
class Singleton(type):
"""
@@ -184,8 +180,7 @@ class CCSessionListener(Listener):
self.session = self.subject.session = self.cc_session._session
# initialize internal data
- self.config_spec = self.cc_session.get_module_spec().get_config_spec()
- self.stats_spec = self.config_spec
+ self.stats_spec = isc.config.module_spec_from_file(SCHEMA_SPECFILE_LOCATION).get_config_spec()
self.stats_data = self.initialize_data(self.stats_spec)
# add event handler invoked via SessionSubject object
@@ -220,7 +215,13 @@ class CCSessionListener(Listener):
self.stats_data['stats.start_time'] = get_datetime()
self.stats_data['stats.last_update_time'] = get_datetime()
self.stats_data['stats.lname'] = self.session.lname
- return self.cc_session.start()
+ self.cc_session.start()
+ # request Bob to send statistics data
+ if self.verbose:
+ sys.stdout.write("[b10-stats] request Bob to send statistics data\n")
+ cmd = isc.config.ccsession.create_command("sendstats", None)
+ seq = self.session.group_sendmsg(cmd, 'Boss')
+ self.session.group_recvmsg(True, seq)
def stop(self):
"""
@@ -270,9 +271,6 @@ class CCSessionListener(Listener):
"""
handle set command
"""
- if self.verbose:
- sys.stdout.write("[b10-stats] 'set' command received, args: "+str(args)+"\n")
-
# 'args' must be dictionary type
self.stats_data.update(args['stats_data'])
diff --git a/src/bin/stats/stats.spec b/src/bin/stats/stats.spec
new file mode 100644
index 0000000..25f6b54
--- /dev/null
+++ b/src/bin/stats/stats.spec
@@ -0,0 +1,61 @@
+{
+ "module_spec": {
+ "module_name": "Stats",
+ "module_description": "Stats daemon",
+ "config_data": [],
+ "commands": [
+ {
+ "command_name": "status",
+ "command_description": "identify whether stats module is alive or not",
+ "command_args": []
+ },
+ {
+ "command_name": "show",
+ "command_description": "show the specified/all statistics data",
+ "command_args": [
+ {
+ "item_name": "stats_item_name",
+ "item_type": "string",
+ "item_optional": true,
+ "item_default": ""
+ }
+ ]
+ },
+ {
+ "command_name": "set",
+ "command_description": "set the value of specified name in statistics data",
+ "command_args": [
+ {
+ "item_name": "stats_data",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": []
+ }
+ ]
+ },
+ {
+ "command_name": "remove",
+ "command_description": "remove the specified name from statistics data",
+ "command_args": [
+ {
+ "item_name": "stats_item_name",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ }
+ ]
+ },
+ {
+ "command_name": "reset",
+ "command_description": "reset all statistics data to default values except for several constant names",
+ "command_args": []
+ },
+ {
+ "command_name": "shutdown",
+ "command_description": "Shut down the stats module",
+ "command_args": []
+ }
+ ]
+ }
+}
diff --git a/src/bin/stats/stats.spec.pre.in b/src/bin/stats/stats.spec.pre.in
deleted file mode 100644
index 6970250..0000000
--- a/src/bin/stats/stats.spec.pre.in
+++ /dev/null
@@ -1,140 +0,0 @@
-{
- "module_spec": {
- "module_name": "Stats",
- "module_description": "Stats daemon",
- "config_data": [
- {
- "item_name": "report_time",
- "item_type": "string",
- "item_optional": false,
- "item_default": "1970-01-01T00:00:00Z",
- "item_title": "Report time",
- "item_description": "A date time when stats module reports",
- "item_format": "date-time"
- },
- {
- "item_name": "bind10.boot_time",
- "item_type": "string",
- "item_optional": false,
- "item_default": "1970-01-01T00:00:00Z",
- "item_title": "stats.BootTime",
- "item_description": "A date time when bind10 process starts initially",
- "item_format": "date-time"
- },
- {
- "item_name": "stats.boot_time",
- "item_type": "string",
- "item_optional": false,
- "item_default": "1970-01-01T00:00:00Z",
- "item_title": "stats.BootTime",
- "item_description": "A date time when the stats module starts initially or when the stats module restarts",
- "item_format": "date-time"
- },
- {
- "item_name": "stats.start_time",
- "item_type": "string",
- "item_optional": false,
- "item_default": "1970-01-01T00:00:00Z",
- "item_title": "stats.StartTime",
- "item_description": "A date time when the stats module starts collecting data or resetting values last time",
- "item_format": "date-time"
- },
- {
- "item_name": "stats.last_update_time",
- "item_type": "string",
- "item_optional": false,
- "item_default": "1970-01-01T00:00:00Z",
- "item_title": "stats.LastUpdateTime",
- "item_description": "The latest date time when the stats module receives from other modules like auth server or boss process and so on",
- "item_format": "date-time"
- },
- {
- "item_name": "stats.timestamp",
- "item_type": "real",
- "item_optional": false,
- "item_default": 0.0,
- "item_title": "stats.Timestamp",
- "item_description": "A current time stamp since epoch time (1970-01-01T00:00:00Z)",
- "item_format": "second"
- },
- {
- "item_name": "stats.lname",
- "item_type": "string",
- "item_optional": false,
- "item_default": "",
- "item_title": "stats.LocalName",
- "item_description": "A localname of stats module given via CC protocol"
- },
- {
- "item_name": "auth.queries.tcp",
- "item_type": "integer",
- "item_optional": false,
- "item_default": 0,
- "item_title": "auth.queries.tcp",
- "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially"
- },
- {
- "item_name": "auth.queries.udp",
- "item_type": "integer",
- "item_optional": false,
- "item_default": 0,
- "item_title": "auth.queries.udp",
- "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially"
- }
- ],
- "commands": [
- {
- "command_name": "status",
- "command_description": "identify whether stats module is alive or not",
- "command_args": []
- },
- {
- "command_name": "show",
- "command_description": "show the specified/all statistics data",
- "command_args": [
- {
- "item_name": "stats_item_name",
- "item_type": "string",
- "item_optional": true,
- "item_default": ""
- }
- ]
- },
- {
- "command_name": "set",
- "command_description": "set the value of specified name in statistics data",
- "command_args": [
- {
- "item_name": "stats_data",
- "item_type": "map",
- "item_optional": false,
- "item_default": {},
- "map_item_spec": []
- }
- ]
- },
- {
- "command_name": "remove",
- "command_description": "remove the specified name from statistics data",
- "command_args": [
- {
- "item_name": "stats_item_name",
- "item_type": "string",
- "item_optional": false,
- "item_default": ""
- }
- ]
- },
- {
- "command_name": "reset",
- "command_description": "reset all statistics data to default values except for several constant names",
- "command_args": []
- },
- {
- "command_name": "shutdown",
- "command_description": "Shut down the stats module",
- "command_args": []
- }
- ]
- }
-}
diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in
new file mode 100755
index 0000000..a6fd066
--- /dev/null
+++ b/src/bin/stats/stats_httpd.py.in
@@ -0,0 +1,492 @@
+#!@PYTHON@
+
+# Copyright (C) 2011 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.
+
+"""
+A standalone HTTP server for HTTP/XML interface of statistics in BIND 10
+
+"""
+import sys; sys.path.append ('@@PYTHONPATH@@')
+import os
+import time
+import errno
+import select
+from optparse import OptionParser, OptionValueError
+import http.server
+import socket
+import string
+import xml.etree.ElementTree
+
+import isc.cc
+import isc.config
+import isc.util.process
+
+# If B10_FROM_SOURCE is set in the environment, we use data files
+# from a directory relative to that, otherwise we use the ones
+# installed on the system
+if "B10_FROM_SOURCE" in os.environ:
+ BASE_LOCATION = os.environ["B10_FROM_SOURCE"] + os.sep + \
+ "src" + os.sep + "bin" + os.sep + "stats"
+else:
+ PREFIX = "@prefix@"
+ DATAROOTDIR = "@datarootdir@"
+ BASE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@"
+ BASE_LOCATION = BASE_LOCATION.replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
+SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd.spec"
+SCHEMA_SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-schema.spec"
+XML_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xml.tpl"
+XSD_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xsd.tpl"
+XSL_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xsl.tpl"
+
+# These variables are paths part of URL.
+# eg. "http://${address}" + XXX_URL_PATH
+XML_URL_PATH = '/bind10/statistics/xml'
+XSD_URL_PATH = '/bind10/statistics/xsd'
+XSL_URL_PATH = '/bind10/statistics/xsl'
+# TODO: This should be considered later.
+XSD_NAMESPACE = 'http://bind10.isc.org' + XSD_URL_PATH
+DEFAULT_CONFIG = dict(listen_on=[('127.0.0.1', 8000)])
+
+# Assign this process name
+isc.util.process.rename()
+
+class HttpHandler(http.server.BaseHTTPRequestHandler):
+ """HTTP handler class for HttpServer class. The class inhrits the super
+ class http.server.BaseHTTPRequestHandler. It implemets do_GET()
+ and do_HEAD() and orverrides log_message()"""
+ def do_GET(self):
+ body = self.send_head()
+ if body is not None:
+ self.wfile.write(body.encode())
+
+ def do_HEAD(self):
+ self.send_head()
+
+ def send_head(self):
+ try:
+ if self.path == XML_URL_PATH:
+ body = self.server.xml_handler()
+ elif self.path == XSD_URL_PATH:
+ body = self.server.xsd_handler()
+ elif self.path == XSL_URL_PATH:
+ body = self.server.xsl_handler()
+ else:
+ if self.path == '/' and 'Host' in self.headers.keys():
+ # redirect to XML URL only when requested with '/'
+ self.send_response(302)
+ self.send_header(
+ "Location",
+ "http://" + self.headers.get('Host') + XML_URL_PATH)
+ self.end_headers()
+ return None
+ else:
+ # Couldn't find HOST
+ self.send_error(404)
+ return None
+ except StatsHttpdError as err:
+ self.send_error(500)
+ if self.server.verbose:
+ self.server.log_writer(
+ "[b10-stats-httpd] %s\n" % err)
+ return None
+ else:
+ self.send_response(200)
+ self.send_header("Content-type", "text/xml")
+ self.send_header("Content-Length", len(body))
+ self.end_headers()
+ return body
+
+ def log_message(self, format, *args):
+ """Change the default log format"""
+ if self.server.verbose:
+ self.server.log_writer(
+ "[b10-stats-httpd] %s - - [%s] %s\n" %
+ (self.address_string(),
+ self.log_date_time_string(),
+ format%args))
+
+class HttpServerError(Exception):
+ """Exception class for HttpServer class. It is intended to be
+ passed from the HttpServer object to the StatsHttpd object."""
+ pass
+
+class HttpServer(http.server.HTTPServer):
+ """HTTP Server class. The class inherits the super
+ http.server.HTTPServer. Some parameters are specified as
+ arguments, which are xml_handler, xsd_handler, xsl_handler, and
+ log_writer. These all are parameters which the StatsHttpd object
+ has. The handler parameters are references of functions which
+ return body of each document. The last parameter log_writer is
+ reference of writer function to just write to
+ sys.stderr.write. They are intended to be referred by HttpHandler
+ object."""
+ def __init__(self, server_address, handler,
+ xml_handler, xsd_handler, xsl_handler, log_writer, verbose=False):
+ self.server_address = server_address
+ self.xml_handler = xml_handler
+ self.xsd_handler = xsd_handler
+ self.xsl_handler = xsl_handler
+ self.log_writer = log_writer
+ self.verbose = verbose
+ http.server.HTTPServer.__init__(self, server_address, handler)
+
+class StatsHttpdError(Exception):
+ """Exception class for StatsHttpd class. It is intended to be
+ thrown from the the StatsHttpd object to the HttpHandler object or
+ main routine."""
+ pass
+
+class StatsHttpd:
+ """The main class of HTTP server of HTTP/XML interface for
+ statistics module. It handles HTTP requests, and command channel
+ and config channel CC session. It uses select.select function
+ while waiting for clients requests."""
+ def __init__(self, verbose=False):
+ self.verbose = verbose
+ self.running = False
+ self.poll_intval = 0.5
+ self.write_log = sys.stderr.write
+ self.mccs = None
+ self.httpd = []
+ self.open_mccs()
+ self.load_config()
+ self.load_templates()
+ self.open_httpd()
+
+ def open_mccs(self):
+ """Opens a ModuleCCSession object"""
+ # create ModuleCCSession
+ if self.verbose:
+ self.write_log("[b10-stats-httpd] Starting CC Session\n")
+ self.mccs = isc.config.ModuleCCSession(
+ SPECFILE_LOCATION, self.config_handler, self.command_handler)
+ self.cc_session = self.mccs._session
+ # read spec file of stats module and subscribe 'Stats'
+ self.stats_module_spec = isc.config.module_spec_from_file(SCHEMA_SPECFILE_LOCATION)
+ self.stats_config_spec = self.stats_module_spec.get_config_spec()
+ self.stats_module_name = self.stats_module_spec.get_module_name()
+
+ def close_mccs(self):
+ """Closes a ModuleCCSession object"""
+ if self.mccs is None:
+ return
+ if self.verbose:
+ self.write_log("[b10-stats-httpd] Closing CC Session\n")
+ self.mccs.close()
+ self.mccs = None
+
+ def load_config(self, new_config={}):
+ """Loads configuration from spec file or new configuration
+ from the config manager"""
+ # load config
+ if len(new_config) > 0:
+ self.config.update(new_config)
+ else:
+ self.config = DEFAULT_CONFIG
+ self.config.update(
+ dict([
+ (itm['item_name'], self.mccs.get_value(itm['item_name'])[0])
+ for itm in self.mccs.get_module_spec().get_config_spec()
+ ])
+ )
+ # set addresses and ports for HTTP
+ self.http_addrs = [ (cf['address'], cf['port']) for cf in self.config['listen_on'] ]
+
+ def open_httpd(self):
+ """Opens sockets for HTTP. Iterating each HTTP address to be
+ configured in spec file"""
+ for addr in self.http_addrs:
+ self.httpd.append(self._open_httpd(addr))
+
+ def _open_httpd(self, server_address, address_family=None):
+ try:
+ # try IPv6 at first
+ if address_family is not None:
+ HttpServer.address_family = address_family
+ elif socket.has_ipv6:
+ HttpServer.address_family = socket.AF_INET6
+ httpd = HttpServer(
+ server_address, HttpHandler,
+ self.xml_handler, self.xsd_handler, self.xsl_handler,
+ self.write_log, self.verbose)
+ except (socket.gaierror, socket.error,
+ OverflowError, TypeError) as err:
+ # try IPv4 next
+ if HttpServer.address_family == socket.AF_INET6:
+ httpd = self._open_httpd(server_address, socket.AF_INET)
+ else:
+ raise HttpServerError(
+ "Invalid address %s, port %s: %s: %s" %
+ (server_address[0], server_address[1],
+ err.__class__.__name__, err))
+ else:
+ if self.verbose:
+ self.write_log(
+ "[b10-stats-httpd] Started on address %s, port %s\n" %
+ server_address)
+ return httpd
+
+ def close_httpd(self):
+ """Closes sockets for HTTP"""
+ if len(self.httpd) == 0:
+ return
+ for ht in self.httpd:
+ if self.verbose:
+ self.write_log(
+ "[b10-stats-httpd] Closing address %s, port %s\n" %
+ (ht.server_address[0], ht.server_address[1])
+ )
+ ht.server_close()
+ self.httpd = []
+
+ def start(self):
+ """Starts StatsHttpd objects to run. Waiting for client
+ requests by using select.select functions"""
+ self.mccs.start()
+ self.running = True
+ while self.running:
+ try:
+ (rfd, wfd, xfd) = select.select(
+ self.get_sockets(), [], [], self.poll_intval)
+ except select.error as err:
+ # select.error exception is caught only in the case of
+ # EINTR, or in other cases it is just thrown.
+ if err.args[0] == errno.EINTR:
+ (rfd, wfd, xfd) = ([], [], [])
+ else:
+ raise
+ # FIXME: This module can handle only one request at a
+ # time. If someone sends only part of the request, we block
+ # waiting for it until we time out.
+ # But it isn't so big issue for administration purposes.
+ for fd in rfd + xfd:
+ if fd == self.mccs.get_socket():
+ self.mccs.check_command(nonblock=False)
+ continue
+ for ht in self.httpd:
+ if fd == ht.socket:
+ ht.handle_request()
+ break
+ self.stop()
+
+ def stop(self):
+ """Stops the running StatsHttpd objects. Closes CC session and
+ HTTP handling sockets"""
+ if self.verbose:
+ self.write_log("[b10-stats-httpd] Shutting down\n")
+ self.close_httpd()
+ self.close_mccs()
+
+ def get_sockets(self):
+ """Returns sockets to select.select"""
+ sockets = []
+ if self.mccs is not None:
+ sockets.append(self.mccs.get_socket())
+ if len(self.httpd) > 0:
+ for ht in self.httpd:
+ sockets.append(ht.socket)
+ return sockets
+
+ def config_handler(self, new_config):
+ """Config handler for the ModuleCCSession object. It resets
+ addresses and ports to listen HTTP requests on."""
+ if self.verbose:
+ self.write_log("[b10-stats-httpd] Loading config : %s\n" % str(new_config))
+ for key in new_config.keys():
+ if key not in DEFAULT_CONFIG:
+ if self.verbose:
+ self.write_log(
+ "[b10-stats-httpd] Unknown known config: %s" % key)
+ return isc.config.ccsession.create_answer(
+ 1, "Unknown known config: %s" % key)
+ # backup old config
+ old_config = self.config.copy()
+ self.close_httpd()
+ self.load_config(new_config)
+ try:
+ self.open_httpd()
+ except HttpServerError as err:
+ if self.verbose:
+ self.write_log("[b10-stats-httpd] %s\n" % err)
+ self.write_log("[b10-stats-httpd] Restoring old config\n")
+ # restore old config
+ self.config_handler(old_config)
+ return isc.config.ccsession.create_answer(
+ 1, "[b10-stats-httpd] %s" % err)
+ else:
+ return isc.config.ccsession.create_answer(0)
+
+ def command_handler(self, command, args):
+ """Command handler for the ModuleCCSesson object. It handles
+ "status" and "shutdown" commands."""
+ if command == "status":
+ if self.verbose:
+ self.write_log("[b10-stats-httpd] Received 'status' command\n")
+ return isc.config.ccsession.create_answer(
+ 0, "Stats Httpd is up. (PID " + str(os.getpid()) + ")")
+ elif command == "shutdown":
+ if self.verbose:
+ self.write_log("[b10-stats-httpd] Received 'shutdown' command\n")
+ self.running = False
+ return isc.config.ccsession.create_answer(
+ 0, "Stats Httpd is shutting down.")
+ else:
+ if self.verbose:
+ self.write_log("[b10-stats-httpd] Received unknown command\n")
+ return isc.config.ccsession.create_answer(
+ 1, "Unknown command: " + str(command))
+
+ def get_stats_data(self):
+ """Requests statistics data to the Stats daemon and returns
+ the data which obtains from it"""
+ try:
+ seq = self.cc_session.group_sendmsg(
+ isc.config.ccsession.create_command('show'),
+ self.stats_module_name)
+ (answer, env) = self.cc_session.group_recvmsg(False, seq)
+ if answer:
+ (rcode, value) = isc.config.ccsession.parse_answer(answer)
+ except (isc.cc.session.SessionTimeout,
+ isc.cc.session.SessionError) as err:
+ raise StatsHttpdError("%s: %s" %
+ (err.__class__.__name__, err))
+ else:
+ if rcode == 0:
+ return value
+ else:
+ raise StatsHttpdError("Stats module: %s" % str(value))
+
+ def get_stats_spec(self):
+ """Just returns spec data"""
+ return self.stats_config_spec
+
+ def load_templates(self):
+ """Setup the bodies of XSD and XSL documents to be responds to
+ HTTP clients. Before that it also creates XML tag structures by
+ using xml.etree.ElementTree.Element class and substitutes
+ concrete strings with parameters embed in the string.Template
+ object."""
+ # for XSD
+ xsd_root = xml.etree.ElementTree.Element("all") # started with "all" tag
+ for item in self.get_stats_spec():
+ element = xml.etree.ElementTree.Element(
+ "element",
+ dict( name=item["item_name"],
+ type=item["item_type"] if item["item_type"].lower() != 'real' else 'float',
+ minOccurs="1",
+ maxOccurs="1" ),
+ )
+ annotation = xml.etree.ElementTree.Element("annotation")
+ appinfo = xml.etree.ElementTree.Element("appinfo")
+ documentation = xml.etree.ElementTree.Element("documentation")
+ appinfo.text = item["item_title"]
+ documentation.text = item["item_description"]
+ annotation.append(appinfo)
+ annotation.append(documentation)
+ element.append(annotation)
+ xsd_root.append(element)
+ xsd_string = xml.etree.ElementTree.tostring(xsd_root)
+ self.xsd_body = self.open_template(XSD_TEMPLATE_LOCATION).substitute(
+ xsd_string=xsd_string,
+ xsd_namespace=XSD_NAMESPACE
+ )
+ assert self.xsd_body is not None
+
+ # for XSL
+ xsd_root = xml.etree.ElementTree.Element(
+ "xsl:template",
+ dict(match="*")) # started with xml:template tag
+ for item in self.get_stats_spec():
+ tr = xml.etree.ElementTree.Element("tr")
+ td1 = xml.etree.ElementTree.Element(
+ "td", { "class" : "title",
+ "title" : item["item_description"] })
+ td1.text = item["item_title"]
+ td2 = xml.etree.ElementTree.Element("td")
+ xsl_valueof = xml.etree.ElementTree.Element(
+ "xsl:value-of",
+ dict(select=item["item_name"]))
+ td2.append(xsl_valueof)
+ tr.append(td1)
+ tr.append(td2)
+ xsd_root.append(tr)
+ xsl_string = xml.etree.ElementTree.tostring(xsd_root)
+ self.xsl_body = self.open_template(XSL_TEMPLATE_LOCATION).substitute(
+ xsl_string=xsl_string,
+ xsd_namespace=XSD_NAMESPACE)
+ assert self.xsl_body is not None
+
+ def xml_handler(self):
+ """Handler which requests to Stats daemon to obtain statistics
+ data and returns the body of XML document"""
+ xml_list=[]
+ for (k, v) in self.get_stats_data().items():
+ (k, v) = (str(k), str(v))
+ elem = xml.etree.ElementTree.Element(k)
+ elem.text = v
+ # The coding conversion is tricky. xml..tostring() of Python 3.2
+ # returns bytes (not string) regardless of the coding, while
+ # tostring() of Python 3.1 returns a string. To support both
+ # cases transparently, we first make sure tostring() returns
+ # bytes by specifying utf-8 and then convert the result to a
+ # plain string (code below assume it).
+ xml_list.append(
+ str(xml.etree.ElementTree.tostring(elem, encoding='utf-8'),
+ encoding='us-ascii'))
+ xml_string = "".join(xml_list)
+ self.xml_body = self.open_template(XML_TEMPLATE_LOCATION).substitute(
+ xml_string=xml_string,
+ xsd_namespace=XSD_NAMESPACE,
+ xsd_url_path=XSD_URL_PATH,
+ xsl_url_path=XSL_URL_PATH)
+ assert self.xml_body is not None
+ return self.xml_body
+
+ def xsd_handler(self):
+ """Handler which just returns the body of XSD document"""
+ return self.xsd_body
+
+ def xsl_handler(self):
+ """Handler which just returns the body of XSL document"""
+ return self.xsl_body
+
+ def open_template(self, file_name):
+ """It opens a template file, and it loads all lines to a
+ string variable and returns string. Template object includes
+ the variable. Limitation of a file size isn't needed there."""
+ lines = "".join(
+ open(file_name, 'r').readlines())
+ assert lines is not None
+ return string.Template(lines)
+
+if __name__ == "__main__":
+ try:
+ parser = OptionParser()
+ parser.add_option(
+ "-v", "--verbose", dest="verbose", action="store_true",
+ help="display more about what is going on")
+ (options, args) = parser.parse_args()
+ stats_httpd = StatsHttpd(verbose=options.verbose)
+ stats_httpd.start()
+ except OptionValueError:
+ sys.exit("[b10-stats-httpd] Error parsing options")
+ except isc.cc.session.SessionError as se:
+ sys.exit("[b10-stats-httpd] Error creating module, "
+ + "is the command channel daemon running?")
+ except HttpServerError as hse:
+ sys.exit("[b10-stats-httpd] %s" % hse)
+ except KeyboardInterrupt as kie:
+ sys.exit("[b10-stats-httpd] Interrupted, exiting")
diff --git a/src/bin/stats/stats_stub.py.in b/src/bin/stats/stats_stub.py.in
deleted file mode 100755
index 4a4a641..0000000
--- a/src/bin/stats/stats_stub.py.in
+++ /dev/null
@@ -1,154 +0,0 @@
-#!@PYTHON@
-
-# Copyright (C) 2010 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.
-
-__version__ = "$Revision$"
-
-import sys; sys.path.append ('@@PYTHONPATH@@')
-import os
-import time
-from optparse import OptionParser, OptionValueError
-from isc.config.ccsession import ModuleCCSession, create_command, parse_answer, parse_command, create_answer
-from isc.cc import Session, SessionError
-from stats import get_datetime
-
-# for setproctitle
-import isc.util.process
-isc.util.process.rename()
-
-# If B10_FROM_BUILD is set in the environment, we use data files
-# from a directory relative to that, otherwise we use the ones
-# installed on the system
-if "B10_FROM_BUILD" in os.environ:
- SPECFILE_LOCATION = os.environ["B10_FROM_BUILD"] + "/src/bin/stats/stats.spec"
-else:
- PREFIX = "@prefix@"
- DATAROOTDIR = "@datarootdir@"
- SPECFILE_LOCATION = "@datadir@/@PACKAGE@/stats.spec".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
-
-class CCSessionStub:
- """
- This class is intended to behaves as a sender to Stats module. It
- creates MoudleCCSession object and send specified command.
- """
- def __init__(self, session=None, verbose=False):
- # create ModuleCCSession object
- self.verbose = verbose
- self.cc_session = ModuleCCSession(SPECFILE_LOCATION,
- self.__dummy, self.__dummy, session)
- self.module_name = self.cc_session._module_name
- self.session = self.cc_session._session
-
- def __dummy(self, *args):
- pass
-
- def send_command(self, command, args):
- """
- send command to stats module with args
- """
- cmd = create_command(command, args)
- if self.verbose:
- sys.stdout.write("[b10-stats_stub] send command : " + str(cmd) + "\n")
- seq = self.session.group_sendmsg(cmd, self.module_name)
- msg, env = self.session.group_recvmsg(False, seq) # non-blocking is False
- if self.verbose:
- sys.stdout.write("[b10-stats_stub] received env : " + str(env) + "\n")
- sys.stdout.write("[b10-stats_stub] received message : " + str(msg) + "\n")
- (ret, arg) = (None, None)
- if 'result' in msg:
- ret, arg = parse_answer(msg)
- elif 'command' in msg:
- ret, arg = parse_command(msg)
- self.session.group_reply(env, create_answer(0))
- return ret, arg, env
-
-class BossModuleStub:
- """
- This class is customized from CCSessionStub and is intended to behaves
- as a virtual Boss module to send to Stats Module.
- """
- def __init__(self, session=None, verbose=False):
- self.stub = CCSessionStub(session=session, verbose=verbose)
-
- def send_boottime(self):
- return self.stub.send_command("set", {"stats_data": {"bind10.boot_time": get_datetime()}})
-
-class AuthModuleStub:
- """
- This class is customized CCSessionStub and is intended to behaves
- as a virtual Auth module to send to Stats Module.
- """
- def __init__(self, session=None, verbose=False):
- self.stub = CCSessionStub(session=session, verbose=verbose)
- self.count = { "udp": 0, "tcp": 0 }
-
- def send_udp_query_count(self, cmd="set", cnt=0):
- """
- count up udp query count
- """
- prt = "udp"
- self.count[prt] = 1
- if cnt > 0:
- self.count[prt] = cnt
- return self.stub.send_command(cmd,
- {"stats_data":
- {"auth.queries."+prt: self.count[prt]}
- })
-
- def send_tcp_query_count(self, cmd="set", cnt=0):
- """
- set udp query count
- """
- prt = "tcp"
- self.count[prt] = self.count[prt] + 1
- if cnt > 0:
- self.count[prt] = cnt
- return self.stub.send_command(cmd,
- {"stats_data":
- {"auth.queries."+prt: self.count[prt]}
- })
-
-def main(session=None):
- try:
- parser=OptionParser()
- parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
- help="display more about what is going on")
- (options, args) = parser.parse_args()
- stub = CCSessionStub(session=session, verbose=options.verbose)
- boss = BossModuleStub(session=stub.session, verbose=options.verbose)
- auth = AuthModuleStub(session=stub.session, verbose=options.verbose)
- stub.send_command("status", None)
- boss.send_boottime()
- t_cnt=0
- u_cnt=81120
- auth.send_udp_query_count(cnt=u_cnt) # This is an example.
- while True:
- u_cnt = u_cnt + 1
- t_cnt = t_cnt + 1
- auth.send_udp_query_count(cnt=u_cnt)
- auth.send_tcp_query_count(cnt=t_cnt)
- time.sleep(1)
-
- except OptionValueError:
- sys.stderr.write("[b10-stats_stub] Error parsing options\n")
- except SessionError as se:
- sys.stderr.write("[b10-stats_stub] Error creating Stats module, "
- + "is the command channel daemon running?\n")
- except KeyboardInterrupt as kie:
- sys.stderr.write("[b10-stats_stub] Interrupted, exiting\n")
-
-if __name__ == "__main__":
- main()
diff --git a/src/bin/stats/tests/Makefile.am b/src/bin/stats/tests/Makefile.am
index 9e94050..8163c7f 100644
--- a/src/bin/stats/tests/Makefile.am
+++ b/src/bin/stats/tests/Makefile.am
@@ -1,8 +1,8 @@
-SUBDIRS = isc testdata
+SUBDIRS = isc http testdata
PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-PYTESTS = b10-stats_test.py b10-stats_stub_test.py
-EXTRA_DIST = $(PYTESTS) fake_time.py
-CLEANFILES = fake_time.pyc
+PYTESTS = b10-stats_test.py b10-stats-httpd_test.py
+EXTRA_DIST = $(PYTESTS) fake_time.py fake_socket.py fake_select.py
+CLEANFILES = fake_time.pyc fake_socket.pyc fake_select.pyc
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
@@ -14,6 +14,11 @@ endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/stats:$(abs_top_builddir)/src/bin/stats/tests \
- B10_FROM_BUILD=$(abs_top_builddir) \
+ B10_FROM_SOURCE=$(abs_top_srcdir) \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py
new file mode 100644
index 0000000..07999ea
--- /dev/null
+++ b/src/bin/stats/tests/b10-stats-httpd_test.py
@@ -0,0 +1,444 @@
+# Copyright (C) 2011 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.
+
+import unittest
+import os
+import http.server
+import string
+import fake_select
+import imp
+import sys
+import fake_socket
+
+import isc.cc
+
+import stats_httpd
+stats_httpd.socket = fake_socket
+stats_httpd.select = fake_select
+
+DUMMY_DATA = {
+ "auth.queries.tcp": 10000,
+ "auth.queries.udp": 12000,
+ "bind10.boot_time": "2011-03-04T11:59:05Z",
+ "report_time": "2011-03-04T11:59:19Z",
+ "stats.boot_time": "2011-03-04T11:59:06Z",
+ "stats.last_update_time": "2011-03-04T11:59:07Z",
+ "stats.lname": "4d70d40a_c at host",
+ "stats.start_time": "2011-03-04T11:59:06Z",
+ "stats.timestamp": 1299239959.560846
+ }
+
+def push_answer(stats_httpd):
+ stats_httpd.cc_session.group_sendmsg(
+ { 'result':
+ [ 0, DUMMY_DATA ] }, "Stats")
+
+def pull_query(stats_httpd):
+ (msg, env) = stats_httpd.cc_session.group_recvmsg()
+ if 'result' in msg:
+ (ret, arg) = isc.config.ccsession.parse_answer(msg)
+ else:
+ (ret, arg) = isc.config.ccsession.parse_command(msg)
+ return (ret, arg, env)
+
+class TestHttpHandler(unittest.TestCase):
+ """Tests for HttpHandler class"""
+
+ def setUp(self):
+ self.verbose = True
+ self.stats_httpd = stats_httpd.StatsHttpd(self.verbose)
+ self.assertTrue(type(self.stats_httpd.httpd) is list)
+ self.httpd = self.stats_httpd.httpd
+ for ht in self.httpd:
+ self.assertTrue(ht.verbose)
+ self.stats_httpd.cc_session.verbose = False
+
+ def test_do_GET(self):
+ for ht in self.httpd:
+ self._test_do_GET(ht._handler)
+
+ def _test_do_GET(self, handler):
+
+ # URL is '/bind10/statistics/xml'
+ handler.path = stats_httpd.XML_URL_PATH
+ push_answer(self.stats_httpd)
+ handler.do_GET()
+ (ret, arg, env) = pull_query(self.stats_httpd)
+ self.assertEqual(ret, "show")
+ self.assertIsNone(arg)
+ self.assertTrue('group' in env)
+ self.assertEqual(env['group'], 'Stats')
+ self.assertEqual(handler.response.code, 200)
+ self.assertEqual(handler.response.headers["Content-type"], "text/xml")
+ self.assertTrue(handler.response.headers["Content-Length"] > 0)
+ self.assertTrue(handler.response.wrote_headers)
+ self.assertTrue(handler.response.body.find(stats_httpd.XSD_NAMESPACE)>0)
+ self.assertTrue(handler.response.body.find(stats_httpd.XSD_URL_PATH)>0)
+ for (k, v) in DUMMY_DATA.items():
+ self.assertTrue(handler.response.body.find(str(k))>0)
+ self.assertTrue(handler.response.body.find(str(v))>0)
+
+ # URL is '/bind10/statitics/xsd'
+ handler.path = stats_httpd.XSD_URL_PATH
+ handler.do_GET()
+ self.assertEqual(handler.response.code, 200)
+ self.assertEqual(handler.response.headers["Content-type"], "text/xml")
+ self.assertTrue(handler.response.headers["Content-Length"] > 0)
+ self.assertTrue(handler.response.wrote_headers)
+ self.assertTrue(handler.response.body.find(stats_httpd.XSD_NAMESPACE)>0)
+ for (k, v) in DUMMY_DATA.items():
+ self.assertTrue(handler.response.body.find(str(k))>0)
+
+ # URL is '/bind10/statitics/xsl'
+ handler.path = stats_httpd.XSL_URL_PATH
+ handler.do_GET()
+ self.assertEqual(handler.response.code, 200)
+ self.assertEqual(handler.response.headers["Content-type"], "text/xml")
+ self.assertTrue(handler.response.headers["Content-Length"] > 0)
+ self.assertTrue(handler.response.wrote_headers)
+ self.assertTrue(handler.response.body.find(stats_httpd.XSD_NAMESPACE)>0)
+ for (k, v) in DUMMY_DATA.items():
+ self.assertTrue(handler.response.body.find(str(k))>0)
+
+ # 302 redirect
+ handler.path = '/'
+ handler.headers = {'Host': 'my.host.domain'}
+ handler.do_GET()
+ self.assertEqual(handler.response.code, 302)
+ self.assertEqual(handler.response.headers["Location"],
+ "http://my.host.domain%s" % stats_httpd.XML_URL_PATH)
+
+ # 404 NotFound
+ handler.path = '/path/to/foo/bar'
+ handler.headers = {}
+ handler.do_GET()
+ self.assertEqual(handler.response.code, 404)
+
+ # failure case(connection with Stats is down)
+ handler.path = stats_httpd.XML_URL_PATH
+ push_answer(self.stats_httpd)
+ self.assertFalse(self.stats_httpd.cc_session._socket._closed)
+ self.stats_httpd.cc_session._socket._closed = True
+ handler.do_GET()
+ self.stats_httpd.cc_session._socket._closed = False
+ self.assertEqual(handler.response.code, 500)
+ self.stats_httpd.cc_session._clear_queues()
+
+ # failure case(Stats module returns err)
+ handler.path = stats_httpd.XML_URL_PATH
+ self.stats_httpd.cc_session.group_sendmsg(
+ { 'result': [ 1, "I have an error." ] }, "Stats")
+ self.assertFalse(self.stats_httpd.cc_session._socket._closed)
+ self.stats_httpd.cc_session._socket._closed = False
+ handler.do_GET()
+ self.assertEqual(handler.response.code, 500)
+ self.stats_httpd.cc_session._clear_queues()
+
+ def test_do_HEAD(self):
+ for ht in self.httpd:
+ self._test_do_HEAD(ht._handler)
+
+ def _test_do_HEAD(self, handler):
+ handler.path = '/path/to/foo/bar'
+ handler.do_HEAD()
+ self.assertEqual(handler.response.code, 404)
+
+ def test_log_message(self):
+ for ht in self.httpd:
+ self._test_log_message(ht._handler)
+
+ def _test_log_message(self, handler):
+ # switch write_log function
+ handler.server.log_writer = handler.response._write_log
+ log_message = 'ABCDEFG'
+ handler.log_message("%s", log_message)
+ self.assertEqual(handler.response.log,
+ "[b10-stats-httpd] %s - - [%s] %s\n" %
+ (handler.address_string(),
+ handler.log_date_time_string(),
+ log_message))
+
+class TestHttpServerError(unittest.TestCase):
+ """Tests for HttpServerError exception"""
+
+ def test_raises(self):
+ try:
+ raise stats_httpd.HttpServerError('Nothing')
+ except stats_httpd.HttpServerError as err:
+ self.assertEqual(str(err), 'Nothing')
+
+class TestHttpServer(unittest.TestCase):
+ """Tests for HttpServer class"""
+
+ def test_httpserver(self):
+ self.verbose = True
+ self.stats_httpd = stats_httpd.StatsHttpd(self.verbose)
+ self.stats_httpd.cc_session.verbose = False
+ for ht in self.stats_httpd.httpd:
+ self.assertTrue(ht.server_address in self.stats_httpd.http_addrs)
+ self.assertEqual(ht.verbose, self.verbose)
+ self.assertEqual(ht.xml_handler, self.stats_httpd.xml_handler)
+ self.assertEqual(ht.xsd_handler, self.stats_httpd.xsd_handler)
+ self.assertEqual(ht.xsl_handler, self.stats_httpd.xsl_handler)
+ self.assertEqual(ht.log_writer, self.stats_httpd.write_log)
+ self.assertTrue(isinstance(ht._handler, stats_httpd.HttpHandler))
+ self.assertTrue(isinstance(ht.socket, fake_socket.socket))
+
+class TestStatsHttpdError(unittest.TestCase):
+ """Tests for StatsHttpdError exception"""
+
+ def test_raises(self):
+ try:
+ raise stats_httpd.StatsHttpdError('Nothing')
+ except stats_httpd.StatsHttpdError as err:
+ self.assertEqual(str(err), 'Nothing')
+
+class TestStatsHttpd(unittest.TestCase):
+ """Tests for StatsHttpd class"""
+
+ def setUp(self):
+ self.verbose = True
+ fake_socket._CLOSED = False
+ fake_socket.has_ipv6 = True
+ self.stats_httpd = stats_httpd.StatsHttpd(self.verbose)
+ self.stats_httpd.cc_session.verbose = False
+
+ def tearDown(self):
+ self.stats_httpd.stop()
+
+ def test_init(self):
+ self.assertTrue(self.stats_httpd.verbose)
+ self.assertFalse(self.stats_httpd.mccs.get_socket()._closed)
+ self.assertEqual(self.stats_httpd.mccs.get_socket().fileno(),
+ id(self.stats_httpd.mccs.get_socket()))
+ for ht in self.stats_httpd.httpd:
+ self.assertFalse(ht.socket._closed)
+ self.assertEqual(ht.socket.fileno(), id(ht.socket))
+ fake_socket._CLOSED = True
+ self.assertRaises(isc.cc.session.SessionError,
+ stats_httpd.StatsHttpd)
+ fake_socket._CLOSED = False
+
+ def test_mccs(self):
+ self.stats_httpd.open_mccs()
+ self.assertTrue(
+ isinstance(self.stats_httpd.mccs.get_socket(), fake_socket.socket))
+ self.assertTrue(
+ isinstance(self.stats_httpd.cc_session, isc.cc.session.Session))
+ self.assertTrue(
+ isinstance(self.stats_httpd.stats_module_spec, isc.config.ModuleSpec))
+ for cfg in self.stats_httpd.stats_config_spec:
+ self.assertTrue('item_name' in cfg)
+ self.assertTrue(cfg['item_name'] in DUMMY_DATA)
+ self.assertTrue(len(self.stats_httpd.stats_config_spec), len(DUMMY_DATA))
+
+ def test_load_config(self):
+ self.stats_httpd.load_config()
+ self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs))
+
+ def test_httpd(self):
+ # dual stack (addresses is ipv4 and ipv6)
+ fake_socket.has_ipv6 = True
+ self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs))
+ self.stats_httpd.http_addrs = [ ('::1', 8000), ('127.0.0.1', 8000) ]
+ self.assertTrue(
+ stats_httpd.HttpServer.address_family in set([fake_socket.AF_INET, fake_socket.AF_INET6]))
+ self.stats_httpd.open_httpd()
+ for ht in self.stats_httpd.httpd:
+ self.assertTrue(isinstance(ht.socket, fake_socket.socket))
+ self.stats_httpd.close_httpd()
+
+ # dual stack (address is ipv6)
+ fake_socket.has_ipv6 = True
+ self.stats_httpd.http_addrs = [ ('::1', 8000) ]
+ self.stats_httpd.open_httpd()
+ for ht in self.stats_httpd.httpd:
+ self.assertTrue(isinstance(ht.socket, fake_socket.socket))
+ self.stats_httpd.close_httpd()
+
+ # dual stack (address is ipv4)
+ fake_socket.has_ipv6 = True
+ self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ]
+ self.stats_httpd.open_httpd()
+ for ht in self.stats_httpd.httpd:
+ self.assertTrue(isinstance(ht.socket, fake_socket.socket))
+ self.stats_httpd.close_httpd()
+
+ # only-ipv4 single stack
+ fake_socket.has_ipv6 = False
+ self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ]
+ self.stats_httpd.open_httpd()
+ for ht in self.stats_httpd.httpd:
+ self.assertTrue(isinstance(ht.socket, fake_socket.socket))
+ self.stats_httpd.close_httpd()
+
+ # only-ipv4 single stack (force set ipv6 )
+ fake_socket.has_ipv6 = False
+ self.stats_httpd.http_addrs = [ ('::1', 8000) ]
+ self.assertRaises(stats_httpd.HttpServerError,
+ self.stats_httpd.open_httpd)
+
+ # hostname
+ self.stats_httpd.http_addrs = [ ('localhost', 8000) ]
+ self.stats_httpd.open_httpd()
+ for ht in self.stats_httpd.httpd:
+ self.assertTrue(isinstance(ht.socket, fake_socket.socket))
+ self.stats_httpd.close_httpd()
+
+ self.stats_httpd.http_addrs = [ ('my.host.domain', 8000) ]
+ self.stats_httpd.open_httpd()
+ for ht in self.stats_httpd.httpd:
+ self.assertTrue(isinstance(ht.socket, fake_socket.socket))
+ self.stats_httpd.close_httpd()
+
+ # over flow of port number
+ self.stats_httpd.http_addrs = [ ('', 80000) ]
+ self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd)
+ # negative
+ self.stats_httpd.http_addrs = [ ('', -8000) ]
+ self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd)
+ # alphabet
+ self.stats_httpd.http_addrs = [ ('', 'ABCDE') ]
+ self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd)
+
+ def test_start(self):
+ self.stats_httpd.cc_session.group_sendmsg(
+ { 'command': [ "shutdown" ] }, "StatsHttpd")
+ self.stats_httpd.start()
+ self.stats_httpd = stats_httpd.StatsHttpd(self.verbose)
+ self.stats_httpd.cc_session.verbose = False
+ self.assertRaises(
+ fake_select.error, self.stats_httpd.start)
+
+ def test_stop(self):
+ # success case
+ fake_socket._CLOSED = False
+ self.stats_httpd.stop()
+ self.assertFalse(self.stats_httpd.running)
+ self.assertIsNone(self.stats_httpd.mccs)
+ for ht in self.stats_httpd.httpd:
+ self.assertTrue(ht.socket._closed)
+ self.assertTrue(self.stats_httpd.cc_session._socket._closed)
+ # failure case
+ self.stats_httpd.cc_session._socket._closed = False
+ self.stats_httpd.open_mccs()
+ self.stats_httpd.cc_session._socket._closed = True
+ self.stats_httpd.stop() # No excetion raises
+ self.stats_httpd.cc_session._socket._closed = False
+
+ def test_open_template(self):
+ # successful conditions
+ tmpl = self.stats_httpd.open_template(stats_httpd.XML_TEMPLATE_LOCATION)
+ self.assertTrue(isinstance(tmpl, string.Template))
+ opts = dict(
+ xml_string="<dummy></dummy>",
+ xsd_namespace="http://host/path/to/",
+ xsd_url_path="/path/to/",
+ xsl_url_path="/path/to/")
+ lines = tmpl.substitute(opts)
+ for n in opts:
+ self.assertTrue(lines.find(opts[n])>0)
+ tmpl = self.stats_httpd.open_template(stats_httpd.XSD_TEMPLATE_LOCATION)
+ self.assertTrue(isinstance(tmpl, string.Template))
+ opts = dict(
+ xsd_string="<dummy></dummy>",
+ xsd_namespace="http://host/path/to/")
+ lines = tmpl.substitute(opts)
+ for n in opts:
+ self.assertTrue(lines.find(opts[n])>0)
+ tmpl = self.stats_httpd.open_template(stats_httpd.XSL_TEMPLATE_LOCATION)
+ self.assertTrue(isinstance(tmpl, string.Template))
+ opts = dict(
+ xsl_string="<dummy></dummy>",
+ xsd_namespace="http://host/path/to/")
+ lines = tmpl.substitute(opts)
+ for n in opts:
+ self.assertTrue(lines.find(opts[n])>0)
+ # unsuccessful condition
+ self.assertRaises(
+ IOError,
+ self.stats_httpd.open_template, '/path/to/foo/bar')
+
+ def test_commands(self):
+ self.assertEqual(self.stats_httpd.command_handler("status", None),
+ isc.config.ccsession.create_answer(
+ 0, "Stats Httpd is up. (PID " + str(os.getpid()) + ")"))
+ self.stats_httpd.running = True
+ self.assertEqual(self.stats_httpd.command_handler("shutdown", None),
+ isc.config.ccsession.create_answer(
+ 0, "Stats Httpd is shutting down."))
+ self.assertFalse(self.stats_httpd.running)
+ self.assertEqual(
+ self.stats_httpd.command_handler("__UNKNOWN_COMMAND__", None),
+ isc.config.ccsession.create_answer(
+ 1, "Unknown command: __UNKNOWN_COMMAND__"))
+
+ def test_config(self):
+ self.assertEqual(
+ self.stats_httpd.config_handler(dict(_UNKNOWN_KEY_=None)),
+ isc.config.ccsession.create_answer(
+ 1, "Unknown known config: _UNKNOWN_KEY_"))
+ self.assertEqual(
+ self.stats_httpd.config_handler(
+ dict(listen_on=[dict(address="::2",port=8000)])),
+ isc.config.ccsession.create_answer(0))
+ self.assertTrue("listen_on" in self.stats_httpd.config)
+ for addr in self.stats_httpd.config["listen_on"]:
+ self.assertTrue("address" in addr)
+ self.assertTrue("port" in addr)
+ self.assertTrue(addr["address"] == "::2")
+ self.assertTrue(addr["port"] == 8000)
+
+ self.assertEqual(
+ self.stats_httpd.config_handler(
+ dict(listen_on=[dict(address="::1",port=80)])),
+ isc.config.ccsession.create_answer(0))
+ self.assertTrue("listen_on" in self.stats_httpd.config)
+ for addr in self.stats_httpd.config["listen_on"]:
+ self.assertTrue("address" in addr)
+ self.assertTrue("port" in addr)
+ self.assertTrue(addr["address"] == "::1")
+ self.assertTrue(addr["port"] == 80)
+
+ self.assertEqual(
+ self.stats_httpd.config_handler(
+ dict(listen_on=[dict(address="1.2.3.4",port=54321)])),
+ isc.config.ccsession.create_answer(0))
+ self.assertTrue("listen_on" in self.stats_httpd.config)
+ for addr in self.stats_httpd.config["listen_on"]:
+ self.assertTrue("address" in addr)
+ self.assertTrue("port" in addr)
+ self.assertTrue(addr["address"] == "1.2.3.4")
+ self.assertTrue(addr["port"] == 54321)
+ (ret, arg) = isc.config.ccsession.parse_answer(
+ self.stats_httpd.config_handler(
+ dict(listen_on=[dict(address="1.2.3.4",port=543210)]))
+ )
+ self.assertEqual(ret, 1)
+
+ def test_for_without_B10_FROM_SOURCE(self):
+ # just lets it go through the code without B10_FROM_SOURCE env
+ # variable
+ if "B10_FROM_SOURCE" in os.environ:
+ tmppath = os.environ["B10_FROM_SOURCE"]
+ os.environ.pop("B10_FROM_SOURCE")
+ imp.reload(stats_httpd)
+ os.environ["B10_FROM_SOURCE"] = tmppath
+ imp.reload(stats_httpd)
+ stats_httpd.socket = fake_socket
+ stats_httpd.select = fake_select
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/src/bin/stats/tests/b10-stats_stub_test.py b/src/bin/stats/tests/b10-stats_stub_test.py
deleted file mode 100644
index b8983c5..0000000
--- a/src/bin/stats/tests/b10-stats_stub_test.py
+++ /dev/null
@@ -1,115 +0,0 @@
-# Copyright (C) 2010 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.
-
-__version__ = "$Revision$"
-
-#
-# Tests for the stats stub module
-#
-import unittest
-import time
-import os
-import imp
-import stats_stub
-from isc.cc.session import Session
-from stats_stub import CCSessionStub, BossModuleStub, AuthModuleStub
-from stats import get_datetime
-
-class TestStats(unittest.TestCase):
-
- def setUp(self):
- self.session = Session()
- self.stub = CCSessionStub(session=self.session, verbose=True)
- self.boss = BossModuleStub(session=self.session, verbose=True)
- self.auth = AuthModuleStub(session=self.session, verbose=True)
- self.env = {'from': self.session.lname, 'group': 'Stats',
- 'instance': '*', 'to':'*',
- 'type':'send','seq':0}
- self.result_ok = {'result': [0]}
-
- def tearDown(self):
- self.session.close()
-
- def test_stub(self):
- """
- Test for send_command of CCSessionStub object
- """
- env = self.env
- result_ok = self.result_ok
- self.assertEqual(('status', None, env),
- self.stub.send_command('status', None))
- self.assertEqual(result_ok, self.session.get_message("Stats", None))
- self.assertEqual(('shutdown', None, env),
- self.stub.send_command('shutdown', None))
- self.assertEqual(result_ok, self.session.get_message("Stats", None))
- self.assertEqual(('show', None, env),
- self.stub.send_command('show', None))
- self.assertEqual(result_ok, self.session.get_message("Stats", None))
- self.assertEqual(('set', {'atest': 100.0}, env),
- self.stub.send_command('set', {'atest': 100.0}))
- self.assertEqual(result_ok, self.session.get_message("Stats", None))
-
- def test_boss_stub(self):
- """
- Test for send_command of BossModuleStub object
- """
- env = self.env
- result_ok = self.result_ok
- self.assertEqual(('set', {"stats_data":
- {"bind10.boot_time": get_datetime()}
- }, env), self.boss.send_boottime())
- self.assertEqual(result_ok, self.session.get_message("Stats", None))
-
- def test_auth_stub(self):
- """
- Test for send_command of AuthModuleStub object
- """
- env = self.env
- result_ok = self.result_ok
- self.assertEqual(
- ('set', {"stats_data": {"auth.queries.udp": 1}}, env),
- self.auth.send_udp_query_count())
- self.assertEqual(result_ok, self.session.get_message("Stats", None))
- self.assertEqual(
- ('set', {"stats_data": {"auth.queries.tcp": 1}}, env),
- self.auth.send_tcp_query_count())
- self.assertEqual(result_ok, self.session.get_message("Stats", None))
- self.assertEqual(
- ('set', {"stats_data": {"auth.queries.udp": 100}}, env),
- self.auth.send_udp_query_count(cmd='set', cnt=100))
- self.assertEqual(result_ok, self.session.get_message("Stats", None))
- self.assertEqual(
- ('set', {"stats_data": {"auth.queries.tcp": 99}}, env),
- self.auth.send_tcp_query_count(cmd='set', cnt=99))
- self.assertEqual(result_ok, self.session.get_message("Stats", None))
-
- def test_func_main(self):
- # explicitly make failed
- self.session.close()
- stats_stub.main(session=self.session)
-
- def test_osenv(self):
- """
- test for not having environ "B10_FROM_BUILD"
- """
- if "B10_FROM_BUILD" in os.environ:
- path = os.environ["B10_FROM_BUILD"]
- os.environ.pop("B10_FROM_BUILD")
- imp.reload(stats_stub)
- os.environ["B10_FROM_BUILD"] = path
- imp.reload(stats_stub)
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py
index e4e1a1e..eccabdc 100644
--- a/src/bin/stats/tests/b10-stats_test.py
+++ b/src/bin/stats/tests/b10-stats_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2010 Internet Systems Consortium.
+# Copyright (C) 2010, 2011 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
@@ -13,8 +13,6 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-__version__ = "$Revision$"
-
#
# Tests for the stats module
#
@@ -25,7 +23,11 @@ import unittest
import imp
from isc.cc.session import Session, SessionError
from isc.config.ccsession import ModuleCCSession, ModuleCCSessionError
+from fake_time import time, strftime, gmtime
import stats
+stats.time = time
+stats.strftime = strftime
+stats.gmtime = gmtime
from stats import SessionSubject, CCSessionListener, get_timestamp, get_datetime
from fake_time import _TEST_TIME_SECS, _TEST_TIME_STRF
@@ -504,6 +506,13 @@ class TestStats(unittest.TestCase):
self.assertEqual(result_ok(),
self.session.get_message("Stats", None))
+ def test_for_boss(self):
+ last_queue = self.session.old_message_queue.pop()
+ self.assertEqual(
+ last_queue.msg, {'command': ['sendstats']})
+ self.assertEqual(
+ last_queue.env['group'], 'Boss')
+
class TestStats2(unittest.TestCase):
def setUp(self):
@@ -530,12 +539,19 @@ class TestStats2(unittest.TestCase):
Test for specfile
"""
- if "B10_FROM_BUILD" in os.environ:
+ if "B10_FROM_SOURCE" in os.environ:
self.assertEqual(stats.SPECFILE_LOCATION,
- os.environ["B10_FROM_BUILD"] + "/src/bin/stats/stats.spec")
+ os.environ["B10_FROM_SOURCE"] + os.sep + \
+ "src" + os.sep + "bin" + os.sep + "stats" + \
+ os.sep + "stats.spec")
+ self.assertEqual(stats.SCHEMA_SPECFILE_LOCATION,
+ os.environ["B10_FROM_SOURCE"] + os.sep + \
+ "src" + os.sep + "bin" + os.sep + "stats" + \
+ os.sep + "stats-schema.spec")
imp.reload(stats)
# change path of SPECFILE_LOCATION
stats.SPECFILE_LOCATION = TEST_SPECFILE_LOCATION
+ stats.SCHEMA_SPECFILE_LOCATION = TEST_SPECFILE_LOCATION
self.assertEqual(stats.SPECFILE_LOCATION, TEST_SPECFILE_LOCATION)
self.subject = stats.SessionSubject(session=self.session, verbose=True)
self.session = self.subject.session
@@ -626,13 +642,13 @@ class TestStats2(unittest.TestCase):
def test_osenv(self):
"""
- test for not having environ "B10_FROM_BUILD"
+ test for not having environ "B10_FROM_SOURCE"
"""
- if "B10_FROM_BUILD" in os.environ:
- path = os.environ["B10_FROM_BUILD"]
- os.environ.pop("B10_FROM_BUILD")
+ if "B10_FROM_SOURCE" in os.environ:
+ path = os.environ["B10_FROM_SOURCE"]
+ os.environ.pop("B10_FROM_SOURCE")
imp.reload(stats)
- os.environ["B10_FROM_BUILD"] = path
+ os.environ["B10_FROM_SOURCE"] = path
imp.reload(stats)
def result_ok(*args):
diff --git a/src/bin/stats/tests/fake_select.py b/src/bin/stats/tests/fake_select.py
new file mode 100644
index 0000000..ca0ca82
--- /dev/null
+++ b/src/bin/stats/tests/fake_select.py
@@ -0,0 +1,43 @@
+# Copyright (C) 2011 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.
+
+"""
+A mock-up module of select
+
+*** NOTE ***
+It is only for testing stats_httpd module and not reusable for
+external module.
+"""
+
+import fake_socket
+import errno
+
+class error(Exception):
+ pass
+
+def select(rlst, wlst, xlst, timeout):
+ if type(timeout) != int and type(timeout) != float:
+ raise TypeError("Error: %s must be integer or float"
+ % timeout.__class__.__name__)
+ for s in rlst + wlst + xlst:
+ if type(s) != fake_socket.socket:
+ raise TypeError("Error: %s must be a dummy socket"
+ % s.__class__.__name__)
+ s._called = s._called + 1
+ if s._called > 3:
+ raise error("Something is happened!")
+ elif s._called > 2:
+ raise error(errno.EINTR)
+ return (rlst, wlst, xlst)
diff --git a/src/bin/stats/tests/fake_socket.py b/src/bin/stats/tests/fake_socket.py
new file mode 100644
index 0000000..4e3a458
--- /dev/null
+++ b/src/bin/stats/tests/fake_socket.py
@@ -0,0 +1,70 @@
+# Copyright (C) 2011 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.
+
+"""
+A mock-up module of socket
+
+*** NOTE ***
+It is only for testing stats_httpd module and not reusable for
+external module.
+"""
+
+import re
+
+AF_INET = 'AF_INET'
+AF_INET6 = 'AF_INET6'
+_ADDRFAMILY = AF_INET
+has_ipv6 = True
+_CLOSED = False
+
+class gaierror(Exception):
+ pass
+
+class error(Exception):
+ pass
+
+class socket:
+
+ def __init__(self, family=None):
+ if family is None:
+ self.address_family = _ADDRFAMILY
+ else:
+ self.address_family = family
+ self._closed = _CLOSED
+ if self._closed:
+ raise error('socket is already closed!')
+ self._called = 0
+
+ def close(self):
+ self._closed = True
+
+ def fileno(self):
+ return id(self)
+
+ def bind(self, server_class):
+ (self.server_address, self.server_port) = server_class
+ if self.address_family not in set([AF_INET, AF_INET6]):
+ raise error("Address family not supported by protocol: %s" % self.address_family)
+ if self.address_family == AF_INET6 and not has_ipv6:
+ raise error("Address family not supported in this machine: %s has_ipv6: %s"
+ % (self.address_family, str(has_ipv6)))
+ if self.address_family == AF_INET and re.search(':', self.server_address) is not None:
+ raise gaierror("Address family for hostname not supported : %s %s" % (self.server_address, self.address_family))
+ if self.address_family == AF_INET6 and re.search(':', self.server_address) is None:
+ raise error("Cannot assign requested address : %s" % str(self.server_address))
+ if type(self.server_port) is not int:
+ raise TypeError("an integer is required: %s" % str(self.server_port))
+ if self.server_port < 0 or self.server_port > 65535:
+ raise OverflowError("port number must be 0-65535.: %s" % str(self.server_port))
diff --git a/src/bin/stats/tests/http/Makefile.am b/src/bin/stats/tests/http/Makefile.am
new file mode 100644
index 0000000..79263a9
--- /dev/null
+++ b/src/bin/stats/tests/http/Makefile.am
@@ -0,0 +1,6 @@
+EXTRA_DIST = __init__.py server.py
+CLEANFILES = __init__.pyc server.pyc
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/stats/tests/http/__init__.py b/src/bin/stats/tests/http/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/bin/stats/tests/http/server.py b/src/bin/stats/tests/http/server.py
new file mode 100644
index 0000000..70ed6fa
--- /dev/null
+++ b/src/bin/stats/tests/http/server.py
@@ -0,0 +1,96 @@
+# Copyright (C) 2011 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.
+
+"""
+A mock-up module of http.server
+
+*** NOTE ***
+It is only for testing stats_httpd module and not reusable for
+external module.
+"""
+
+import fake_socket
+
+class DummyHttpResponse:
+ def __init__(self, path):
+ self.path = path
+ self.headers={}
+ self.log = ""
+
+ def _write_log(self, msg):
+ self.log = self.log + msg
+
+class HTTPServer:
+ """
+ A mock-up class of http.server.HTTPServer
+ """
+ address_family = fake_socket.AF_INET
+ def __init__(self, server_class, handler_class):
+ self.socket = fake_socket.socket(self.address_family)
+ self.server_class = server_class
+ self.socket.bind(self.server_class)
+ self._handler = handler_class(None, None, self)
+
+ def handle_request(self):
+ pass
+
+ def server_close(self):
+ self.socket.close()
+
+class BaseHTTPRequestHandler:
+ """
+ A mock-up class of http.server.BaseHTTPRequestHandler
+ """
+
+ def __init__(self, request, client_address, server):
+ self.path = "/path/to"
+ self.headers = {}
+ self.server = server
+ self.response = DummyHttpResponse(path=self.path)
+ self.response.write = self._write
+ self.wfile = self.response
+
+ def send_response(self, code=0):
+ if self.path != self.response.path:
+ self.response = DummyHttpResponse(path=self.path)
+ self.response.code = code
+
+ def send_header(self, key, value):
+ if self.path != self.response.path:
+ self.response = DummyHttpResponse(path=self.path)
+ self.response.headers[key] = value
+
+ def end_headers(self):
+ if self.path != self.response.path:
+ self.response = DummyHttpResponse(path=self.path)
+ self.response.wrote_headers = True
+
+ def send_error(self, code, message=None):
+ if self.path != self.response.path:
+ self.response = DummyHttpResponse(path=self.path)
+ self.response.code = code
+ self.response.body = message
+
+ def address_string(self):
+ return 'dummyhost'
+
+ def log_date_time_string(self):
+ return '[DD/MM/YYYY HH:MI:SS]'
+
+ def _write(self, obj):
+ if self.path != self.response.path:
+ self.response = DummyHttpResponse(path=self.path)
+ self.response.body = obj.decode()
+
diff --git a/src/bin/stats/tests/isc/Makefile.am b/src/bin/stats/tests/isc/Makefile.am
index 059107a..bfad7e3 100644
--- a/src/bin/stats/tests/isc/Makefile.am
+++ b/src/bin/stats/tests/isc/Makefile.am
@@ -1,3 +1,8 @@
SUBDIRS = cc config util
EXTRA_DIST = __init__.py
CLEANFILES = __init__.pyc
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/stats/tests/isc/cc/Makefile.am b/src/bin/stats/tests/isc/cc/Makefile.am
index ccf4dde..67323b5 100644
--- a/src/bin/stats/tests/isc/cc/Makefile.am
+++ b/src/bin/stats/tests/isc/cc/Makefile.am
@@ -1,2 +1,7 @@
EXTRA_DIST = __init__.py session.py
CLEANFILES = __init__.pyc session.pyc
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/stats/tests/isc/cc/session.py b/src/bin/stats/tests/isc/cc/session.py
index 4d12adc..e16d6a9 100644
--- a/src/bin/stats/tests/isc/cc/session.py
+++ b/src/bin/stats/tests/isc/cc/session.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2010 Internet Systems Consortium.
+# Copyright (C) 2010,2011 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
@@ -13,11 +13,16 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-# This module is a mock-up class of isc.cc.session
+"""
+A mock-up module of isc.cc.session
-__version__ = "$Revision$"
+*** NOTE ***
+It is only for testing stats_httpd module and not reusable for
+external module.
+"""
import sys
+import fake_socket
# set a dummy lname
_TEST_LNAME = '123abc at xxxx'
@@ -33,12 +38,18 @@ class Queue():
class SessionError(Exception):
pass
+class SessionTimeout(Exception):
+ pass
+
class Session:
def __init__(self, socket_file=None, verbose=False):
self._lname = _TEST_LNAME
self.message_queue = []
self.old_message_queue = []
- self._socket = True
+ try:
+ self._socket = fake_socket.socket()
+ except fake_socket.error as se:
+ raise SessionError(se)
self.verbose = verbose
@property
@@ -46,13 +57,17 @@ class Session:
return self._lname
def close(self):
- self._socket = False
+ self._socket.close()
+
+ def _clear_queues(self):
+ while len(self.message_queue) > 0:
+ self.dequeue()
def _next_sequence(self, que=None):
return len(self.message_queue)
def enqueue(self, msg=None, env={}):
- if not self._socket:
+ if self._socket._closed:
raise SessionError("Session has been closed.")
seq = self._next_sequence()
env.update({"seq": 0}) # fixed here
@@ -62,12 +77,12 @@ class Session:
sys.stdout.write("[Session] enqueue: " + str(que.dump()) + "\n")
return seq
- def dequeue(self, seq=0):
- if not self._socket:
+ def dequeue(self):
+ if self._socket._closed:
raise SessionError("Session has been closed.")
que = None
try:
- que = self.message_queue.pop(seq)
+ que = self.message_queue.pop(0) # always pop at index 0
self.old_message_queue.append(que)
except IndexError:
que = Queue()
@@ -76,7 +91,7 @@ class Session:
return que
def get_queue(self, seq=None):
- if not self._socket:
+ if self._socket._closed:
raise SessionError("Session has been closed.")
if seq is None:
seq = len(self.message_queue) - 1
@@ -99,7 +114,7 @@ class Session:
"instance": instance })
def group_recvmsg(self, nonblock=True, seq=0):
- que = self.dequeue(seq)
+ que = self.dequeue()
return que.msg, que.env
def group_reply(self, routing, msg):
@@ -112,7 +127,7 @@ class Session:
"reply": routing["seq"] })
def get_message(self, group, to='*'):
- if not self._socket:
+ if self._socket._closed:
raise SessionError("Session has been closed.")
que = Queue()
for q in self.message_queue:
@@ -124,3 +139,10 @@ class Session:
sys.stdout.write("[Session] get_message: " + str(que.dump()) + "\n")
return q.msg
+ def group_subscribe(self, group, instance = "*"):
+ if self._socket._closed:
+ raise SessionError("Session has been closed.")
+
+ def group_unsubscribe(self, group, instance = "*"):
+ if self._socket._closed:
+ raise SessionError("Session has been closed.")
diff --git a/src/bin/stats/tests/isc/config/Makefile.am b/src/bin/stats/tests/isc/config/Makefile.am
index 5b0379a..ffbecda 100644
--- a/src/bin/stats/tests/isc/config/Makefile.am
+++ b/src/bin/stats/tests/isc/config/Makefile.am
@@ -1,2 +1,7 @@
EXTRA_DIST = __init__.py ccsession.py
CLEANFILES = __init__.pyc ccsession.pyc
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/stats/tests/isc/config/ccsession.py b/src/bin/stats/tests/isc/config/ccsession.py
index fc285a7..a4e9c37 100644
--- a/src/bin/stats/tests/isc/config/ccsession.py
+++ b/src/bin/stats/tests/isc/config/ccsession.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2010 Internet Systems Consortium.
+# Copyright (C) 2010,2011 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
@@ -13,16 +13,22 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-# This module is a mock-up class of isc.cc.session
+"""
+A mock-up module of isc.cc.session
-__version__ = "$Revision$"
+*** NOTE ***
+It is only for testing stats_httpd module and not reusable for
+external module.
+"""
import json
+import os
from isc.cc.session import Session
COMMAND_CONFIG_UPDATE = "config_update"
def parse_answer(msg):
+ assert 'result' in msg
try:
return msg['result'][0], msg['result'][1]
except IndexError:
@@ -35,6 +41,7 @@ def create_answer(rcode, arg = None):
return { 'result': [ rcode, arg ] }
def parse_command(msg):
+ assert 'command' in msg
try:
return msg['command'][0], msg['command'][1]
except IndexError:
@@ -47,9 +54,21 @@ def create_command(command_name, params = None):
return {"command": [command_name, params]}
def module_spec_from_file(spec_file, check = True):
- file = open(spec_file)
- module_spec = json.loads(file.read())
- return ModuleSpec(module_spec['module_spec'], check)
+ try:
+ file = open(spec_file)
+ json_str = file.read()
+ module_spec = json.loads(json_str)
+ file.close()
+ return ModuleSpec(module_spec['module_spec'], check)
+ except IOError as ioe:
+ raise ModuleSpecError("JSON read error: " + str(ioe))
+ except ValueError as ve:
+ raise ModuleSpecError("JSON parse error: " + str(ve))
+ except KeyError as err:
+ raise ModuleSpecError("Data definition has no module_spec element")
+
+class ModuleSpecError(Exception):
+ pass
class ModuleSpec:
def __init__(self, module_spec, check = True):
@@ -67,10 +86,34 @@ class ModuleSpec:
class ModuleCCSessionError(Exception):
pass
+class DataNotFoundError(Exception):
+ pass
+
class ConfigData:
def __init__(self, specification):
self.specification = specification
+ def get_value(self, identifier):
+ """Returns a tuple where the first item is the value at the
+ given identifier, and the second item is absolutely False
+ even if the value is an unset default or not. Raises an
+ DataNotFoundError if the identifier is not found in the
+ specification file.
+ *** NOTE ***
+ There are some differences from the original method. This
+ method never handles local settings like the original
+ method. But these different behaviors aren't so big issues
+ for a mock-up method of stats_httpd because stats_httpd
+ calls this method at only first."""
+ for config_map in self.get_module_spec().get_config_spec():
+ if config_map['item_name'] == identifier:
+ if 'item_default' in config_map:
+ return config_map['item_default'], False
+ raise DataNotFoundError("item_name %s is not found in the specfile" % identifier)
+
+ def get_module_spec(self):
+ return self.specification
+
class ModuleCCSession(ConfigData):
def __init__(self, spec_file_name, config_handler, command_handler, cc_session = None):
module_spec = module_spec_from_file(spec_file_name)
@@ -111,3 +154,7 @@ class ModuleCCSession(ConfigData):
def get_module_spec(self):
return self.specification
+
+ def get_socket(self):
+ return self._session._socket
+
diff --git a/src/bin/stats/tests/isc/util/Makefile.am b/src/bin/stats/tests/isc/util/Makefile.am
index b09fdee..9c74354 100644
--- a/src/bin/stats/tests/isc/util/Makefile.am
+++ b/src/bin/stats/tests/isc/util/Makefile.am
@@ -1,2 +1,7 @@
EXTRA_DIST = __init__.py process.py
CLEANFILES = __init__.pyc process.pyc
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/stats/tests/isc/util/process.py b/src/bin/stats/tests/isc/util/process.py
index 7274a48..0f764c1 100644
--- a/src/bin/stats/tests/isc/util/process.py
+++ b/src/bin/stats/tests/isc/util/process.py
@@ -13,6 +13,9 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-# A dummy function of isc.util.process.rename()
+"""
+A dummy function of isc.util.process.rename()
+"""
+
def rename(name=None):
pass
diff --git a/src/bin/stats/tests/isc/utils/Makefile.am b/src/bin/stats/tests/isc/utils/Makefile.am
deleted file mode 100644
index b09fdee..0000000
--- a/src/bin/stats/tests/isc/utils/Makefile.am
+++ /dev/null
@@ -1,2 +0,0 @@
-EXTRA_DIST = __init__.py process.py
-CLEANFILES = __init__.pyc process.pyc
diff --git a/src/bin/stats/tests/isc/utils/__init__.py b/src/bin/stats/tests/isc/utils/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/src/bin/stats/tests/isc/utils/process.py b/src/bin/stats/tests/isc/utils/process.py
deleted file mode 100644
index 4c0cf8c..0000000
--- a/src/bin/stats/tests/isc/utils/process.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (C) 2010 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.
-
-# A dummy function of isc.utils.process.rename()
-def rename(name=None):
- pass
diff --git a/src/bin/stats/tests/stats_test.in b/src/bin/stats/tests/stats_test.in
deleted file mode 100644
index a3279a7..0000000
--- a/src/bin/stats/tests/stats_test.in
+++ /dev/null
@@ -1,31 +0,0 @@
-#! /bin/sh
-
-# Copyright (C) 2010 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.
-
-PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
-export PYTHON_EXEC
-
-PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_srcdir@/src/bin/stats:@abs_top_srcdir@/src/bin/stats/tests
-export PYTHONPATH
-
-B10_FROM_BUILD=@abs_top_builddir@
-export B10_FROM_BUILD
-
-TEST_PATH=@abs_top_srcdir@/src/bin/stats/tests
-
-cd ${TEST_PATH}
-${PYTHON_EXEC} -O b10-stats_test.py $*
-${PYTHON_EXEC} -O b10-stats_stub_test.py $*
diff --git a/src/bin/tests/Makefile.am b/src/bin/tests/Makefile.am
index 4340c64..b5bcea2 100644
--- a/src/bin/tests/Makefile.am
+++ b/src/bin/tests/Makefile.am
@@ -3,6 +3,13 @@ PYTESTS = process_rename_test.py
# .py will be generated by configure, so we don't have to include it
# in EXTRA_DIST.
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
if ENABLE_PYTHON_COVERAGE
@@ -12,6 +19,7 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
+ $(LIBRARY_PATH_PLACEHOLDER) \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
$(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
done
diff --git a/src/bin/tests/process_rename_test.py.in b/src/bin/tests/process_rename_test.py.in
index 81ea085..4b45210 100644
--- a/src/bin/tests/process_rename_test.py.in
+++ b/src/bin/tests/process_rename_test.py.in
@@ -1,4 +1,4 @@
-# Copyright (C) 2010 CZ NIC
+# Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
diff --git a/src/bin/xfrin/Makefile.am b/src/bin/xfrin/Makefile.am
index ee8505e..0af9be6 100644
--- a/src/bin/xfrin/Makefile.am
+++ b/src/bin/xfrin/Makefile.am
@@ -6,12 +6,13 @@ pkglibexec_SCRIPTS = b10-xfrin
b10_xfrindir = $(pkgdatadir)
b10_xfrin_DATA = xfrin.spec
+pyexec_DATA = xfrin_messages.py
-CLEANFILES = b10-xfrin xfrin.pyc
+CLEANFILES = b10-xfrin xfrin.pyc xfrinlog.py xfrin_messages.py xfrin_messages.pyc
man_MANS = b10-xfrin.8
EXTRA_DIST = $(man_MANS) b10-xfrin.xml
-EXTRA_DIST += xfrin.spec
+EXTRA_DIST += xfrin.spec xfrin_messages.mes
if ENABLE_MAN
@@ -20,8 +21,17 @@ b10-xfrin.8: b10-xfrin.xml
endif
+# Define rule to build logging source files from message file
+xfrin_messages.py: xfrin_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/xfrin/xfrin_messages.mes
+
# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-b10-xfrin: xfrin.py
+b10-xfrin: xfrin.py xfrin_messages.py
$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
-e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" xfrin.py >$@
chmod a+x $@
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/xfrin/b10-xfrin.8 b/src/bin/xfrin/b10-xfrin.8
index d0723b5..3ea2293 100644
--- a/src/bin/xfrin/b10-xfrin.8
+++ b/src/bin/xfrin/b10-xfrin.8
@@ -2,12 +2,12 @@
.\" Title: b10-xfrin
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: September 8, 2010
+.\" Date: May 19, 2011
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "B10\-XFRIN" "8" "September 8, 2010" "BIND10" "BIND10"
+.TH "B10\-XFRIN" "8" "May 19, 2011" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -43,7 +43,7 @@ boss process\&. When triggered it can request and receive a zone transfer and st
.ps -1
.br
.sp
-The Y1 prototype release only supports AXFR\&. IXFR is not implemented\&.
+This prototype release only supports AXFR\&. IXFR is not implemented\&.
.sp .5v
.RE
.PP
@@ -61,15 +61,34 @@ receives its configurations from
.PP
The configurable settings are:
.PP
-\fImaster_addr\fR
-The default is 127\&.0\&.0\&.1\&.
-.PP
-\fImaster_port\fR
-The default is 53\&.
-.PP
\fItransfers\-in\fR
defines the maximum number of inbound zone transfers that can run concurrently\&. The default is 10\&.
.PP
+
+\fIzones\fR
+is a list of zones known to the
+\fBb10\-xfrin\fR
+daemon\&. The list items are:
+\fIname\fR
+(the zone name),
+\fImaster_addr\fR
+(the zone master to transfer from),
+\fImaster_port\fR
+(defaults to 53), and
+\fItsig_key\fR
+(optional TSIG key to use)\&. The
+\fItsig_key\fR
+is specified using a full string colon\-delimited name:key:algorithm representation (e\&.g\&.
+\(lqfoo\&.example\&.org:EvABsfU2h7uofnmqaRCrhHunGsd=:hmac\-sha1\(rq)\&.
+.PP
+(The site\-wide
+\fImaster_addr\fR
+and
+\fImaster_port\fR
+configurations are deprecated; use the
+\fIzones\fR
+list configuration instead\&.)
+.PP
The configuration commands are:
.PP
@@ -106,7 +125,9 @@ to define the class (defaults to
\fImaster\fR
to define the IP address of the authoritative server to transfer from, and
\fIport\fR
-to define the port number on the authoritative server (defaults to 53)\&.
+to define the port number on the authoritative server (defaults to 53)\&. If the address or port is not specified, it will use the values previously defined in the
+\fIzones\fR
+configuration\&.
.PP
\fBshutdown\fR
@@ -143,5 +164,5 @@ The
daemon was implemented in March 2010 by Zhang Likun of CNNIC for the ISC BIND 10 project\&.
.SH "COPYRIGHT"
.br
-Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2011 Internet Systems Consortium, Inc. ("ISC")
.br
diff --git a/src/bin/xfrin/b10-xfrin.xml b/src/bin/xfrin/b10-xfrin.xml
index fdfe1ef..ea4c724 100644
--- a/src/bin/xfrin/b10-xfrin.xml
+++ b/src/bin/xfrin/b10-xfrin.xml
@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "—">]>
<!--
- - Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-2011 Internet Systems Consortium, Inc. ("ISC")
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>September 8, 2010</date>
+ <date>May 19, 2011</date>
</refentryinfo>
<refmeta>
@@ -36,7 +36,7 @@
<docinfo>
<copyright>
- <year>2010</year>
+ <year>2010-2011</year>
<holder>Internet Systems Consortium, Inc. ("ISC")</holder>
</copyright>
</docinfo>
@@ -62,6 +62,12 @@
the zone in a BIND 10 zone data store.
</para>
+<!-- TODO:
+xfrin only does the transfer to make it as simple as possible.
+The logic for handling transfer triggers or zone management is handled
+in separate zonemgr process.
+-->
+
<note><simpara>
This prototype release only supports AXFR. IXFR is not implemented.
</simpara></note>
@@ -86,20 +92,33 @@
The configurable settings are:
</para>
- <para><varname>master_addr</varname>
-<!-- TODO: how can there be a single setting for this? -->
- The default is 127.0.0.1.
+ <para><varname>transfers-in</varname>
+ defines the maximum number of inbound zone transfers
+ that can run concurrently. The default is 10.
</para>
- <para><varname>master_port</varname>
-<!-- TODO: what if custom is needed per zone? -->
- The default is 53.
+<!-- TODO: is name okay for master_addr or just IP? -->
+ <para>
+ <varname>zones</varname> is a list of zones known to the
+ <command>b10-xfrin</command> daemon.
+ The list items are:
+ <varname>name</varname> (the zone name),
+ <varname>master_addr</varname> (the zone master to transfer from),
+ <varname>master_port</varname> (defaults to 53), and
+ <varname>tsig_key</varname> (optional TSIG key to use).
+ The <varname>tsig_key</varname> is specified using a full string
+ colon-delimited name:key:algorithm representation (e.g.
+ <quote>foo.example.org:EvABsfU2h7uofnmqaRCrhHunGsd=:hmac-sha1</quote>).
</para>
+<!-- TODO: document this better -->
+<!-- TODO: the tsig_key format may change -->
- <para><varname>transfers-in</varname>
- defines the maximum number of inbound zone transfers
- that can run concurrently. The default is 10.
+ <para>
+ (The site-wide <varname>master_addr</varname> and
+ <varname>master_port</varname> configurations are deprecated;
+ use the <varname>zones</varname> list configuration instead.)
</para>
+<!-- NOTE: also tsig_key but not mentioning since so short lived. -->
<!-- TODO: formating -->
<para>
@@ -148,6 +167,9 @@
the authoritative server to transfer from,
and <varname>port</varname> to define the port number on the
authoritative server (defaults to 53).
+ If the address or port is not specified, it will use the
+ values previously defined in the <varname>zones</varname>
+ configuration.
</para>
<!-- TODO: later hostname for master? -->
diff --git a/src/bin/xfrin/tests/Makefile.am b/src/bin/xfrin/tests/Makefile.am
index 755d76e..0f485aa 100644
--- a/src/bin/xfrin/tests/Makefile.am
+++ b/src/bin/xfrin/tests/Makefile.am
@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
# required by loadable python modules.
LIBRARY_PATH_PLACEHOLDER =
if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
endif
# test using command-line arguments, so use check-local target instead of TESTS
@@ -18,7 +18,7 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
- env PYTHONPATH=$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/bin/xfrin:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
$(LIBRARY_PATH_PLACEHOLDER) \
+ env PYTHONPATH=$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/bin/xfrin:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index 04d04a6..2acd9d6 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 Internet Systems Consortium.
+# Copyright (C) 2009-2011 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
@@ -15,13 +15,17 @@
import unittest
import socket
+import io
+from isc.testutils.tsigctx_mock import MockTSIGContext
from xfrin import *
#
# Commonly used (mostly constant) test parameters
#
-TEST_ZONE_NAME = "example.com"
+TEST_ZONE_NAME_STR = "example.com."
+TEST_ZONE_NAME = Name(TEST_ZONE_NAME_STR)
TEST_RRCLASS = RRClass.IN()
+TEST_RRCLASS_STR = 'IN'
TEST_DB_FILE = 'db_file'
TEST_MASTER_IPV4_ADDRESS = '127.0.0.1'
TEST_MASTER_IPV4_ADDRINFO = (socket.AF_INET, socket.SOCK_STREAM,
@@ -35,15 +39,17 @@ TEST_MASTER_IPV6_ADDRINFO = (socket.AF_INET6, socket.SOCK_STREAM,
# If some other process uses this port test will fail.
TEST_MASTER_PORT = '53535'
+TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==")
+
soa_rdata = Rdata(RRType.SOA(), TEST_RRCLASS,
'master.example.com. admin.example.com ' +
'1234 3600 1800 2419200 7200')
-soa_rrset = RRset(Name(TEST_ZONE_NAME), TEST_RRCLASS, RRType.SOA(),
+soa_rrset = RRset(TEST_ZONE_NAME, TEST_RRCLASS, RRType.SOA(),
RRTTL(3600))
soa_rrset.add_rdata(soa_rdata)
-example_axfr_question = Question(Name(TEST_ZONE_NAME), TEST_RRCLASS,
+example_axfr_question = Question(TEST_ZONE_NAME, TEST_RRCLASS,
RRType.AXFR())
-example_soa_question = Question(Name(TEST_ZONE_NAME), TEST_RRCLASS,
+example_soa_question = Question(TEST_ZONE_NAME, TEST_RRCLASS,
RRType.SOA())
default_questions = [example_axfr_question]
default_answers = [soa_rrset]
@@ -51,6 +57,13 @@ default_answers = [soa_rrset]
class XfrinTestException(Exception):
pass
+class MockCC():
+ def get_default_value(self, identifier):
+ if identifier == "zones/master_port":
+ return TEST_MASTER_PORT
+ if identifier == "zones/class":
+ return TEST_RRCLASS_STR
+
class MockXfrin(Xfrin):
# This is a class attribute of a callable object that specifies a non
# default behavior triggered in _cc_check_command(). Specific test methods
@@ -60,16 +73,28 @@ class MockXfrin(Xfrin):
check_command_hook = None
def _cc_setup(self):
+ self._tsig_key = None
+ self._module_cc = MockCC()
pass
def _get_db_file(self):
pass
-
+
def _cc_check_command(self):
self._shutdown_event.set()
if MockXfrin.check_command_hook:
MockXfrin.check_command_hook()
+ def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo,
+ tsig_key, check_soa=True):
+ # store some of the arguments for verification, then call this
+ # method in the superclass
+ self.xfrin_started_master_addr = master_addrinfo[2][0]
+ self.xfrin_started_master_port = master_addrinfo[2][1]
+ return Xfrin.xfrin_start(self, zone_name, rrclass, db_file,
+ master_addrinfo, tsig_key,
+ check_soa)
+
class MockXfrinConnection(XfrinConnection):
def __init__(self, sock_map, zone_name, rrclass, db_file, shutdown_event,
master_addr):
@@ -121,10 +146,11 @@ class MockXfrinConnection(XfrinConnection):
self.response_generator()
return len(data)
- def create_response_data(self, response = True, bad_qid = False,
- rcode = Rcode.NOERROR(),
- questions = default_questions,
- answers = default_answers):
+ def create_response_data(self, response=True, bad_qid=False,
+ rcode=Rcode.NOERROR(),
+ questions=default_questions,
+ answers=default_answers,
+ tsig_ctx=None):
resp = Message(Message.RENDER)
qid = self.qid
if bad_qid:
@@ -138,7 +164,10 @@ class MockXfrinConnection(XfrinConnection):
[resp.add_rrset(Message.SECTION_ANSWER, a) for a in answers]
renderer = MessageRenderer()
- resp.to_wire(renderer)
+ if tsig_ctx is not None:
+ resp.to_wire(renderer, tsig_ctx)
+ else:
+ resp.to_wire(renderer)
reply_data = struct.pack('H', socket.htons(renderer.get_length()))
reply_data += renderer.get_data()
@@ -153,20 +182,44 @@ class TestXfrinConnection(unittest.TestCase):
TEST_RRCLASS, TEST_DB_FILE,
threading.Event(),
TEST_MASTER_IPV4_ADDRINFO)
- self.axfr_after_soa = False
self.soa_response_params = {
'questions': [example_soa_question],
'bad_qid': False,
'response': True,
'rcode': Rcode.NOERROR(),
+ 'tsig': False,
'axfr_after_soa': self._create_normal_response_data
}
+ self.axfr_response_params = {
+ 'tsig_1st': None,
+ 'tsig_2nd': None
+ }
def tearDown(self):
self.conn.close()
if os.path.exists(TEST_DB_FILE):
os.remove(TEST_DB_FILE)
+ def __create_mock_tsig(self, key, error):
+ # This helper function creates a MockTSIGContext for a given key
+ # and TSIG error to be used as a result of verify (normally faked
+ # one)
+ mock_ctx = MockTSIGContext(key)
+ mock_ctx.error = error
+ return mock_ctx
+
+ def __match_exception(self, expected_exception, expected_msg, expression):
+ # This helper method is a higher-granularity version of assertRaises().
+ # If it's not sufficient to check the exception class (e.g., when
+ # the same type of exceptions can be thrown from many places), this
+ # method can be used to check it with the exception argument.
+ try:
+ expression()
+ except expected_exception as ex:
+ self.assertEqual(str(ex), expected_msg)
+ else:
+ self.assertFalse('exception is expected, but not raised')
+
def test_close(self):
# we shouldn't be using the global asyncore map.
self.assertEqual(len(asyncore.socket_map), 0)
@@ -196,10 +249,53 @@ class TestXfrinConnection(unittest.TestCase):
RRClass.CH())
c.close()
+ def test_send_query(self):
+ def create_msg(query_type):
+ msg = Message(Message.RENDER)
+ query_id = 0x1035
+ msg.set_qid(query_id)
+ msg.set_opcode(Opcode.QUERY())
+ msg.set_rcode(Rcode.NOERROR())
+ query_question = Question(Name("example.com."), RRClass.IN(), query_type)
+ msg.add_question(query_question)
+ return msg
+
+ def message_has_tsig(data):
+ # a simple check if the actual data contains a TSIG RR.
+ # At our level this simple check should suffice; other detailed
+ # tests regarding the TSIG protocol are done in pydnspp.
+ msg = Message(Message.PARSE)
+ msg.from_wire(data)
+ return msg.get_tsig_record() is not None
+
+ self.conn._create_query = create_msg
+ # soa request
+ self.conn._send_query(RRType.SOA())
+ self.assertEqual(self.conn.query_data, b'\x00\x1d\x105\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\x06\x00\x01')
+ # axfr request
+ self.conn._send_query(RRType.AXFR())
+ self.assertEqual(self.conn.query_data, b'\x00\x1d\x105\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01')
+
+ # soa request with tsig
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._send_query(RRType.SOA())
+ self.assertTrue(message_has_tsig(self.conn.query_data[2:]))
+
+ # axfr request with tsig
+ self.conn._send_query(RRType.AXFR())
+ self.assertTrue(message_has_tsig(self.conn.query_data[2:]))
+
def test_response_with_invalid_msg(self):
self.conn.reply_data = b'aaaxxxx'
self.assertRaises(XfrinTestException, self._handle_xfrin_response)
+ def test_response_with_tsigfail(self):
+ self.conn._tsig_key = TSIG_KEY
+ # server tsig check fail, return with RCODE 9 (NOTAUTH)
+ self.conn._send_query(RRType.SOA())
+ self.conn.reply_data = self.conn.create_response_data(rcode=Rcode.NOTAUTH())
+ self.assertRaises(XfrinException, self._handle_xfrin_response)
+
def test_response_without_end_soa(self):
self.conn._send_query(RRType.AXFR())
self.conn.reply_data = self.conn.create_response_data()
@@ -210,6 +306,31 @@ class TestXfrinConnection(unittest.TestCase):
self.conn.reply_data = self.conn.create_response_data(bad_qid = True)
self.assertRaises(XfrinException, self._handle_xfrin_response)
+ def test_response_error_code_bad_sig(self):
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, TSIGError.BAD_SIG)
+ self.conn._send_query(RRType.AXFR())
+ self.conn.reply_data = self.conn.create_response_data(
+ rcode=Rcode.SERVFAIL())
+ # xfrin should check TSIG before other part of incoming message
+ # validate log message for XfrinException
+ self.__match_exception(XfrinException,
+ "TSIG verify fail: BADSIG",
+ self._handle_xfrin_response)
+
+ def test_response_bad_qid_bad_key(self):
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, TSIGError.BAD_KEY)
+ self.conn._send_query(RRType.AXFR())
+ self.conn.reply_data = self.conn.create_response_data(bad_qid=True)
+ # xfrin should check TSIG before other part of incoming message
+ # validate log message for XfrinException
+ self.__match_exception(XfrinException,
+ "TSIG verify fail: BADKEY",
+ self._handle_xfrin_response)
+
def test_response_non_response(self):
self.conn._send_query(RRType.AXFR())
self.conn.reply_data = self.conn.create_response_data(response = False)
@@ -254,6 +375,18 @@ class TestXfrinConnection(unittest.TestCase):
self.conn.response_generator = self._create_soa_response_data
self.assertRaises(XfrinException, self.conn._check_soa_serial)
+ def test_soacheck_bad_qid_bad_sig(self):
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, TSIGError.BAD_SIG)
+ self.soa_response_params['bad_qid'] = True
+ self.conn.response_generator = self._create_soa_response_data
+ # xfrin should check TSIG before other part of incoming message
+ # validate log message for XfrinException
+ self.__match_exception(XfrinException,
+ "TSIG verify fail: BADSIG",
+ self.conn._check_soa_serial)
+
def test_soacheck_non_response(self):
self.soa_response_params['response'] = False
self.conn.response_generator = self._create_soa_response_data
@@ -264,6 +397,54 @@ class TestXfrinConnection(unittest.TestCase):
self.conn.response_generator = self._create_soa_response_data
self.assertRaises(XfrinException, self.conn._check_soa_serial)
+ def test_soacheck_with_tsig(self):
+ # Use a mock tsig context emulating a validly signed response
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, TSIGError.NOERROR)
+ self.conn.response_generator = self._create_soa_response_data
+ self.assertEqual(self.conn._check_soa_serial(), XFRIN_OK)
+ self.assertEqual(self.conn._tsig_ctx.get_error(), TSIGError.NOERROR)
+
+ def test_soacheck_with_tsig_notauth(self):
+ # emulate a valid error response
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, TSIGError.BAD_SIG)
+ self.soa_response_params['rcode'] = Rcode.NOTAUTH()
+ self.conn.response_generator = self._create_soa_response_data
+
+ self.assertRaises(XfrinException, self.conn._check_soa_serial)
+
+ def test_soacheck_with_tsig_noerror_badsig(self):
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, TSIGError.BAD_SIG)
+
+ # emulate a normal response bad verification failure due to BADSIG.
+ # According RFC2845, in this case we should ignore it and keep
+ # waiting for a valid response until a timeout. But we immediately
+ # treat this as a final failure (just as BIND 9 does).
+ self.conn.response_generator = self._create_soa_response_data
+
+ self.assertRaises(XfrinException, self.conn._check_soa_serial)
+
+ def test_soacheck_with_tsig_unsigned_response(self):
+ # we can use a real TSIGContext for this. the response doesn't
+ # contain a TSIG while we sent a signed query. RFC2845 states
+ # we should wait for a valid response in this case, but we treat
+ # it as a fatal transaction failure, too.
+ self.conn._tsig_key = TSIG_KEY
+ self.conn.response_generator = self._create_soa_response_data
+ self.assertRaises(XfrinException, self.conn._check_soa_serial)
+
+ def test_soacheck_with_unexpected_tsig_response(self):
+ # we reject unexpected TSIG in responses (following BIND 9's
+ # behavior)
+ self.soa_response_params['tsig'] = True
+ self.conn.response_generator = self._create_soa_response_data
+ self.assertRaises(XfrinException, self.conn._check_soa_serial)
+
def test_response_shutdown(self):
self.conn.response_generator = self._create_normal_response_data
self.conn._shutdown_event.set()
@@ -297,6 +478,88 @@ class TestXfrinConnection(unittest.TestCase):
self.conn.response_generator = self._create_normal_response_data
self.assertEqual(self.conn.do_xfrin(False), XFRIN_OK)
+ def test_do_xfrin_with_tsig(self):
+ # use TSIG with a mock context. we fake all verify results to
+ # emulate successful verification.
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, TSIGError.NOERROR)
+ self.conn.response_generator = self._create_normal_response_data
+ self.assertEqual(self.conn.do_xfrin(False), XFRIN_OK)
+ # We use two messages in the tests. The same context should have been
+ # usef for both.
+ self.assertEqual(2, self.conn._tsig_ctx.verify_called)
+
+ def test_do_xfrin_with_tsig_fail(self):
+ # TSIG verify will fail for the first message. xfrin should fail
+ # immediately.
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, TSIGError.BAD_SIG)
+ self.conn.response_generator = self._create_normal_response_data
+ self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+ self.assertEqual(1, self.conn._tsig_ctx.verify_called)
+
+ def test_do_xfrin_with_tsig_fail_for_second_message(self):
+ # Similar to the previous test, but first verify succeeds. There
+ # should be a second verify attempt, which will fail, which should
+ # make xfrin fail.
+ def fake_tsig_error(ctx):
+ if self.conn._tsig_ctx.verify_called == 1:
+ return TSIGError.NOERROR
+ return TSIGError.BAD_SIG
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, fake_tsig_error)
+ self.conn.response_generator = self._create_normal_response_data
+ self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+ self.assertEqual(2, self.conn._tsig_ctx.verify_called)
+
+ def test_do_xfrin_with_missing_tsig(self):
+ # XFR request sent with TSIG, but the response doesn't have TSIG.
+ # xfr should fail.
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, None)
+ self.conn._tsig_ctx = MockTSIGContext(TSIG_KEY)
+ self.conn.response_generator = self._create_normal_response_data
+ self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+ self.assertEqual(1, self.conn._tsig_ctx.verify_called)
+
+ def test_do_xfrin_with_missing_tsig_for_second_message(self):
+ # Similar to the previous test, but firt one contains TSIG and verify
+ # succeeds (due to fake). The second message lacks TSIG.
+ #
+ # Note: this test case is actually not that trivial: Skipping
+ # intermediate TSIG is allowed. In this case, however, the second
+ # message is the last one, which must contain TSIG anyway, so the
+ # expected result is correct. If/when we support skipping
+ # intermediate TSIGs, we'll need additional test cases.
+ def fake_tsig_error(ctx):
+ if self.conn._tsig_ctx.verify_called == 1:
+ return TSIGError.NOERROR
+ return TSIGError.FORMERR
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, fake_tsig_error)
+ self.conn.response_generator = self._create_normal_response_data
+ self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+ self.assertEqual(2, self.conn._tsig_ctx.verify_called)
+
+ def test_do_xfrin_with_unexpected_tsig(self):
+ # XFR request wasn't signed, but response includes TSIG. Like BIND 9,
+ # we reject that.
+ self.axfr_response_params['tsig_1st'] = TSIGContext(TSIG_KEY)
+ self.conn.response_generator = self._create_normal_response_data
+ self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+
+ def test_do_xfrin_with_unexpected_tsig_for_second_message(self):
+ # similar to the previous test, but the first message is normal.
+ # the second one contains an unexpected TSIG. should be rejected.
+ self.axfr_response_params['tsig_2nd'] = TSIGContext(TSIG_KEY)
+ self.conn.response_generator = self._create_normal_response_data
+ self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+
def test_do_xfrin_empty_response(self):
# skipping the creation of response data, so the transfer will fail.
self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
@@ -315,6 +578,23 @@ class TestXfrinConnection(unittest.TestCase):
self.conn.response_generator = self._create_soa_response_data
self.assertEqual(self.conn.do_xfrin(True), XFRIN_OK)
+ def test_do_soacheck_and_xfrin_with_tsig(self):
+ # We are going to have a SOA query/response transaction, followed by
+ # AXFR, all TSIG signed. xfrin should use a new TSIG context for
+ # AXFR. We are not interested in whether verify works correctly in
+ # this test, so we simply fake the results (they need to succeed for
+ # this test)
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, TSIGError.NOERROR)
+ self.soa_response_params['tsig'] = True
+ self.conn.response_generator = self._create_soa_response_data
+ self.assertEqual(self.conn.do_xfrin(True), XFRIN_OK)
+ # We should've got 3 response messages: 1 SOA and two AXFR, but
+ # the context should be replaced for AXFR, so verify() should be
+ # called only twice for the latest context.
+ self.assertEqual(2, self.conn._tsig_ctx.verify_called)
+
def test_do_soacheck_broken_response(self):
self.conn.response_generator = self._create_broken_response_data
# XXX: TODO: this test failed here, should xfr not raise an
@@ -342,21 +622,39 @@ class TestXfrinConnection(unittest.TestCase):
# This helper method creates a simple sequence of DNS messages that
# forms a valid XFR transaction. It consists of two messages, each
# containing just a single SOA RR.
- self.conn.reply_data = self.conn.create_response_data()
- self.conn.reply_data += self.conn.create_response_data()
+ tsig_1st = self.axfr_response_params['tsig_1st']
+ tsig_2nd = self.axfr_response_params['tsig_2nd']
+ self.conn.reply_data = self.conn.create_response_data(tsig_ctx=tsig_1st)
+ self.conn.reply_data += \
+ self.conn.create_response_data(tsig_ctx=tsig_2nd)
def _create_soa_response_data(self):
# This helper method creates a DNS message that is supposed to be
# used a valid response to SOA queries prior to XFR.
+ # If tsig is True, it tries to verify the query with a locally
+ # created TSIG context (which may or may not succeed) so that the
+ # response will include a TSIG.
# If axfr_after_soa is True, it resets the response_generator so that
# a valid XFR messages will follow.
+
+ verify_ctx = None
+ if self.soa_response_params['tsig']:
+ # xfrin (curreently) always uses TCP. strip off the length field.
+ query_data = self.conn.query_data[2:]
+ query_message = Message(Message.PARSE)
+ query_message.from_wire(query_data)
+ verify_ctx = TSIGContext(TSIG_KEY)
+ verify_ctx.verify(query_message.get_tsig_record(), query_data)
+
self.conn.reply_data = self.conn.create_response_data(
bad_qid=self.soa_response_params['bad_qid'],
response=self.soa_response_params['response'],
rcode=self.soa_response_params['rcode'],
- questions=self.soa_response_params['questions'])
+ questions=self.soa_response_params['questions'],
+ tsig_ctx=verify_ctx)
if self.soa_response_params['axfr_after_soa'] != None:
- self.conn.response_generator = self.soa_response_params['axfr_after_soa']
+ self.conn.response_generator = \
+ self.soa_response_params['axfr_after_soa']
def _create_broken_response_data(self):
# This helper method creates a bogus "DNS message" that only contains
@@ -399,21 +697,28 @@ class TestXfrinRecorder(unittest.TestCase):
class TestXfrin(unittest.TestCase):
def setUp(self):
+ # redirect output
+ self.stderr_backup = sys.stderr
+ sys.stderr = open(os.devnull, 'w')
self.xfr = MockXfrin()
self.args = {}
- self.args['zone_name'] = TEST_ZONE_NAME
+ self.args['zone_name'] = TEST_ZONE_NAME_STR
+ self.args['class'] = TEST_RRCLASS_STR
self.args['port'] = TEST_MASTER_PORT
self.args['master'] = TEST_MASTER_IPV4_ADDRESS
self.args['db_file'] = TEST_DB_FILE
+ self.args['tsig_key'] = ''
def tearDown(self):
self.xfr.shutdown()
+ sys.stderr= self.stderr_backup
def _do_parse_zone_name_class(self):
return self.xfr._parse_zone_name_and_class(self.args)
def _do_parse_master_port(self):
- return self.xfr._parse_master_and_port(self.args)
+ name, rrclass = self._do_parse_zone_name_class()
+ return self.xfr._parse_master_and_port(self.args, name, rrclass)
def test_parse_cmd_params(self):
name, rrclass = self._do_parse_zone_name_class()
@@ -441,7 +746,7 @@ class TestXfrin(unittest.TestCase):
def test_parse_cmd_params_bogusclass(self):
self.args['zone_class'] = 'XXX'
- self.assertRaises(XfrinException, self._do_parse_zone_name_class)
+ self.assertRaises(XfrinZoneInfoException, self._do_parse_zone_name_class)
def test_parse_cmd_params_nozone(self):
# zone name is mandatory.
@@ -451,8 +756,7 @@ class TestXfrin(unittest.TestCase):
def test_parse_cmd_params_nomaster(self):
# master address is mandatory.
del self.args['master']
- master_addrinfo = self._do_parse_master_port()
- self.assertEqual(master_addrinfo[2][0], DEFAULT_MASTER)
+ self.assertRaises(XfrinException, self._do_parse_master_port)
def test_parse_cmd_params_bad_ip4(self):
self.args['master'] = '3.3.3.3.3'
@@ -482,6 +786,77 @@ class TestXfrin(unittest.TestCase):
def test_command_handler_retransfer(self):
self.assertEqual(self.xfr.command_handler("retransfer",
self.args)['result'][0], 0)
+ self.assertEqual(self.args['master'], self.xfr.xfrin_started_master_addr)
+ self.assertEqual(int(self.args['port']), self.xfr.xfrin_started_master_port)
+
+ def test_command_handler_retransfer_short_command1(self):
+ # try it when only specifying the zone name (of unknown zone)
+ # this should fail because master address is not specified.
+ short_args = {}
+ short_args['zone_name'] = TEST_ZONE_NAME_STR
+ self.assertEqual(self.xfr.command_handler("retransfer",
+ short_args)['result'][0], 1)
+
+ def test_command_handler_retransfer_short_command2(self):
+ # try it when only specifying the zone name (of known zone)
+ short_args = {}
+ short_args['zone_name'] = TEST_ZONE_NAME_STR
+
+ zones = { 'zones': [
+ { 'name': TEST_ZONE_NAME_STR,
+ 'master_addr': TEST_MASTER_IPV4_ADDRESS,
+ 'master_port': TEST_MASTER_PORT
+ }
+ ]}
+ self.xfr.config_handler(zones)
+ self.assertEqual(self.xfr.command_handler("retransfer",
+ short_args)['result'][0], 0)
+ self.assertEqual(TEST_MASTER_IPV4_ADDRESS,
+ self.xfr.xfrin_started_master_addr)
+ self.assertEqual(int(TEST_MASTER_PORT),
+ self.xfr.xfrin_started_master_port)
+
+ def test_command_handler_retransfer_short_command3(self):
+ # try it when only specifying the zone name (of known zone)
+ short_args = {}
+ # test it without the trailing root dot
+ short_args['zone_name'] = TEST_ZONE_NAME_STR[:-1]
+
+ zones = { 'zones': [
+ { 'name': TEST_ZONE_NAME_STR,
+ 'master_addr': TEST_MASTER_IPV4_ADDRESS,
+ 'master_port': TEST_MASTER_PORT
+ }
+ ]}
+ self.xfr.config_handler(zones)
+ self.assertEqual(self.xfr.command_handler("retransfer",
+ short_args)['result'][0], 0)
+ self.assertEqual(TEST_MASTER_IPV4_ADDRESS,
+ self.xfr.xfrin_started_master_addr)
+ self.assertEqual(int(TEST_MASTER_PORT),
+ self.xfr.xfrin_started_master_port)
+
+ def test_command_handler_retransfer_short_command4(self):
+ # try it when only specifying the zone name (of known zone, with
+ # different case)
+ short_args = {}
+
+ # swap the case of the zone name in our command
+ short_args['zone_name'] = TEST_ZONE_NAME_STR.swapcase()
+
+ zones = { 'zones': [
+ { 'name': TEST_ZONE_NAME_STR,
+ 'master_addr': TEST_MASTER_IPV4_ADDRESS,
+ 'master_port': TEST_MASTER_PORT
+ }
+ ]}
+ self.xfr.config_handler(zones)
+ self.assertEqual(self.xfr.command_handler("retransfer",
+ short_args)['result'][0], 0)
+ self.assertEqual(TEST_MASTER_IPV4_ADDRESS,
+ self.xfr.xfrin_started_master_addr)
+ self.assertEqual(int(TEST_MASTER_PORT),
+ self.xfr.xfrin_started_master_port)
def test_command_handler_retransfer_badcommand(self):
self.args['master'] = 'invalid'
@@ -489,13 +864,15 @@ class TestXfrin(unittest.TestCase):
self.args)['result'][0], 1)
def test_command_handler_retransfer_quota(self):
+ self.args['master'] = TEST_MASTER_IPV4_ADDRESS
+
for i in range(self.xfr._max_transfers_in - 1):
- self.xfr.recorder.increment(str(i) + TEST_ZONE_NAME)
+ self.xfr.recorder.increment(Name(str(i) + TEST_ZONE_NAME_STR))
# there can be one more outstanding transfer.
self.assertEqual(self.xfr.command_handler("retransfer",
self.args)['result'][0], 0)
# make sure the # xfrs would excceed the quota
- self.xfr.recorder.increment(str(self.xfr._max_transfers_in) + TEST_ZONE_NAME)
+ self.xfr.recorder.increment(Name(str(self.xfr._max_transfers_in) + TEST_ZONE_NAME_STR))
# this one should fail
self.assertEqual(self.xfr.command_handler("retransfer",
self.args)['result'][0], 1)
@@ -519,14 +896,43 @@ class TestXfrin(unittest.TestCase):
self.args['master'] = TEST_MASTER_IPV6_ADDRESS
self.assertEqual(self.xfr.command_handler("refresh",
self.args)['result'][0], 0)
+ self.assertEqual(TEST_MASTER_IPV6_ADDRESS,
+ self.xfr.xfrin_started_master_addr)
+ self.assertEqual(int(TEST_MASTER_PORT),
+ self.xfr.xfrin_started_master_port)
def test_command_handler_notify(self):
# at this level, refresh is no different than retransfer.
self.args['master'] = TEST_MASTER_IPV6_ADDRESS
- # ...but right now we disable the feature due to security concerns.
+ # ...but the zone is unknown so this would return an error
+ self.assertEqual(self.xfr.command_handler("notify",
+ self.args)['result'][0], 1)
+
+ def test_command_handler_notify_known_zone(self):
+ # try it with a known zone
+ self.args['master'] = TEST_MASTER_IPV6_ADDRESS
+
+ # but use a different address in the actual command
+ zones = { 'zones': [
+ { 'name': TEST_ZONE_NAME_STR,
+ 'master_addr': TEST_MASTER_IPV4_ADDRESS,
+ 'master_port': TEST_MASTER_PORT
+ }
+ ]}
+ self.xfr.config_handler(zones)
self.assertEqual(self.xfr.command_handler("notify",
self.args)['result'][0], 0)
+ # and see if we used the address from the command, and not from
+ # the config
+ # This is actually NOT the address given in the command, which
+ # would at this point not make sense, see the TODO in
+ # xfrin.py.in Xfrin.command_handler())
+ self.assertEqual(TEST_MASTER_IPV4_ADDRESS,
+ self.xfr.xfrin_started_master_addr)
+ self.assertEqual(int(TEST_MASTER_PORT),
+ self.xfr.xfrin_started_master_port)
+
def test_command_handler_unknown(self):
self.assertEqual(self.xfr.command_handler("xxx", None)['result'][0], 1)
@@ -535,20 +941,145 @@ class TestXfrin(unittest.TestCase):
self.assertEqual(self.xfr.config_handler({'transfers_in': 3})['result'][0], 0)
self.assertEqual(self.xfr._max_transfers_in, 3)
- def test_command_handler_masters(self):
- master_info = {'master_addr': '1.1.1.1', 'master_port':53}
- self.assertEqual(self.xfr.config_handler(master_info)['result'][0], 0)
-
- master_info = {'master_addr': '1111.1.1.1', 'master_port':53 }
- self.assertEqual(self.xfr.config_handler(master_info)['result'][0], 1)
-
- master_info = {'master_addr': '2.2.2.2', 'master_port':530000 }
- self.assertEqual(self.xfr.config_handler(master_info)['result'][0], 1)
-
- master_info = {'master_addr': '2.2.2.2', 'master_port':53 }
- self.xfr.config_handler(master_info)
- self.assertEqual(self.xfr._master_addr, '2.2.2.2')
- self.assertEqual(self.xfr._master_port, 53)
+ def _check_zones_config(self, config_given):
+ if 'transfers_in' in config_given:
+ self.assertEqual(config_given['transfers_in'],
+ self.xfr._max_transfers_in)
+ for zone_config in config_given['zones']:
+ zone_name = zone_config['name']
+ zone_info = self.xfr._get_zone_info(Name(zone_name), RRClass.IN())
+ self.assertEqual(str(zone_info.master_addr), zone_config['master_addr'])
+ self.assertEqual(zone_info.master_port, zone_config['master_port'])
+ if 'tsig_key' in zone_config:
+ self.assertEqual(zone_info.tsig_key.to_text(), TSIGKey(zone_config['tsig_key']).to_text())
+ else:
+ self.assertIsNone(zone_info.tsig_key)
+
+ def test_command_handler_zones(self):
+ config1 = { 'transfers_in': 3,
+ 'zones': [
+ { 'name': 'test.example.',
+ 'master_addr': '192.0.2.1',
+ 'master_port': 53
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(config1)['result'][0], 0)
+ self._check_zones_config(config1)
+
+ config2 = { 'transfers_in': 4,
+ 'zones': [
+ { 'name': 'test.example.',
+ 'master_addr': '192.0.2.2',
+ 'master_port': 53,
+ 'tsig_key': "example.com:SFuWd/q99SzF8Yzd1QbB9g=="
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(config2)['result'][0], 0)
+ self._check_zones_config(config2)
+
+ # test that configuring the zone multiple times fails
+ zones = { 'transfers_in': 5,
+ 'zones': [
+ { 'name': 'test.example.',
+ 'master_addr': '192.0.2.1',
+ 'master_port': 53
+ },
+ { 'name': 'test.example.',
+ 'master_addr': '192.0.2.2',
+ 'master_port': 53
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config2)
+
+ zones = { 'zones': [
+ { 'name': 'test.example.',
+ 'master_addr': '192.0.2.3',
+ 'master_port': 53,
+ 'class': 'BADCLASS'
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ self._check_zones_config(config2)
+
+ zones = { 'zones': [
+ { 'master_addr': '192.0.2.4',
+ 'master_port': 53
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config2)
+
+ zones = { 'zones': [
+ { 'name': 'bad..zone.',
+ 'master_addr': '192.0.2.5',
+ 'master_port': 53
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config2)
+
+ zones = { 'zones': [
+ { 'name': '',
+ 'master_addr': '192.0.2.6',
+ 'master_port': 53
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config2)
+
+ zones = { 'zones': [
+ { 'name': 'test.example',
+ 'master_addr': 'badaddress',
+ 'master_port': 53
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config2)
+
+ zones = { 'zones': [
+ { 'name': 'test.example',
+ 'master_addr': '192.0.2.7',
+ 'master_port': 'bad_port'
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config2)
+
+ zones = { 'zones': [
+ { 'name': 'test.example',
+ 'master_addr': '192.0.2.7',
+ 'master_port': 53,
+ # using a bad TSIG key spec
+ 'tsig_key': "bad..example.com:SFuWd/q99SzF8Yzd1QbB9g=="
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config2)
+
+ # let's also add a zone that is correct too, and make sure
+ # that the new config is not partially taken
+ zones = { 'zones': [
+ { 'name': 'test.example.',
+ 'master_addr': '192.0.2.8',
+ 'master_port': 53
+ },
+ { 'name': 'test2.example.',
+ 'master_addr': '192.0.2.9',
+ 'master_port': 53,
+ 'tsig_key': 'badkey'
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config2)
def raise_interrupt():
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index fdbc990..d1fbbfe 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -1,7 +1,6 @@
#!@PYTHON@
-# Copyright (C) 2010 Internet Systems Consortium.
-# Copyright (C) 2010 CZ NIC
+# Copyright (C) 2009-2011 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
@@ -30,12 +29,17 @@ from isc.config.ccsession import *
from isc.notify import notify_out
import isc.util.process
import isc.net.parse
+from xfrin_messages import *
+
+isc.log.init("b10-xfrin")
+logger = isc.log.Logger("xfrin")
+
try:
from pydnspp import *
except ImportError as e:
# C++ loadable module may not be installed; even so the xfrin process
# must keep running, so we warn about it and move forward.
- sys.stderr.write('[b10-xfrin] failed to import DNS module: %s\n' % str(e))
+ logger.error(XFRIN_IMPORT_DNS, str(e))
isc.util.process.rename()
@@ -57,26 +61,64 @@ XFROUT_MODULE_NAME = 'Xfrout'
ZONE_MANAGER_MODULE_NAME = 'Zonemgr'
REFRESH_FROM_ZONEMGR = 'refresh_from_zonemgr'
ZONE_XFRIN_FAILED = 'zone_xfrin_failed'
+
+# These two default are currently hard-coded. For config this isn't
+# necessary, but we need these defaults for optional command arguments
+# (TODO: have similar support to get default values for command
+# arguments as we do for config options)
+DEFAULT_MASTER_PORT = 53
+DEFAULT_ZONE_CLASS = RRClass.IN()
+
__version__ = 'BIND10'
# define xfrin rcode
XFRIN_OK = 0
XFRIN_FAIL = 1
-DEFAULT_MASTER_PORT = '53'
-DEFAULT_MASTER = '127.0.0.1'
-
-def log_error(msg):
- sys.stderr.write("[b10-xfrin] %s\n" % str(msg))
+class XfrinException(Exception):
+ pass
-class XfrinException(Exception):
+class XfrinZoneInfoException(Exception):
+ """This exception is raised if there is an error in the given
+ configuration (part), or when a command does not have a required
+ argument or has bad arguments, for instance when the zone's master
+ address is not a valid IP address, when the zone does not
+ have a name, or when multiple settings are given for the same
+ zone."""
pass
+def _check_zone_name(zone_name_str):
+ """Checks if the given zone name is a valid domain name, and returns
+ it as a Name object. Raises an XfrinException if it is not."""
+ try:
+ # In the _zones dict, part of the key is the zone name,
+ # but due to a limitation in the Name class, we
+ # cannot directly use it as a dict key, and we use to_text()
+ #
+ # Downcase the name here for that reason.
+ return Name(zone_name_str, True)
+ except (EmptyLabel, TooLongLabel, BadLabelType, BadEscape,
+ TooLongName, IncompleteName) as ne:
+ raise XfrinZoneInfoException("bad zone name: " + zone_name_str + " (" + str(ne) + ")")
+
+def _check_zone_class(zone_class_str):
+ """If the given argument is a string: checks if the given class is
+ a valid one, and returns an RRClass object if so.
+ Raises XfrinZoneInfoException if not.
+ If it is None, this function returns the default RRClass.IN()"""
+ if zone_class_str is None:
+ return DEFAULT_ZONE_CLASS
+ try:
+ return RRClass(zone_class_str)
+ except InvalidRRClass as irce:
+ raise XfrinZoneInfoException("bad zone class: " + zone_class_str + " (" + str(irce) + ")")
+
class XfrinConnection(asyncore.dispatcher):
- '''Do xfrin in this class. '''
+ '''Do xfrin in this class. '''
def __init__(self,
sock_map, zone_name, rrclass, db_file, shutdown_event,
- master_addrinfo, verbose = False, idle_timeout = 60):
+ master_addrinfo, tsig_key = None, verbose = False,
+ idle_timeout = 60):
''' idle_timeout: max idle time for read data from socket.
db_file: specify the data source file.
check_soa: when it's true, check soa first before sending xfr query
@@ -94,6 +136,14 @@ class XfrinConnection(asyncore.dispatcher):
self._shutdown_event = shutdown_event
self._verbose = verbose
self._master_address = master_addrinfo[2]
+ self._tsig_key = tsig_key
+ self._tsig_ctx = None
+ # tsig_ctx_creator is introduced to allow tests to use a mock class for
+ # easier tests (in normal case we always use the default)
+ self._tsig_ctx_creator = self.__create_tsig_ctx
+
+ def __create_tsig_ctx(self, key):
+ return TSIGContext(key)
def connect_to_master(self):
'''Connect to master in TCP.'''
@@ -102,8 +152,7 @@ class XfrinConnection(asyncore.dispatcher):
self.connect(self._master_address)
return True
except socket.error as e:
- self.log_msg('Failed to connect:(%s), %s' % (self._master_address,
- str(e)))
+ logger.error(XFRIN_CONNECT_MASTER, self._master_address, str(e))
return False
def _create_query(self, query_type):
@@ -131,9 +180,15 @@ class XfrinConnection(asyncore.dispatcher):
msg = self._create_query(query_type)
render = MessageRenderer()
- msg.to_wire(render)
- header_len = struct.pack('H', socket.htons(render.get_length()))
+ # 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 self._tsig_key is not None:
+ self._tsig_ctx = self._tsig_ctx_creator(self._tsig_key)
+ msg.to_wire(render, self._tsig_ctx)
+ else:
+ msg.to_wire(render)
+ header_len = struct.pack('H', socket.htons(render.get_length()))
self._send_data(header_len)
self._send_data(render.get_data())
@@ -143,7 +198,7 @@ class XfrinConnection(asyncore.dispatcher):
_get_request_response so that we can test the rest of the code without
involving actual communication with a remote server.'''
asyncore.loop(self._idle_timeout, map=self._sock_map, count=1)
-
+
def _get_request_response(self, size):
recv_size = 0
data = b''
@@ -159,6 +214,22 @@ class XfrinConnection(asyncore.dispatcher):
return data
+ def _check_response_tsig(self, msg, response_data):
+ tsig_record = msg.get_tsig_record()
+ if self._tsig_ctx is not None:
+ tsig_error = self._tsig_ctx.verify(tsig_record, response_data)
+ if tsig_error != TSIGError.NOERROR:
+ raise XfrinException('TSIG verify fail: %s' % str(tsig_error))
+ elif tsig_record is not None:
+ # If the response includes a TSIG while we didn't sign the query,
+ # we treat it as an error. RFC doesn't say anything about this
+ # case, but it clearly states the server must not sign a response
+ # to an unsigned request. Although we could be flexible, no sane
+ # implementation would return such a response, and since this is
+ # part of security mechanism, it's probably better to be more
+ # strict.
+ raise XfrinException('Unexpected TSIG in response')
+
def _check_soa_serial(self):
''' Compare the soa serial, if soa serial in master is less than
the soa serial in local, Finish xfrin.
@@ -166,18 +237,21 @@ class XfrinConnection(asyncore.dispatcher):
True: soa serial in master is bigger
'''
- self._send_query(RRType("SOA"))
+ self._send_query(RRType.SOA())
data_len = self._get_request_response(2)
msg_len = socket.htons(struct.unpack('H', data_len)[0])
soa_response = self._get_request_response(msg_len)
msg = Message(Message.PARSE)
msg.from_wire(soa_response)
+ # TSIG related checks, including an unexpected signed response
+ self._check_response_tsig(msg, soa_response)
+
# perform some minimal level validation. It's an open issue how
# strict we should be (see the comment in _check_response_header())
self._check_response_header(msg)
- # TODO, need select soa record from data source then compare the two
+ # TODO, need select soa record from data source then compare the two
# serial, current just return OK, since this function hasn't been used
# now.
return XFRIN_OK
@@ -191,32 +265,27 @@ class XfrinConnection(asyncore.dispatcher):
logstr = 'SOA check for \'%s\' ' % self._zone_name
ret = self._check_soa_serial()
- logstr = 'transfer of \'%s\': AXFR ' % self._zone_name
if ret == XFRIN_OK:
- self.log_msg(logstr + 'started')
- # TODO: .AXFR() RRType.AXFR()
- self._send_query(RRType(252))
+ logger.info(XFRIN_AXFR_TRANSFER_STARTED, self._zone_name)
+ self._send_query(RRType.AXFR())
isc.datasrc.sqlite3_ds.load(self._db_file, self._zone_name,
self._handle_xfrin_response)
- self.log_msg(logstr + 'succeeded')
+ logger.info(XFRIN_AXFR_TRANSFER_SUCCESS, self._zone_name)
except XfrinException as e:
- self.log_msg(e)
- self.log_msg(logstr + 'failed')
+ logger.error(XFRIN_AXFR_TRANSFER_FAILURE, self._zone_name, str(e))
ret = XFRIN_FAIL
#TODO, recover data source.
except isc.datasrc.sqlite3_ds.Sqlite3DSError as e:
- self.log_msg(e)
- self.log_msg(logstr + 'failed')
+ logger.error(XFRIN_AXFR_DATABASE_FAILURE, self._zone_name, str(e))
ret = XFRIN_FAIL
except UserWarning as e:
# XXX: this is an exception from our C++ library via the
# Boost.Python binding. It would be better to have more more
# specific exceptions, but at this moment this is the finest
# granularity.
- self.log_msg(e)
- self.log_msg(logstr + 'failed')
+ logger.error(XFRIN_AXFR_INTERNAL_FAILURE, self._zone_name, str(e))
ret = XFRIN_FAIL
finally:
self.close()
@@ -239,7 +308,7 @@ class XfrinConnection(asyncore.dispatcher):
raise XfrinException('error response: %s' % msg_rcode.to_text())
if not msg.get_header_flag(Message.HEADERFLAG_QR):
- raise XfrinException('response is not a response ')
+ raise XfrinException('response is not a response')
if msg.get_qid() != self._query_id:
raise XfrinException('bad query id')
@@ -266,7 +335,7 @@ class XfrinConnection(asyncore.dispatcher):
for rdata in rrset.get_rdata():
# Count the soa record count
- if rrset.get_type() == RRType("SOA"):
+ if rrset.get_type() == RRType.SOA():
self._soa_rr_count += 1
# XXX: the current DNS message parser can't preserve the
@@ -290,15 +359,20 @@ class XfrinConnection(asyncore.dispatcher):
recvdata = self._get_request_response(msg_len)
msg = Message(Message.PARSE)
msg.from_wire(recvdata)
+
+ # TSIG related checks, including an unexpected signed response
+ self._check_response_tsig(msg, recvdata)
+
+ # Perform response status validation
self._check_response_status(msg)
-
+
answer_section = msg.get_section(Message.SECTION_ANSWER)
for rr in self._handle_answer_section(answer_section):
yield rr
if self._soa_rr_count == 2:
break
-
+
if self._shutdown_event.is_set():
raise XfrinException('xfrin is forced to stop')
@@ -318,21 +392,18 @@ class XfrinConnection(asyncore.dispatcher):
# Overwrite the log function, log nothing
pass
- def log_msg(self, msg):
- if self._verbose:
- sys.stdout.write('[b10-xfrin] %s\n' % str(msg))
-
-
-def process_xfrin(server, xfrin_recorder, zone_name, rrclass, db_file,
- shutdown_event, master_addrinfo, check_soa, verbose):
+def process_xfrin(server, xfrin_recorder, zone_name, rrclass, db_file,
+ shutdown_event, master_addrinfo, check_soa, verbose,
+ tsig_key):
xfrin_recorder.increment(zone_name)
sock_map = {}
conn = XfrinConnection(sock_map, zone_name, rrclass, db_file,
- shutdown_event, master_addrinfo, verbose)
+ shutdown_event, master_addrinfo,
+ tsig_key, verbose)
ret = XFRIN_FAIL
if conn.connect_to_master():
ret = conn.do_xfrin(check_soa)
-
+
# Publish the zone transfer result news, so zonemgr can reset the
# zone timer, and xfrout can notify the zone's slaves if the result
# is success.
@@ -368,60 +439,168 @@ class XfrinRecorder:
self._lock.release()
return ret
+class ZoneInfo:
+ def __init__(self, config_data, module_cc):
+ """Creates a zone_info with the config data element as
+ specified by the 'zones' list in xfrin.spec. Module_cc is
+ needed to get the defaults from the specification"""
+ self._module_cc = module_cc
+ self.set_name(config_data.get('name'))
+ self.set_master_addr(config_data.get('master_addr'))
+
+ self.set_master_port(config_data.get('master_port'))
+ self.set_zone_class(config_data.get('class'))
+ self.set_tsig_key(config_data.get('tsig_key'))
+
+ def set_name(self, name_str):
+ """Set the name for this zone given a name string.
+ Raises XfrinZoneInfoException if name_str is None or if it
+ cannot be parsed."""
+ if name_str is None:
+ raise XfrinZoneInfoException("Configuration zones list "
+ "element does not contain "
+ "'name' attribute")
+ else:
+ self.name = _check_zone_name(name_str)
+
+ def set_master_addr(self, master_addr_str):
+ """Set the master address for this zone given an IP address
+ string. Raises XfrinZoneInfoException if master_addr_str is
+ None or if it cannot be parsed."""
+ if master_addr_str is None:
+ raise XfrinZoneInfoException("master address missing from config data")
+ else:
+ try:
+ self.master_addr = isc.net.parse.addr_parse(master_addr_str)
+ except ValueError:
+ logger.error(XFRIN_BAD_MASTER_ADDR_FORMAT, master_addr_str)
+ errmsg = "bad format for zone's master: " + master_addr_str
+ raise XfrinZoneInfoException(errmsg)
+
+ def set_master_port(self, master_port_str):
+ """Set the master port given a port number string. If
+ master_port_str is None, the default from the specification
+ for this module will be used. Raises XfrinZoneInfoException if
+ the string contains an invalid port number"""
+ if master_port_str is None:
+ self.master_port = self._module_cc.get_default_value("zones/master_port")
+ else:
+ try:
+ self.master_port = isc.net.parse.port_parse(master_port_str)
+ except ValueError:
+ logger.error(XFRIN_BAD_MASTER_PORT_FORMAT, master_port_str)
+ errmsg = "bad format for zone's master port: " + master_port_str
+ raise XfrinZoneInfoException(errmsg)
+
+ def set_zone_class(self, zone_class_str):
+ """Set the zone class given an RR class str (e.g. "IN"). If
+ zone_class_str is None, it will default to what is specified
+ in the specification file for this module. Raises
+ XfrinZoneInfoException if the string cannot be parsed."""
+ # TODO: remove _str
+ self.class_str = zone_class_str or self._module_cc.get_default_value("zones/class")
+ if zone_class_str == None:
+ #TODO rrclass->zone_class
+ self.rrclass = RRClass(self._module_cc.get_default_value("zones/class"))
+ else:
+ try:
+ self.rrclass = RRClass(zone_class_str)
+ except InvalidRRClass:
+ logger.error(XFRIN_BAD_ZONE_CLASS, zone_class_str)
+ errmsg = "invalid zone class: " + zone_class_str
+ raise XfrinZoneInfoException(errmsg)
+
+ def set_tsig_key(self, tsig_key_str):
+ """Set the tsig_key for this zone, given a TSIG key string
+ representation. If tsig_key_str is None, no TSIG key will
+ be set. Raises XfrinZoneInfoException if tsig_key_str cannot
+ be parsed."""
+ if tsig_key_str is None:
+ self.tsig_key = None
+ else:
+ try:
+ self.tsig_key = TSIGKey(tsig_key_str)
+ except InvalidParameter as ipe:
+ logger.error(XFRIN_BAD_TSIG_KEY_STRING, tsig_key_str)
+ errmsg = "bad TSIG key string: " + tsig_key_str
+ raise XfrinZoneInfoException(errmsg)
+
+ def get_master_addr_info(self):
+ return (self.master_addr.family, socket.SOCK_STREAM,
+ (str(self.master_addr), self.master_port))
+
class Xfrin:
def __init__(self, verbose = False):
self._max_transfers_in = 10
- #TODO, this is the temp way to set the zone's master.
- self._master_addr = DEFAULT_MASTER
- self._master_port = DEFAULT_MASTER_PORT
+ self._zones = {}
self._cc_setup()
self.recorder = XfrinRecorder()
self._shutdown_event = threading.Event()
self._verbose = verbose
def _cc_setup(self):
- '''This method is used only as part of initialization, but is
- implemented separately for convenience of unit tests; by letting
- the test code override this method we can test most of this class
+ '''This method is used only as part of initialization, but is
+ implemented separately for convenience of unit tests; by letting
+ the test code override this method we can test most of this class
without requiring a command channel.'''
- # Create one session for sending command to other modules, because the
+ # Create one session for sending command to other modules, because the
# listening session will block the send operation.
self._send_cc_session = isc.cc.Session()
self._module_cc = isc.config.ModuleCCSession(SPECFILE_LOCATION,
self.config_handler,
- self.command_handler)
+ self.command_handler,
+ None, True)
self._module_cc.start()
config_data = self._module_cc.get_full_config()
- self._max_transfers_in = config_data.get("transfers_in")
- self._master_addr = config_data.get('master_addr') or self._master_addr
- self._master_port = config_data.get('master_port') or self._master_port
+ self.config_handler(config_data)
def _cc_check_command(self):
- '''This is a straightforward wrapper for cc.check_command,
- but provided as a separate method for the convenience
+ '''This is a straightforward wrapper for cc.check_command,
+ but provided as a separate method for the convenience
of unit tests.'''
self._module_cc.check_command(False)
+ def _get_zone_info(self, name, rrclass):
+ """Returns the ZoneInfo object containing the configured data
+ for the given zone name. If the zone name did not have any
+ data, returns None"""
+ return self._zones.get((name.to_text(), rrclass.to_text()))
+
+ def _add_zone_info(self, zone_info):
+ """Add the zone info. Raises a XfrinZoneInfoException if a zone
+ with the same name and class is already configured"""
+ key = (zone_info.name.to_text(), zone_info.class_str)
+ if key in self._zones:
+ raise XfrinZoneInfoException("zone " + str(key) +
+ " configured multiple times")
+ self._zones[key] = zone_info
+
+ def _clear_zone_info(self):
+ self._zones = {}
+
def config_handler(self, new_config):
+ # backup all config data (should there be a problem in the new
+ # data)
+ old_max_transfers_in = self._max_transfers_in
+ old_zones = self._zones
+
self._max_transfers_in = new_config.get("transfers_in") or self._max_transfers_in
- if ('master_addr' in new_config) or ('master_port' in new_config):
- # User should change the port and address together.
- try:
- addr = new_config.get('master_addr') or self._master_addr
- port = new_config.get('master_port') or self._master_port
- isc.net.parse.addr_parse(addr)
- isc.net.parse.port_parse(port)
- self._master_addr = addr
- self._master_port = port
- except ValueError:
- errmsg = "bad format for zone's master: " + str(new_config)
- log_error(errmsg)
- return create_answer(1, errmsg)
+
+ if 'zones' in new_config:
+ self._clear_zone_info()
+ for zone_config in new_config.get('zones'):
+ try:
+ zone_info = ZoneInfo(zone_config, self._module_cc)
+ self._add_zone_info(zone_info)
+ except XfrinZoneInfoException as xce:
+ self._zones = old_zones
+ self._max_transfers_in = old_max_transfers_in
+ return create_answer(1, str(xce))
return create_answer(0)
def shutdown(self):
- ''' shutdown the xfrin process. the thread which is doing xfrin should be
+ ''' shutdown the xfrin process. the thread which is doing xfrin should be
terminated.
'''
self._shutdown_event.set()
@@ -437,61 +616,104 @@ class Xfrin:
if command == 'shutdown':
self._shutdown_event.set()
elif command == 'notify' or command == REFRESH_FROM_ZONEMGR:
- # Xfrin receives the refresh/notify command from zone manager.
- # notify command maybe has the parameters which
+ # Xfrin receives the refresh/notify command from zone manager.
+ # notify command maybe has the parameters which
# specify the notifyfrom address and port, according the RFC1996, zone
# transfer should starts first from the notifyfrom, but now, let 'TODO' it.
+ # (using the value now, while we can only set one master address, would be
+ # a security hole. Once we add the ability to have multiple master addresses,
+ # we should check if it matches one of them, and then use it.)
(zone_name, rrclass) = self._parse_zone_name_and_class(args)
- (master_addr) = build_addr_info(self._master_addr, self._master_port)
- ret = self.xfrin_start(zone_name,
- rrclass,
- self._get_db_file(),
- master_addr,
- True)
- answer = create_answer(ret[0], ret[1])
+ zone_info = self._get_zone_info(zone_name, rrclass)
+ if zone_info is None:
+ # TODO what to do? no info known about zone. defaults?
+ errmsg = "Got notification to retransfer unknown zone " + zone_name.to_text()
+ logger.error(XFRIN_RETRANSFER_UNKNOWN_ZONE, zone_name.to_text())
+ answer = create_answer(1, errmsg)
+ else:
+ master_addr = zone_info.get_master_addr_info()
+ ret = self.xfrin_start(zone_name,
+ rrclass,
+ self._get_db_file(),
+ master_addr,
+ zone_info.tsig_key,
+ True)
+ answer = create_answer(ret[0], ret[1])
elif command == 'retransfer' or command == 'refresh':
# Xfrin receives the retransfer/refresh from cmdctl(sent by bindctl).
- # If the command has specified master address, do transfer from the
- # master address, or else do transfer from the configured masters.
+ # If the command has specified master address, do transfer from the
+ # master address, or else do transfer from the configured masters.
(zone_name, rrclass) = self._parse_zone_name_and_class(args)
- master_addr = self._parse_master_and_port(args)
+ master_addr = self._parse_master_and_port(args, zone_name,
+ rrclass)
+ zone_info = self._get_zone_info(zone_name, rrclass)
+ tsig_key = None
+ if zone_info:
+ tsig_key = zone_info.tsig_key
db_file = args.get('db_file') or self._get_db_file()
- ret = self.xfrin_start(zone_name,
- rrclass,
- db_file,
+ ret = self.xfrin_start(zone_name,
+ rrclass,
+ db_file,
master_addr,
+ tsig_key,
(False if command == 'retransfer' else True))
answer = create_answer(ret[0], ret[1])
else:
answer = create_answer(1, 'unknown command: ' + command)
except XfrinException as err:
- log_error('error happened for command: %s, %s' % (command, str(err)) )
+ logger.error(XFRIN_COMMAND_ERROR, command, str(err))
answer = create_answer(1, str(err))
return answer
def _parse_zone_name_and_class(self, args):
- zone_name = args.get('zone_name')
- if not zone_name:
+ zone_name_str = args.get('zone_name')
+ if zone_name_str is None:
raise XfrinException('zone name should be provided')
- rrclass = args.get('zone_class')
- if not rrclass:
- rrclass = RRClass.IN()
+ return (_check_zone_name(zone_name_str), _check_zone_class(args.get('zone_class')))
+
+ def _parse_master_and_port(self, args, zone_name, zone_class):
+ """
+ Return tuple (family, socktype, sockaddr) for address and port in given
+ args dict.
+ IPv4 and IPv6 are the only supported addresses now, so sockaddr will be
+ (address, port). The socktype is socket.SOCK_STREAM for now.
+ """
+ # check if we have configured info about this zone, in case
+ # port or master are not specified
+ zone_info = self._get_zone_info(zone_name, zone_class)
+
+ addr_str = args.get('master')
+ if addr_str is None:
+ if zone_info is not None:
+ addr = zone_info.master_addr
+ else:
+ raise XfrinException("Master address not given or "
+ "configured for " + zone_name.to_text())
+ else:
+ try:
+ addr = isc.net.parse.addr_parse(addr_str)
+ except ValueError as err:
+ raise XfrinException("failed to resolve master address %s: %s" %
+ (addr_str, str(err)))
+
+ port_str = args.get('port')
+ if port_str is None:
+ if zone_info is not None:
+ port = zone_info.master_port
+ else:
+ port = DEFAULT_MASTER_PORT
else:
try:
- rrclass = RRClass(rrclass)
- except InvalidRRClass as e:
- raise XfrinException('invalid RRClass: ' + rrclass)
-
- return zone_name, rrclass
-
- def _parse_master_and_port(self, args):
- port = args.get('port') or self._master_port
- master = args.get('master') or self._master_addr
- return build_addr_info(master, port)
-
+ port = isc.net.parse.port_parse(port_str)
+ except ValueError as err:
+ raise XfrinException("failed to parse port=%s: %s" %
+ (port_str, str(err)))
+
+ return (addr.family, socket.SOCK_STREAM, (str(addr), port))
+
def _get_db_file(self):
#TODO, the db file path should be got in auth server's configuration
# if we need access to this configuration more often, we
@@ -507,12 +729,12 @@ class Xfrin:
db_file = os.environ["B10_FROM_BUILD"] + os.sep + "bind10_zones.sqlite3"
self._module_cc.remove_remote_config(AUTH_SPECFILE_LOCATION)
return db_file
-
+
def publish_xfrin_news(self, zone_name, zone_class, xfr_result):
'''Send command to xfrout/zone manager module.
- If xfrin has finished successfully for one zone, tell the good
+ If xfrin has finished successfully for one zone, tell the good
news(command: zone_new_data_ready) to zone manager and xfrout.
- if xfrin failed, just tell the bad news to zone manager, so that
+ if xfrin failed, just tell the bad news to zone manager, so that
it can reset the refresh timer for that zone. '''
param = {'zone_name': zone_name, 'zone_class': zone_class.to_text()}
if xfr_result == XFRIN_OK:
@@ -532,9 +754,8 @@ class Xfrin:
seq)
except isc.cc.session.SessionTimeout:
pass # for now we just ignore the failure
- except socket.error as err:
- log_error("Fail to send message to %s and %s, msgq may has been killed"
- % (XFROUT_MODULE_NAME, ZONE_MANAGER_MODULE_NAME))
+ except socket.error as err:
+ logger.error(XFRIN_MSGQ_SEND_ERROR, XFROUT_MODULE_NAME, ZONE_MANAGER_MODULE_NAME)
else:
msg = create_command(ZONE_XFRIN_FAILED, param)
# catch the exception, in case msgq has been killed.
@@ -546,14 +767,13 @@ class Xfrin:
except isc.cc.session.SessionTimeout:
pass # for now we just ignore the failure
except socket.error as err:
- log_error("Fail to send message to %s, msgq may has been killed"
- % ZONE_MANAGER_MODULE_NAME)
+ logger.error(XFRIN_MSGQ_SEND_ERROR_ZONE_MANAGER, ZONE_MANAGER_MODULE_NAME)
def startup(self):
while not self._shutdown_event.is_set():
self._cc_check_command()
- def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo,
+ def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo, tsig_key,
check_soa = True):
if "pydnspp" not in sys.modules:
return (1, "xfrin failed, can't load dns message python library: 'pydnspp'")
@@ -568,11 +788,13 @@ class Xfrin:
xfrin_thread = threading.Thread(target = process_xfrin,
args = (self,
self.recorder,
- zone_name, rrclass,
+ zone_name.to_text(),
+ rrclass,
db_file,
self._shutdown_event,
master_addrinfo, check_soa,
- self._verbose))
+ self._verbose,
+ tsig_key))
xfrin_thread.start()
return (0, 'zone xfrin is started')
@@ -589,20 +811,6 @@ def set_signal_handler():
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
-def build_addr_info(addrstr, portstr):
- """
- Return tuple (family, socktype, sockaddr) for given address and port.
- IPv4 and IPv6 are the only supported addresses now, so sockaddr will be
- (address, port). The socktype is socket.SOCK_STREAM for now.
- """
- try:
- port = isc.net.parse.port_parse(portstr)
- addr = isc.net.parse.addr_parse(addrstr)
- return (addr.family, socket.SOCK_STREAM, (addrstr, port))
- except ValueError as err:
- raise XfrinException("failed to resolve master address/port=%s/%s: %s" %
- (addrstr, portstr, str(err)))
-
def set_cmd_options(parser):
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help="display more about what is going on")
@@ -627,12 +835,11 @@ def main(xfrin_class, use_signal = True):
xfrind = xfrin_class(verbose = options.verbose)
xfrind.startup()
except KeyboardInterrupt:
- log_error("exit b10-xfrin")
+ logger.info(XFRIN_STOPPED_BY_KEYBOARD)
except isc.cc.session.SessionError as e:
- log_error(str(e))
- log_error('Error happened! is the command channel daemon running?')
+ logger.error(XFRIN_CC_SESSION_ERROR, str(e))
except Exception as e:
- log_error(str(e))
+ logger.error(XFRIN_UNKNOWN_ERROR, str(e))
if xfrind:
xfrind.shutdown()
diff --git a/src/bin/xfrin/xfrin.spec b/src/bin/xfrin/xfrin.spec
index 61ddaad..a3e62ce 100644
--- a/src/bin/xfrin/xfrin.spec
+++ b/src/bin/xfrin/xfrin.spec
@@ -9,16 +9,43 @@
"item_optional": false,
"item_default": 10
},
- {
- "item_name": "master_addr",
- "item_type": "string",
- "item_optional": false,
- "item_default": ""
- },
- { "item_name": "master_port",
- "item_type": "integer",
+ { "item_name": "zones",
+ "item_type": "list",
"item_optional": false,
- "item_default": 53
+ "item_default": [],
+ "list_item_spec":
+ { "item_type": "map",
+ "item_name": "zone_info",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "name",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ },
+ { "item_name": "class",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "IN"
+ },
+ {
+ "item_name": "master_addr",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ },
+ { "item_name": "master_port",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 53
+ },
+ { "item_name": "tsig_key",
+ "item_type": "string",
+ "item_optional": true
+ }
+ ]
+ }
}
],
"commands": [
diff --git a/src/bin/xfrin/xfrin_messages.mes b/src/bin/xfrin/xfrin_messages.mes
new file mode 100644
index 0000000..80a0be3
--- /dev/null
+++ b/src/bin/xfrin/xfrin_messages.mes
@@ -0,0 +1,91 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# No namespace declaration - these constants go in the global namespace
+# of the xfrin messages python module.
+
+% XFRIN_AXFR_INTERNAL_FAILURE AXFR transfer of zone %1 failed: %2
+The AXFR transfer for the given zone has failed due to an internal
+problem in the bind10 python wrapper library.
+The error is shown in the log message.
+
+% XFRIN_AXFR_DATABASE_FAILURE AXFR transfer of zone %1 failed: %2
+The AXFR transfer for the given zone has failed due to a database problem.
+The error is shown in the log message.
+
+% XFRIN_AXFR_TRANSFER_FAILURE AXFR transfer of zone %1 failed: %2
+The AXFR transfer for the given zone has failed due to a protocol error.
+The error is shown in the log message.
+
+% XFRIN_AXFR_TRANSFER_STARTED AXFR transfer of zone %1 started
+A connection to the master server has been made, the serial value in
+the SOA record has been checked, and a zone transfer has been started.
+
+% XFRIN_AXFR_TRANSFER_SUCCESS AXFR transfer of zone %1 succeeded
+The AXFR transfer of the given zone was successfully completed.
+
+% XFRIN_BAD_MASTER_ADDR_FORMAT bad format for master address: %1
+The given master address is not a valid IP address.
+
+% XFRIN_BAD_MASTER_PORT_FORMAT bad format for master port: %1
+The master port as read from the configuration is not a valid port number.
+
+% XFRIN_BAD_TSIG_KEY_STRING bad TSIG key string: %1
+The TSIG key string as read from the configuration does not represent
+a valid TSIG key.
+
+% XFRIN_BAD_ZONE_CLASS Invalid zone class: %1
+The zone class as read from the configuration is not a valid DNS class.
+
+% XFRIN_CC_SESSION_ERROR error reading from cc channel: %1
+There was a problem reading from the command and control channel. The
+most likely cause is that xfrin the msgq daemon is not running.
+
+% XFRIN_COMMAND_ERROR error while executing command '%1': %2
+There was an error while the given command was being processed. The
+error is given in the log message.
+
+% XFRIN_CONNECT_MASTER error connecting to master at %1: %2
+There was an error opening a connection to the master. The error is
+shown in the log message.
+
+% XFRIN_MSGQ_SEND_ERROR error while contacting %1 and %2
+There was a problem sending a message to the xfrout module or the
+zone manager. This most likely means that the msgq daemon has quit or
+was killed.
+
+% XFRIN_MSGQ_SEND_ERROR_ZONE_MANAGER error while contacting %1
+There was a problem sending a message to the zone manager. This most
+likely means that the msgq daemon has quit or was killed.
+
+% XFRIN_IMPORT_DNS error importing python DNS module: %1
+There was an error importing the python DNS module pydnspp. The most
+likely cause is a PYTHONPATH problem.
+
+% XFRIN_RETRANSFER_UNKNOWN_ZONE got notification to retransfer unknown zone %1
+There was an internal command to retransfer the given zone, but the
+zone is not known to the system. This may indicate that the configuration
+for xfrin is incomplete, or there was a typographical error in the
+zone name in the configuration.
+
+% XFRIN_STARTING starting resolver with command line '%1'
+An informational message, this is output when the resolver starts up.
+
+% XFRIN_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
+There was a keyboard interrupt signal to stop the xfrin daemon. The
+daemon will now shut down.
+
+% XFRIN_UNKNOWN_ERROR unknown error: %1
+An uncaught exception was raised while running the xfrin daemon. The
+exception message is printed in the log message.
diff --git a/src/bin/xfrout/Makefile.am b/src/bin/xfrout/Makefile.am
index d4f021e..c5492ad 100644
--- a/src/bin/xfrout/Makefile.am
+++ b/src/bin/xfrout/Makefile.am
@@ -6,11 +6,12 @@ pkglibexec_SCRIPTS = b10-xfrout
b10_xfroutdir = $(pkgdatadir)
b10_xfrout_DATA = xfrout.spec
+pyexec_DATA = xfrout_messages.py
-CLEANFILES= b10-xfrout xfrout.pyc xfrout.spec
+CLEANFILES= b10-xfrout xfrout.pyc xfrout.spec xfrout_messages.py xfrout_messages.pyc
man_MANS = b10-xfrout.8
-EXTRA_DIST = $(man_MANS) b10-xfrout.xml
+EXTRA_DIST = $(man_MANS) b10-xfrout.xml xfrout_messages.mes
if ENABLE_MAN
@@ -19,12 +20,20 @@ b10-xfrout.8: b10-xfrout.xml
endif
+# Define rule to build logging source files from message file
+xfrout_messages.py: xfrout_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/xfrout/xfrout_messages.mes
xfrout.spec: xfrout.spec.pre
$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" xfrout.spec.pre >$@
# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-b10-xfrout: xfrout.py
+b10-xfrout: xfrout.py xfrout_messages.py
$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
-e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" xfrout.py >$@
chmod a+x $@
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/xfrout/tests/Makefile.am b/src/bin/xfrout/tests/Makefile.am
index 01f2e40..6ca2b42 100644
--- a/src/bin/xfrout/tests/Makefile.am
+++ b/src/bin/xfrout/tests/Makefile.am
@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
# required by loadable python modules.
LIBRARY_PATH_PLACEHOLDER =
if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
endif
# test using command-line arguments, so use check-local target instead of TESTS
@@ -18,7 +18,7 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
- env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
$(LIBRARY_PATH_PLACEHOLDER) \
- $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+ env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/util/io/.libs \
+ $(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
done
diff --git a/src/bin/xfrout/tests/xfrout_test.py b/src/bin/xfrout/tests/xfrout_test.py
deleted file mode 100644
index 5aec072..0000000
--- a/src/bin/xfrout/tests/xfrout_test.py
+++ /dev/null
@@ -1,437 +0,0 @@
-# Copyright (C) 2010 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 XfroutSession and UnixSockServer classes '''
-
-
-import unittest
-import os
-from isc.cc.session import *
-from pydnspp import *
-from xfrout import *
-
-# our fake socket, where we can read and insert messages
-class MySocket():
- def __init__(self, family, type):
- self.family = family
- self.type = type
- self.sendqueue = bytearray()
-
- def connect(self, to):
- pass
-
- def close(self):
- pass
-
- def send(self, data):
- self.sendqueue.extend(data);
- return len(data)
-
- def readsent(self):
- if len(self.sendqueue) >= 2:
- size = 2 + struct.unpack("!H", self.sendqueue[:2])[0]
- else:
- size = 0
- result = self.sendqueue[:size]
- self.sendqueue = self.sendqueue[size:]
- return result
-
- def read_msg(self):
- sent_data = self.readsent()
- get_msg = Message(Message.PARSE)
- get_msg.from_wire(bytes(sent_data[2:]))
- return get_msg
-
- def clear_send(self):
- del self.sendqueue[:]
-
-# We subclass the Session class we're testing here, only
-# to override the handle() and _send_data() method
-class MyXfroutSession(XfroutSession):
- def handle(self):
- pass
-
- def _send_data(self, sock, data):
- size = len(data)
- total_count = 0
- while total_count < size:
- count = sock.send(data[total_count:])
- total_count += count
-
-class Dbserver:
- def __init__(self):
- self._shutdown_event = threading.Event()
- def get_db_file(self):
- return None
- def decrease_transfers_counter(self):
- pass
-
-class TestXfroutSession(unittest.TestCase):
- def getmsg(self):
- msg = Message(Message.PARSE)
- msg.from_wire(self.mdata)
- return msg
-
- def setUp(self):
- self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM)
- self.log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False )
- self.xfrsess = MyXfroutSession(self.sock, None, Dbserver(), self.log)
- self.mdata = bytes(b'\xd6=\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01')
- self.soa_record = (4, 3, 'example.com.', 'com.example.', 3600, 'SOA', None, 'master.example.com. admin.example.com. 1234 3600 1800 2419200 7200')
-
- def test_parse_query_message(self):
- [get_rcode, get_msg] = self.xfrsess._parse_query_message(self.mdata)
- self.assertEqual(get_rcode.to_text(), "NOERROR")
-
- def test_get_query_zone_name(self):
- msg = self.getmsg()
- self.assertEqual(self.xfrsess._get_query_zone_name(msg), "example.com.")
-
- def test_send_data(self):
- self.xfrsess._send_data(self.sock, self.mdata)
- senddata = self.sock.readsent()
- self.assertEqual(senddata, self.mdata)
-
- def test_reply_xfrout_query_with_error_rcode(self):
- msg = self.getmsg()
- self.xfrsess._reply_query_with_error_rcode(msg, self.sock, Rcode(3))
- get_msg = self.sock.read_msg()
- self.assertEqual(get_msg.get_rcode().to_text(), "NXDOMAIN")
-
- def test_send_message(self):
- msg = self.getmsg()
- msg.make_response()
- # soa record data with different cases
- soa_record = (4, 3, 'Example.com.', 'com.Example.', 3600, 'SOA', None, 'master.Example.com. admin.exAmple.com. 1234 3600 1800 2419200 7200')
- rrset_soa = self.xfrsess._create_rrset_from_db_record(soa_record)
- msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
- self.xfrsess._send_message(self.sock, msg)
- send_out_data = self.sock.readsent()[2:]
-
- # CASE_INSENSITIVE compression mode
- render = MessageRenderer();
- render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
- msg.to_wire(render)
- self.assertNotEqual(render.get_data(), send_out_data)
-
- # CASE_SENSITIVE compression mode
- render.clear()
- render.set_compress_mode(MessageRenderer.CASE_SENSITIVE)
- render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
- msg.to_wire(render)
- self.assertEqual(render.get_data(), send_out_data)
-
- def test_clear_message(self):
- msg = self.getmsg()
- qid = msg.get_qid()
- opcode = msg.get_opcode()
- rcode = msg.get_rcode()
-
- self.xfrsess._clear_message(msg)
- self.assertEqual(msg.get_qid(), qid)
- self.assertEqual(msg.get_opcode(), opcode)
- self.assertEqual(msg.get_rcode(), rcode)
- self.assertTrue(msg.get_header_flag(Message.HEADERFLAG_AA))
-
- def test_reply_query_with_format_error(self):
- msg = self.getmsg()
- self.xfrsess._reply_query_with_format_error(msg, self.sock)
- get_msg = self.sock.read_msg()
- self.assertEqual(get_msg.get_rcode().to_text(), "FORMERR")
-
- def test_create_rrset_from_db_record(self):
- rrset = self.xfrsess._create_rrset_from_db_record(self.soa_record)
- self.assertEqual(rrset.get_name().to_text(), "example.com.")
- self.assertEqual(rrset.get_class(), RRClass("IN"))
- self.assertEqual(rrset.get_type().to_text(), "SOA")
- rdata = rrset.get_rdata()
- self.assertEqual(rdata[0].to_text(), self.soa_record[7])
-
- def test_send_message_with_last_soa(self):
- rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
-
- msg = self.getmsg()
- msg.make_response()
- self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, 0)
- get_msg = self.sock.read_msg()
-
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
-
- #answer_rrset_iter = section_iter(get_msg, section.ANSWER())
- answer = get_msg.get_section(Message.SECTION_ANSWER)[0]#answer_rrset_iter.get_rrset()
- self.assertEqual(answer.get_name().to_text(), "example.com.")
- self.assertEqual(answer.get_class(), RRClass("IN"))
- self.assertEqual(answer.get_type().to_text(), "SOA")
- rdata = answer.get_rdata()
- self.assertEqual(rdata[0].to_text(), self.soa_record[7])
-
- def test_trigger_send_message_with_last_soa(self):
- rrset_a = RRset(Name("example.com"), RRClass.IN(), RRType.A(), RRTTL(3600))
- rrset_a.add_rdata(Rdata(RRType.A(), RRClass.IN(), "192.0.2.1"))
- rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
-
- msg = self.getmsg()
- msg.make_response()
-
- msg.add_rrset(Message.SECTION_ANSWER, rrset_a)
- # give the function a value that is larger than MAX-len(rrset)
- self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, 65520)
-
- # this should have triggered the sending of two messages
- # (1 with the rrset we added manually, and 1 that triggered
- # the sending in _with_last_soa)
- get_msg = self.sock.read_msg()
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
-
- answer = get_msg.get_section(Message.SECTION_ANSWER)[0]
- self.assertEqual(answer.get_name().to_text(), "example.com.")
- self.assertEqual(answer.get_class(), RRClass("IN"))
- self.assertEqual(answer.get_type().to_text(), "A")
- rdata = answer.get_rdata()
- self.assertEqual(rdata[0].to_text(), "192.0.2.1")
-
- get_msg = self.sock.read_msg()
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 0)
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
- self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
-
- #answer_rrset_iter = section_iter(get_msg, Message.SECTION_ANSWER)
- answer = get_msg.get_section(Message.SECTION_ANSWER)[0]
- self.assertEqual(answer.get_name().to_text(), "example.com.")
- self.assertEqual(answer.get_class(), RRClass("IN"))
- self.assertEqual(answer.get_type().to_text(), "SOA")
- rdata = answer.get_rdata()
- self.assertEqual(rdata[0].to_text(), self.soa_record[7])
-
- # and it should not have sent anything else
- self.assertEqual(0, len(self.sock.sendqueue))
-
- def test_get_rrset_len(self):
- rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
- self.assertEqual(82, get_rrset_len(rrset_soa))
-
- def test_zone_has_soa(self):
- global sqlite3_ds
- def mydb1(zone, file):
- return True
- sqlite3_ds.get_zone_soa = mydb1
- self.assertTrue(self.xfrsess._zone_has_soa(""))
- def mydb2(zone, file):
- return False
- sqlite3_ds.get_zone_soa = mydb2
- self.assertFalse(self.xfrsess._zone_has_soa(""))
-
- def test_zone_exist(self):
- global sqlite3_ds
- def zone_exist(zone, file):
- return zone
- sqlite3_ds.zone_exist = zone_exist
- self.assertTrue(self.xfrsess._zone_exist(True))
- self.assertFalse(self.xfrsess._zone_exist(False))
-
- def test_check_xfrout_available(self):
- def zone_exist(zone):
- return zone
- def zone_has_soa(zone):
- return (not zone)
- self.xfrsess._zone_exist = zone_exist
- self.xfrsess._zone_has_soa = zone_has_soa
- self.assertEqual(self.xfrsess._check_xfrout_available(False).to_text(), "NOTAUTH")
- self.assertEqual(self.xfrsess._check_xfrout_available(True).to_text(), "SERVFAIL")
-
- def zone_empty(zone):
- return zone
- self.xfrsess._zone_has_soa = zone_empty
- def false_func():
- return False
- self.xfrsess._server.increase_transfers_counter = false_func
- self.assertEqual(self.xfrsess._check_xfrout_available(True).to_text(), "REFUSED")
- def true_func():
- return True
- self.xfrsess._server.increase_transfers_counter = true_func
- self.assertEqual(self.xfrsess._check_xfrout_available(True).to_text(), "NOERROR")
-
- def test_dns_xfrout_start_formerror(self):
- # formerror
- self.xfrsess.dns_xfrout_start(self.sock, b"\xd6=\x00\x00\x00\x01\x00")
- sent_data = self.sock.readsent()
- self.assertEqual(len(sent_data), 0)
-
- def default(self, param):
- return "example.com"
-
- def test_dns_xfrout_start_notauth(self):
- self.xfrsess._get_query_zone_name = self.default
- def notauth(formpara):
- return Rcode.NOTAUTH()
- self.xfrsess._check_xfrout_available = notauth
- self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
- get_msg = self.sock.read_msg()
- self.assertEqual(get_msg.get_rcode().to_text(), "NOTAUTH")
-
- def test_dns_xfrout_start_noerror(self):
- self.xfrsess._get_query_zone_name = self.default
- def noerror(form):
- return Rcode.NOERROR()
- self.xfrsess._check_xfrout_available = noerror
-
- def myreply(msg, sock, zonename):
- self.sock.send(b"success")
-
- self.xfrsess._reply_xfrout_query = myreply
- self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
- self.assertEqual(self.sock.readsent(), b"success")
-
- def test_reply_xfrout_query_noerror(self):
- global sqlite3_ds
- def get_zone_soa(zonename, file):
- return self.soa_record
-
- def get_zone_datas(zone, file):
- return [self.soa_record]
-
- sqlite3_ds.get_zone_soa = get_zone_soa
- sqlite3_ds.get_zone_datas = get_zone_datas
- self.xfrsess._reply_xfrout_query(self.getmsg(), self.sock, "example.com.")
- reply_msg = self.sock.read_msg()
- self.assertEqual(reply_msg.get_rr_count(Message.SECTION_ANSWER), 2)
-
-class MyCCSession():
- def __init__(self):
- pass
-
- def get_remote_config_value(self, module_name, identifier):
- if module_name == "Auth" and identifier == "database_file":
- return "initdb.file", False
- else:
- return "unknown", False
-
-
-class MyUnixSockServer(UnixSockServer):
- def __init__(self):
- self._lock = threading.Lock()
- self._transfers_counter = 0
- self._shutdown_event = threading.Event()
- self._max_transfers_out = 10
- self._cc = MyCCSession()
- self._log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False )
-
-class TestUnixSockServer(unittest.TestCase):
- def setUp(self):
- self.write_sock, self.read_sock = socket.socketpair()
- self.unix = MyUnixSockServer()
-
- def test_receive_query_message(self):
- send_msg = b"\xd6=\x00\x00\x00\x01\x00"
- msg_len = struct.pack('H', socket.htons(len(send_msg)))
- self.write_sock.send(msg_len)
- self.write_sock.send(send_msg)
- recv_msg = self.unix._receive_query_message(self.read_sock)
- self.assertEqual(recv_msg, send_msg)
-
- def test_updata_config_data(self):
- self.unix.update_config_data({'transfers_out':10 })
- self.assertEqual(self.unix._max_transfers_out, 10)
-
- def test_get_db_file(self):
- self.assertEqual(self.unix.get_db_file(), "initdb.file")
-
- def test_increase_transfers_counter(self):
- self.unix._max_transfers_out = 10
- count = self.unix._transfers_counter
- self.assertEqual(self.unix.increase_transfers_counter(), True)
- self.assertEqual(count + 1, self.unix._transfers_counter)
-
- self.unix._max_transfers_out = 0
- count = self.unix._transfers_counter
- self.assertEqual(self.unix.increase_transfers_counter(), False)
- self.assertEqual(count, self.unix._transfers_counter)
-
- def test_decrease_transfers_counter(self):
- count = self.unix._transfers_counter
- self.unix.decrease_transfers_counter()
- self.assertEqual(count - 1, self.unix._transfers_counter)
-
- def _remove_file(self, sock_file):
- try:
- os.remove(sock_file)
- except OSError:
- pass
-
- def test_sock_file_in_use_file_exist(self):
- sock_file = 'temp.sock.file'
- self._remove_file(sock_file)
- self.assertFalse(self.unix._sock_file_in_use(sock_file))
- self.assertFalse(os.path.exists(sock_file))
-
- def test_sock_file_in_use_file_not_exist(self):
- self.assertFalse(self.unix._sock_file_in_use('temp.sock.file'))
-
- def _start_unix_sock_server(self, sock_file):
- serv = ThreadingUnixStreamServer(sock_file, BaseRequestHandler)
- serv_thread = threading.Thread(target=serv.serve_forever)
- serv_thread.setDaemon(True)
- serv_thread.start()
-
- def test_sock_file_in_use(self):
- sock_file = 'temp.sock.file'
- self._remove_file(sock_file)
- self.assertFalse(self.unix._sock_file_in_use(sock_file))
- self._start_unix_sock_server(sock_file)
-
- old_stdout = sys.stdout
- sys.stdout = open(os.devnull, 'w')
- self.assertTrue(self.unix._sock_file_in_use(sock_file))
- sys.stdout = old_stdout
-
- def test_remove_unused_sock_file_in_use(self):
- sock_file = 'temp.sock.file'
- self._remove_file(sock_file)
- self.assertFalse(self.unix._sock_file_in_use(sock_file))
- self._start_unix_sock_server(sock_file)
- old_stdout = sys.stdout
- sys.stdout = open(os.devnull, 'w')
- try:
- self.unix._remove_unused_sock_file(sock_file)
- except SystemExit:
- pass
- else:
- # This should never happen
- self.assertTrue(False)
-
- sys.stdout = old_stdout
-
- def test_remove_unused_sock_file_dir(self):
- import tempfile
- dir_name = tempfile.mkdtemp()
- old_stdout = sys.stdout
- sys.stdout = open(os.devnull, 'w')
- try:
- self.unix._remove_unused_sock_file(dir_name)
- except SystemExit:
- pass
- else:
- # This should never happen
- self.assertTrue(False)
-
- sys.stdout = old_stdout
- os.rmdir(dir_name)
-
-if __name__== "__main__":
- unittest.main()
diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in
new file mode 100644
index 0000000..adabf48
--- /dev/null
+++ b/src/bin/xfrout/tests/xfrout_test.py.in
@@ -0,0 +1,673 @@
+# Copyright (C) 2010 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 XfroutSession and UnixSockServer classes '''
+
+
+import unittest
+import os
+from isc.testutils.tsigctx_mock import MockTSIGContext
+from isc.cc.session import *
+from pydnspp import *
+from xfrout import *
+import xfrout
+
+TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==")
+
+# our fake socket, where we can read and insert messages
+class MySocket():
+ def __init__(self, family, type):
+ self.family = family
+ self.type = type
+ self.sendqueue = bytearray()
+
+ def connect(self, to):
+ pass
+
+ def close(self):
+ pass
+
+ def send(self, data):
+ self.sendqueue.extend(data);
+ return len(data)
+
+ def readsent(self):
+ if len(self.sendqueue) >= 2:
+ size = 2 + struct.unpack("!H", self.sendqueue[:2])[0]
+ else:
+ size = 0
+ result = self.sendqueue[:size]
+ self.sendqueue = self.sendqueue[size:]
+ return result
+
+ def read_msg(self):
+ sent_data = self.readsent()
+ get_msg = Message(Message.PARSE)
+ get_msg.from_wire(bytes(sent_data[2:]))
+ return get_msg
+
+ def clear_send(self):
+ del self.sendqueue[:]
+
+# We subclass the Session class we're testing here, only
+# to override the handle() and _send_data() method
+class MyXfroutSession(XfroutSession):
+ def handle(self):
+ pass
+
+ def _send_data(self, sock, data):
+ size = len(data)
+ total_count = 0
+ while total_count < size:
+ count = sock.send(data[total_count:])
+ total_count += count
+
+class Dbserver:
+ def __init__(self):
+ self._shutdown_event = threading.Event()
+ def get_db_file(self):
+ return None
+ def decrease_transfers_counter(self):
+ pass
+
+class TestXfroutSession(unittest.TestCase):
+ def getmsg(self):
+ msg = Message(Message.PARSE)
+ msg.from_wire(self.mdata)
+ return msg
+
+ def create_mock_tsig_ctx(self, error):
+ # This helper function creates a MockTSIGContext for a given key
+ # and TSIG error to be used as a result of verify (normally faked
+ # one)
+ mock_ctx = MockTSIGContext(TSIG_KEY)
+ mock_ctx.error = error
+ return mock_ctx
+
+ def message_has_tsig(self, msg):
+ return msg.get_tsig_record() is not None
+
+ def create_request_data_with_tsig(self):
+ msg = Message(Message.RENDER)
+ query_id = 0x1035
+ msg.set_qid(query_id)
+ msg.set_opcode(Opcode.QUERY())
+ msg.set_rcode(Rcode.NOERROR())
+ query_question = Question(Name("example.com."), RRClass.IN(), RRType.AXFR())
+ msg.add_question(query_question)
+
+ renderer = MessageRenderer()
+ tsig_ctx = MockTSIGContext(TSIG_KEY)
+ msg.to_wire(renderer, tsig_ctx)
+ reply_data = renderer.get_data()
+ return reply_data
+
+ def setUp(self):
+ self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM)
+ #self.log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False )
+ self.xfrsess = MyXfroutSession(self.sock, None, Dbserver(), TSIGKeyRing())
+ self.mdata = bytes(b'\xd6=\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01')
+ self.soa_record = (4, 3, 'example.com.', 'com.example.', 3600, 'SOA', None, 'master.example.com. admin.example.com. 1234 3600 1800 2419200 7200')
+
+ def test_parse_query_message(self):
+ [get_rcode, get_msg] = self.xfrsess._parse_query_message(self.mdata)
+ self.assertEqual(get_rcode.to_text(), "NOERROR")
+
+ # tsig signed query message
+ request_data = self.create_request_data_with_tsig()
+ # BADKEY
+ [rcode, msg] = self.xfrsess._parse_query_message(request_data)
+ self.assertEqual(rcode.to_text(), "NOTAUTH")
+ self.assertTrue(self.xfrsess._tsig_ctx is not None)
+ # NOERROR
+ self.xfrsess._tsig_key_ring.add(TSIG_KEY)
+ [rcode, msg] = self.xfrsess._parse_query_message(request_data)
+ self.assertEqual(rcode.to_text(), "NOERROR")
+ self.assertTrue(self.xfrsess._tsig_ctx is not None)
+
+ def test_get_query_zone_name(self):
+ msg = self.getmsg()
+ self.assertEqual(self.xfrsess._get_query_zone_name(msg), "example.com.")
+
+ def test_send_data(self):
+ self.xfrsess._send_data(self.sock, self.mdata)
+ senddata = self.sock.readsent()
+ self.assertEqual(senddata, self.mdata)
+
+ def test_reply_xfrout_query_with_error_rcode(self):
+ msg = self.getmsg()
+ self.xfrsess._reply_query_with_error_rcode(msg, self.sock, Rcode(3))
+ get_msg = self.sock.read_msg()
+ self.assertEqual(get_msg.get_rcode().to_text(), "NXDOMAIN")
+
+ # tsig signed message
+ msg = self.getmsg()
+ self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
+ self.xfrsess._reply_query_with_error_rcode(msg, self.sock, Rcode(3))
+ get_msg = self.sock.read_msg()
+ self.assertEqual(get_msg.get_rcode().to_text(), "NXDOMAIN")
+ self.assertTrue(self.message_has_tsig(get_msg))
+
+ def test_send_message(self):
+ msg = self.getmsg()
+ msg.make_response()
+ # soa record data with different cases
+ soa_record = (4, 3, 'Example.com.', 'com.Example.', 3600, 'SOA', None, 'master.Example.com. admin.exAmple.com. 1234 3600 1800 2419200 7200')
+ rrset_soa = self.xfrsess._create_rrset_from_db_record(soa_record)
+ msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
+ self.xfrsess._send_message(self.sock, msg)
+ send_out_data = self.sock.readsent()[2:]
+
+ # CASE_INSENSITIVE compression mode
+ render = MessageRenderer();
+ render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
+ msg.to_wire(render)
+ self.assertNotEqual(render.get_data(), send_out_data)
+
+ # CASE_SENSITIVE compression mode
+ render.clear()
+ render.set_compress_mode(MessageRenderer.CASE_SENSITIVE)
+ render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
+ msg.to_wire(render)
+ self.assertEqual(render.get_data(), send_out_data)
+
+ def test_clear_message(self):
+ msg = self.getmsg()
+ qid = msg.get_qid()
+ opcode = msg.get_opcode()
+ rcode = msg.get_rcode()
+
+ self.xfrsess._clear_message(msg)
+ self.assertEqual(msg.get_qid(), qid)
+ self.assertEqual(msg.get_opcode(), opcode)
+ self.assertEqual(msg.get_rcode(), rcode)
+ self.assertTrue(msg.get_header_flag(Message.HEADERFLAG_AA))
+
+ def test_reply_query_with_format_error(self):
+ msg = self.getmsg()
+ self.xfrsess._reply_query_with_format_error(msg, self.sock)
+ get_msg = self.sock.read_msg()
+ self.assertEqual(get_msg.get_rcode().to_text(), "FORMERR")
+
+ # tsig signed message
+ msg = self.getmsg()
+ self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
+ self.xfrsess._reply_query_with_format_error(msg, self.sock)
+ get_msg = self.sock.read_msg()
+ self.assertEqual(get_msg.get_rcode().to_text(), "FORMERR")
+ self.assertTrue(self.message_has_tsig(get_msg))
+
+ def test_create_rrset_from_db_record(self):
+ rrset = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+ self.assertEqual(rrset.get_name().to_text(), "example.com.")
+ self.assertEqual(rrset.get_class(), RRClass("IN"))
+ self.assertEqual(rrset.get_type().to_text(), "SOA")
+ rdata = rrset.get_rdata()
+ self.assertEqual(rdata[0].to_text(), self.soa_record[7])
+
+ def test_send_message_with_last_soa(self):
+ rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+ msg = self.getmsg()
+ msg.make_response()
+
+ # packet number less than TSIG_SIGN_EVERY_NTH
+ packet_neet_not_sign = xfrout.TSIG_SIGN_EVERY_NTH - 1
+ self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa,
+ 0, packet_neet_not_sign)
+ get_msg = self.sock.read_msg()
+ # tsig context is not exist
+ self.assertFalse(self.message_has_tsig(get_msg))
+
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
+
+ #answer_rrset_iter = section_iter(get_msg, section.ANSWER())
+ answer = get_msg.get_section(Message.SECTION_ANSWER)[0]#answer_rrset_iter.get_rrset()
+ self.assertEqual(answer.get_name().to_text(), "example.com.")
+ self.assertEqual(answer.get_class(), RRClass("IN"))
+ self.assertEqual(answer.get_type().to_text(), "SOA")
+ rdata = answer.get_rdata()
+ self.assertEqual(rdata[0].to_text(), self.soa_record[7])
+
+ # msg is the TSIG_SIGN_EVERY_NTH one
+ # sending the message with last soa together
+ self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa,
+ 0, TSIG_SIGN_EVERY_NTH)
+ get_msg = self.sock.read_msg()
+ # tsig context is not exist
+ self.assertFalse(self.message_has_tsig(get_msg))
+
+ def test_send_message_with_last_soa_with_tsig(self):
+ # create tsig context
+ self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
+
+ rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+ msg = self.getmsg()
+ msg.make_response()
+
+ # packet number less than TSIG_SIGN_EVERY_NTH
+ packet_neet_not_sign = xfrout.TSIG_SIGN_EVERY_NTH - 1
+ # msg is not the TSIG_SIGN_EVERY_NTH one
+ # sending the message with last soa together
+ self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa,
+ 0, packet_neet_not_sign)
+ get_msg = self.sock.read_msg()
+ self.assertTrue(self.message_has_tsig(get_msg))
+
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
+
+ # msg is the TSIG_SIGN_EVERY_NTH one
+ # sending the message with last soa together
+ self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa,
+ 0, TSIG_SIGN_EVERY_NTH)
+ get_msg = self.sock.read_msg()
+ self.assertTrue(self.message_has_tsig(get_msg))
+
+ def test_trigger_send_message_with_last_soa(self):
+ rrset_a = RRset(Name("example.com"), RRClass.IN(), RRType.A(), RRTTL(3600))
+ rrset_a.add_rdata(Rdata(RRType.A(), RRClass.IN(), "192.0.2.1"))
+ rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+
+ msg = self.getmsg()
+ msg.make_response()
+ msg.add_rrset(Message.SECTION_ANSWER, rrset_a)
+
+ # length larger than MAX-len(rrset)
+ length_need_split = xfrout.XFROUT_MAX_MESSAGE_SIZE - get_rrset_len(rrset_soa) + 1
+ # packet number less than TSIG_SIGN_EVERY_NTH
+ packet_neet_not_sign = xfrout.TSIG_SIGN_EVERY_NTH - 1
+
+ # give the function a value that is larger than MAX-len(rrset)
+ # this should have triggered the sending of two messages
+ # (1 with the rrset we added manually, and 1 that triggered
+ # the sending in _with_last_soa)
+ self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, length_need_split,
+ packet_neet_not_sign)
+ get_msg = self.sock.read_msg()
+ self.assertFalse(self.message_has_tsig(get_msg))
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
+
+ answer = get_msg.get_section(Message.SECTION_ANSWER)[0]
+ self.assertEqual(answer.get_name().to_text(), "example.com.")
+ self.assertEqual(answer.get_class(), RRClass("IN"))
+ self.assertEqual(answer.get_type().to_text(), "A")
+ rdata = answer.get_rdata()
+ self.assertEqual(rdata[0].to_text(), "192.0.2.1")
+
+ get_msg = self.sock.read_msg()
+ self.assertFalse(self.message_has_tsig(get_msg))
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 0)
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
+ self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
+
+ #answer_rrset_iter = section_iter(get_msg, Message.SECTION_ANSWER)
+ answer = get_msg.get_section(Message.SECTION_ANSWER)[0]
+ self.assertEqual(answer.get_name().to_text(), "example.com.")
+ self.assertEqual(answer.get_class(), RRClass("IN"))
+ self.assertEqual(answer.get_type().to_text(), "SOA")
+ rdata = answer.get_rdata()
+ self.assertEqual(rdata[0].to_text(), self.soa_record[7])
+
+ # and it should not have sent anything else
+ self.assertEqual(0, len(self.sock.sendqueue))
+
+ def test_trigger_send_message_with_last_soa_with_tsig(self):
+ self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
+ rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+ msg = self.getmsg()
+ msg.make_response()
+ msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
+
+ # length larger than MAX-len(rrset)
+ length_need_split = xfrout.XFROUT_MAX_MESSAGE_SIZE - get_rrset_len(rrset_soa) + 1
+ # packet number less than TSIG_SIGN_EVERY_NTH
+ packet_neet_not_sign = xfrout.TSIG_SIGN_EVERY_NTH - 1
+
+ # give the function a value that is larger than MAX-len(rrset)
+ # this should have triggered the sending of two messages
+ # (1 with the rrset we added manually, and 1 that triggered
+ # the sending in _with_last_soa)
+ self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, length_need_split,
+ packet_neet_not_sign)
+ get_msg = self.sock.read_msg()
+ # msg is not the TSIG_SIGN_EVERY_NTH one, it shouldn't be tsig signed
+ self.assertFalse(self.message_has_tsig(get_msg))
+ # the last packet should be tsig signed
+ get_msg = self.sock.read_msg()
+ self.assertTrue(self.message_has_tsig(get_msg))
+ # and it should not have sent anything else
+ self.assertEqual(0, len(self.sock.sendqueue))
+
+
+ # msg is the TSIG_SIGN_EVERY_NTH one, it should be tsig signed
+ self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, length_need_split,
+ xfrout.TSIG_SIGN_EVERY_NTH)
+ get_msg = self.sock.read_msg()
+ self.assertTrue(self.message_has_tsig(get_msg))
+ # the last packet should be tsig signed
+ get_msg = self.sock.read_msg()
+ self.assertTrue(self.message_has_tsig(get_msg))
+ # and it should not have sent anything else
+ self.assertEqual(0, len(self.sock.sendqueue))
+
+ def test_get_rrset_len(self):
+ rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+ self.assertEqual(82, get_rrset_len(rrset_soa))
+
+ def test_zone_has_soa(self):
+ global sqlite3_ds
+ def mydb1(zone, file):
+ return True
+ sqlite3_ds.get_zone_soa = mydb1
+ self.assertTrue(self.xfrsess._zone_has_soa(""))
+ def mydb2(zone, file):
+ return False
+ sqlite3_ds.get_zone_soa = mydb2
+ self.assertFalse(self.xfrsess._zone_has_soa(""))
+
+ def test_zone_exist(self):
+ global sqlite3_ds
+ def zone_exist(zone, file):
+ return zone
+ sqlite3_ds.zone_exist = zone_exist
+ self.assertTrue(self.xfrsess._zone_exist(True))
+ self.assertFalse(self.xfrsess._zone_exist(False))
+
+ def test_check_xfrout_available(self):
+ def zone_exist(zone):
+ return zone
+ def zone_has_soa(zone):
+ return (not zone)
+ self.xfrsess._zone_exist = zone_exist
+ self.xfrsess._zone_has_soa = zone_has_soa
+ self.assertEqual(self.xfrsess._check_xfrout_available(False).to_text(), "NOTAUTH")
+ self.assertEqual(self.xfrsess._check_xfrout_available(True).to_text(), "SERVFAIL")
+
+ def zone_empty(zone):
+ return zone
+ self.xfrsess._zone_has_soa = zone_empty
+ def false_func():
+ return False
+ self.xfrsess._server.increase_transfers_counter = false_func
+ self.assertEqual(self.xfrsess._check_xfrout_available(True).to_text(), "REFUSED")
+ def true_func():
+ return True
+ self.xfrsess._server.increase_transfers_counter = true_func
+ self.assertEqual(self.xfrsess._check_xfrout_available(True).to_text(), "NOERROR")
+
+ def test_dns_xfrout_start_formerror(self):
+ # formerror
+ self.xfrsess.dns_xfrout_start(self.sock, b"\xd6=\x00\x00\x00\x01\x00")
+ sent_data = self.sock.readsent()
+ self.assertEqual(len(sent_data), 0)
+
+ def default(self, param):
+ return "example.com"
+
+ def test_dns_xfrout_start_notauth(self):
+ self.xfrsess._get_query_zone_name = self.default
+ def notauth(formpara):
+ return Rcode.NOTAUTH()
+ self.xfrsess._check_xfrout_available = notauth
+ self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
+ get_msg = self.sock.read_msg()
+ self.assertEqual(get_msg.get_rcode().to_text(), "NOTAUTH")
+
+ def test_dns_xfrout_start_noerror(self):
+ self.xfrsess._get_query_zone_name = self.default
+ def noerror(form):
+ return Rcode.NOERROR()
+ self.xfrsess._check_xfrout_available = noerror
+
+ def myreply(msg, sock, zonename):
+ self.sock.send(b"success")
+
+ self.xfrsess._reply_xfrout_query = myreply
+ self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
+ self.assertEqual(self.sock.readsent(), b"success")
+
+ def test_reply_xfrout_query_noerror(self):
+ global sqlite3_ds
+ def get_zone_soa(zonename, file):
+ return self.soa_record
+
+ def get_zone_datas(zone, file):
+ return [self.soa_record]
+
+ sqlite3_ds.get_zone_soa = get_zone_soa
+ sqlite3_ds.get_zone_datas = get_zone_datas
+ self.xfrsess._reply_xfrout_query(self.getmsg(), self.sock, "example.com.")
+ reply_msg = self.sock.read_msg()
+ self.assertEqual(reply_msg.get_rr_count(Message.SECTION_ANSWER), 2)
+
+ def test_reply_xfrout_query_noerror_with_tsig(self):
+ rrset_data = (4, 3, 'a.example.com.', 'com.example.', 3600, 'A', None, '192.168.1.1')
+ global sqlite3_ds
+ global xfrout
+ def get_zone_soa(zonename, file):
+ return self.soa_record
+
+ def get_zone_datas(zone, file):
+ zone_rrsets = []
+ for i in range(0, 100):
+ zone_rrsets.insert(i, rrset_data)
+ return zone_rrsets
+
+ def get_rrset_len(rrset):
+ return 65520
+
+ sqlite3_ds.get_zone_soa = get_zone_soa
+ sqlite3_ds.get_zone_datas = get_zone_datas
+ xfrout.get_rrset_len = get_rrset_len
+
+ self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
+ self.xfrsess._reply_xfrout_query(self.getmsg(), self.sock, "example.com.")
+
+ # tsig signed first package
+ reply_msg = self.sock.read_msg()
+ self.assertEqual(reply_msg.get_rr_count(Message.SECTION_ANSWER), 1)
+ self.assertTrue(self.message_has_tsig(reply_msg))
+ # (TSIG_SIGN_EVERY_NTH - 1) packets have no tsig
+ for i in range(0, xfrout.TSIG_SIGN_EVERY_NTH - 1):
+ reply_msg = self.sock.read_msg()
+ self.assertFalse(self.message_has_tsig(reply_msg))
+ # TSIG_SIGN_EVERY_NTH packet has tsig
+ reply_msg = self.sock.read_msg()
+ self.assertTrue(self.message_has_tsig(reply_msg))
+
+ for i in range(0, 100 - TSIG_SIGN_EVERY_NTH):
+ reply_msg = self.sock.read_msg()
+ self.assertFalse(self.message_has_tsig(reply_msg))
+ # tsig signed last package
+ reply_msg = self.sock.read_msg()
+ self.assertTrue(self.message_has_tsig(reply_msg))
+
+ # and it should not have sent anything else
+ self.assertEqual(0, len(self.sock.sendqueue))
+
+class MyCCSession():
+ def __init__(self):
+ pass
+
+ def get_remote_config_value(self, module_name, identifier):
+ if module_name == "Auth" and identifier == "database_file":
+ return "initdb.file", False
+ else:
+ return "unknown", False
+
+
+class MyUnixSockServer(UnixSockServer):
+ def __init__(self):
+ self._lock = threading.Lock()
+ self._transfers_counter = 0
+ self._shutdown_event = threading.Event()
+ self._max_transfers_out = 10
+ self._cc = MyCCSession()
+ #self._log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False )
+
+class TestUnixSockServer(unittest.TestCase):
+ def setUp(self):
+ self.write_sock, self.read_sock = socket.socketpair()
+ self.unix = MyUnixSockServer()
+
+ def test_receive_query_message(self):
+ send_msg = b"\xd6=\x00\x00\x00\x01\x00"
+ msg_len = struct.pack('H', socket.htons(len(send_msg)))
+ self.write_sock.send(msg_len)
+ self.write_sock.send(send_msg)
+ recv_msg = self.unix._receive_query_message(self.read_sock)
+ self.assertEqual(recv_msg, send_msg)
+
+ def test_updata_config_data(self):
+ tsig_key_str = 'example.com:SFuWd/q99SzF8Yzd1QbB9g=='
+ tsig_key_list = [tsig_key_str]
+ bad_key_list = ['bad..example.com:SFuWd/q99SzF8Yzd1QbB9g==']
+ self.unix.update_config_data({'transfers_out':10 })
+ self.assertEqual(self.unix._max_transfers_out, 10)
+ self.assertTrue(self.unix.tsig_key_ring is not None)
+
+ self.unix.update_config_data({'transfers_out':9, 'tsig_key_ring':tsig_key_list})
+ self.assertEqual(self.unix._max_transfers_out, 9)
+ self.assertEqual(self.unix.tsig_key_ring.size(), 1)
+ self.unix.tsig_key_ring.remove(Name("example.com."))
+ self.assertEqual(self.unix.tsig_key_ring.size(), 0)
+
+ # bad tsig key
+ config_data = {'transfers_out':9, 'tsig_key_ring': bad_key_list}
+ self.assertRaises(None, self.unix.update_config_data(config_data))
+ self.assertEqual(self.unix.tsig_key_ring.size(), 0)
+
+ def test_get_db_file(self):
+ self.assertEqual(self.unix.get_db_file(), "initdb.file")
+
+ def test_increase_transfers_counter(self):
+ self.unix._max_transfers_out = 10
+ count = self.unix._transfers_counter
+ self.assertEqual(self.unix.increase_transfers_counter(), True)
+ self.assertEqual(count + 1, self.unix._transfers_counter)
+
+ self.unix._max_transfers_out = 0
+ count = self.unix._transfers_counter
+ self.assertEqual(self.unix.increase_transfers_counter(), False)
+ self.assertEqual(count, self.unix._transfers_counter)
+
+ def test_decrease_transfers_counter(self):
+ count = self.unix._transfers_counter
+ self.unix.decrease_transfers_counter()
+ self.assertEqual(count - 1, self.unix._transfers_counter)
+
+ def _remove_file(self, sock_file):
+ try:
+ os.remove(sock_file)
+ except OSError:
+ pass
+
+ def test_sock_file_in_use_file_exist(self):
+ sock_file = 'temp.sock.file'
+ self._remove_file(sock_file)
+ self.assertFalse(self.unix._sock_file_in_use(sock_file))
+ self.assertFalse(os.path.exists(sock_file))
+
+ def test_sock_file_in_use_file_not_exist(self):
+ self.assertFalse(self.unix._sock_file_in_use('temp.sock.file'))
+
+ def _start_unix_sock_server(self, sock_file):
+ serv = ThreadingUnixStreamServer(sock_file, BaseRequestHandler)
+ serv_thread = threading.Thread(target=serv.serve_forever)
+ serv_thread.setDaemon(True)
+ serv_thread.start()
+
+ def test_sock_file_in_use(self):
+ sock_file = 'temp.sock.file'
+ self._remove_file(sock_file)
+ self.assertFalse(self.unix._sock_file_in_use(sock_file))
+ self._start_unix_sock_server(sock_file)
+
+ old_stdout = sys.stdout
+ sys.stdout = open(os.devnull, 'w')
+ self.assertTrue(self.unix._sock_file_in_use(sock_file))
+ sys.stdout = old_stdout
+
+ def test_remove_unused_sock_file_in_use(self):
+ sock_file = 'temp.sock.file'
+ self._remove_file(sock_file)
+ self.assertFalse(self.unix._sock_file_in_use(sock_file))
+ self._start_unix_sock_server(sock_file)
+ old_stdout = sys.stdout
+ sys.stdout = open(os.devnull, 'w')
+ try:
+ self.unix._remove_unused_sock_file(sock_file)
+ except SystemExit:
+ pass
+ else:
+ # This should never happen
+ self.assertTrue(False)
+
+ sys.stdout = old_stdout
+
+ def test_remove_unused_sock_file_dir(self):
+ import tempfile
+ dir_name = tempfile.mkdtemp()
+ old_stdout = sys.stdout
+ sys.stdout = open(os.devnull, 'w')
+ try:
+ self.unix._remove_unused_sock_file(dir_name)
+ except SystemExit:
+ pass
+ else:
+ # This should never happen
+ self.assertTrue(False)
+
+ sys.stdout = old_stdout
+ os.rmdir(dir_name)
+
+class TestInitialization(unittest.TestCase):
+ def setEnv(self, name, value):
+ if value is None:
+ if name in os.environ:
+ del os.environ[name]
+ else:
+ os.environ[name] = value
+
+ def setUp(self):
+ self._oldSocket = os.getenv("BIND10_XFROUT_SOCKET_FILE")
+ self._oldFromBuild = os.getenv("B10_FROM_BUILD")
+
+ def tearDown(self):
+ self.setEnv("B10_FROM_BUILD", self._oldFromBuild)
+ self.setEnv("BIND10_XFROUT_SOCKET_FILE", self._oldSocket)
+ # Make sure even the computed values are back
+ xfrout.init_paths()
+
+ def testNoEnv(self):
+ self.setEnv("B10_FROM_BUILD", None)
+ self.setEnv("BIND10_XFROUT_SOCKET_FILE", None)
+ xfrout.init_paths()
+ self.assertEqual(xfrout.UNIX_SOCKET_FILE,
+ "@@LOCALSTATEDIR@@/auth_xfrout_conn")
+
+ def testProvidedSocket(self):
+ self.setEnv("B10_FROM_BUILD", None)
+ self.setEnv("BIND10_XFROUT_SOCKET_FILE", "The/Socket/File")
+ xfrout.init_paths()
+ self.assertEqual(xfrout.UNIX_SOCKET_FILE, "The/Socket/File")
+
+if __name__== "__main__":
+ unittest.main()
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index f420d4b..a75ff22 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -1,7 +1,6 @@
#!@PYTHON@
# Copyright (C) 2010 Internet Systems Consortium.
-# Copyright (C) 2010 CZ NIC
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -27,7 +26,6 @@ from isc.datasrc import sqlite3_ds
from socketserver import *
import os
from isc.config.ccsession import *
-from isc.log.log import *
from isc.cc import SessionError, SessionTimeout
from isc.notify import notify_out
import isc.util.process
@@ -37,39 +35,55 @@ import errno
from optparse import OptionParser, OptionValueError
from isc.util import socketserver_mixin
+from xfrout_messages import *
+
+isc.log.init("b10-xfrout")
+logger = isc.log.Logger("xfrout")
+
try:
- from libxfr_python import *
+ from libutil_io_python import *
from pydnspp import *
except ImportError as e:
# C++ loadable module may not be installed; even so the xfrout process
# must keep running, so we warn about it and move forward.
- sys.stderr.write('[b10-xfrout] failed to import DNS or XFR module: %s\n' % str(e))
+ log.error(XFROUT_IMPORT, str(e))
isc.util.process.rename()
-if "B10_FROM_BUILD" in os.environ:
- SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/xfrout"
- AUTH_SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/auth"
- if "B10_FROM_SOURCE_LOCALSTATEDIR" in os.environ:
- UNIX_SOCKET_FILE = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"] + \
- "/auth_xfrout_conn"
+def init_paths():
+ global SPECFILE_PATH
+ global AUTH_SPECFILE_PATH
+ global UNIX_SOCKET_FILE
+ if "B10_FROM_BUILD" in os.environ:
+ SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/xfrout"
+ AUTH_SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/auth"
+ if "B10_FROM_SOURCE_LOCALSTATEDIR" in os.environ:
+ UNIX_SOCKET_FILE = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"] + \
+ "/auth_xfrout_conn"
+ else:
+ UNIX_SOCKET_FILE = os.environ["B10_FROM_BUILD"] + "/auth_xfrout_conn"
else:
- UNIX_SOCKET_FILE = os.environ["B10_FROM_BUILD"] + "/auth_xfrout_conn"
-else:
- PREFIX = "@prefix@"
- DATAROOTDIR = "@datarootdir@"
- SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
- AUTH_SPECFILE_PATH = SPECFILE_PATH
- UNIX_SOCKET_FILE = "@@LOCALSTATEDIR@@/auth_xfrout_conn"
+ PREFIX = "@prefix@"
+ DATAROOTDIR = "@datarootdir@"
+ SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
+ AUTH_SPECFILE_PATH = SPECFILE_PATH
+ if "BIND10_XFROUT_SOCKET_FILE" in os.environ:
+ UNIX_SOCKET_FILE = os.environ["BIND10_XFROUT_SOCKET_FILE"]
+ else:
+ UNIX_SOCKET_FILE = "@@LOCALSTATEDIR@@/auth_xfrout_conn"
+
+init_paths()
SPECFILE_LOCATION = SPECFILE_PATH + "/xfrout.spec"
AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + os.sep + "auth.spec"
MAX_TRANSFERS_OUT = 10
VERBOSE_MODE = False
-
+# tsig sign every N axfr packets.
+TSIG_SIGN_EVERY_NTH = 96
XFROUT_MAX_MESSAGE_SIZE = 65535
+
def get_rrset_len(rrset):
"""Returns the wire length of the given RRset"""
bytes = bytearray()
@@ -78,41 +92,68 @@ def get_rrset_len(rrset):
class XfroutSession():
- def __init__(self, sock_fd, request_data, server, log):
+ def __init__(self, sock_fd, request_data, server, tsig_key_ring):
# The initializer for the superclass may call functions
# that need _log to be set, so we set it first
self._sock_fd = sock_fd
self._request_data = request_data
self._server = server
- self._log = log
+ #self._log = log
+ self._tsig_key_ring = tsig_key_ring
+ self._tsig_ctx = None
+ self._tsig_len = 0
self.handle()
+ def create_tsig_ctx(self, tsig_record, tsig_key_ring):
+ return TSIGContext(tsig_record.get_name(), tsig_record.get_rdata().get_algorithm(),
+ tsig_key_ring)
+
def handle(self):
''' Handle a xfrout query, send xfrout response '''
try:
self.dns_xfrout_start(self._sock_fd, self._request_data)
#TODO, avoid catching all exceptions
except Exception as e:
- self._log.log_message("error", str(e))
+ logger.error(XFROUT_HANDLE_QUERY_ERROR, str(e))
+ pass
os.close(self._sock_fd)
+ def _check_request_tsig(self, msg, request_data):
+ ''' If request has a tsig record, perform tsig related checks '''
+ tsig_record = msg.get_tsig_record()
+ if tsig_record is not None:
+ self._tsig_len = tsig_record.get_length()
+ self._tsig_ctx = self.create_tsig_ctx(tsig_record, self._tsig_key_ring)
+ tsig_error = self._tsig_ctx.verify(tsig_record, request_data)
+ if tsig_error != TSIGError.NOERROR:
+ return Rcode.NOTAUTH()
+
+ return Rcode.NOERROR()
+
def _parse_query_message(self, mdata):
''' parse query message to [socket,message]'''
#TODO, need to add parseHeader() in case the message header is invalid
try:
msg = Message(Message.PARSE)
Message.from_wire(msg, mdata)
+
+ # TSIG related checks
+ rcode = self._check_request_tsig(msg, mdata)
+
except Exception as err:
- self._log.log_message("error", str(err))
+ logger.error(XFROUT_PARSE_QUERY_ERROR, str(err))
return Rcode.FORMERR(), None
- return Rcode.NOERROR(), msg
+ return rcode, msg
def _get_query_zone_name(self, msg):
question = msg.get_question()[0]
return question.get_name().to_text()
+ def _get_query_zone_class(self, msg):
+ question = msg.get_question()[0]
+ return question.get_class().to_text()
def _send_data(self, sock_fd, data):
size = len(data)
@@ -122,13 +163,20 @@ class XfroutSession():
total_count += count
- def _send_message(self, sock_fd, msg):
+ def _send_message(self, sock_fd, msg, tsig_ctx=None):
render = MessageRenderer()
# As defined in RFC5936 section3.4, perform case-preserving name
# compression for AXFR message.
render.set_compress_mode(MessageRenderer.CASE_SENSITIVE)
render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
- msg.to_wire(render)
+
+ # 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)
+
header_len = struct.pack('H', socket.htons(render.get_length()))
self._send_data(sock_fd, header_len)
self._send_data(sock_fd, render.get_data())
@@ -137,7 +185,7 @@ class XfroutSession():
def _reply_query_with_error_rcode(self, msg, sock_fd, rcode_):
msg.make_response()
msg.set_rcode(rcode_)
- self._send_message(sock_fd, msg)
+ self._send_message(sock_fd, msg, self._tsig_ctx)
def _reply_query_with_format_error(self, msg, sock_fd):
@@ -147,7 +195,7 @@ class XfroutSession():
msg.make_response()
msg.set_rcode(Rcode.FORMERR())
- self._send_message(sock_fd, msg)
+ self._send_message(sock_fd, msg, self._tsig_ctx)
def _zone_has_soa(self, zone):
'''Judge if the zone has an SOA record.'''
@@ -196,22 +244,29 @@ class XfroutSession():
def dns_xfrout_start(self, sock_fd, msg_query):
rcode_, msg = self._parse_query_message(msg_query)
#TODO. create query message and parse header
- if rcode_ != Rcode.NOERROR():
+ if rcode_ == Rcode.NOTAUTH():
+ return self._reply_query_with_error_rcode(msg, sock_fd, rcode_)
+ elif rcode_ != Rcode.NOERROR():
return self._reply_query_with_format_error(msg, sock_fd)
zone_name = self._get_query_zone_name(msg)
+ zone_class_str = self._get_query_zone_class(msg)
+ # TODO: should we not also include class in the check?
rcode_ = self._check_xfrout_available(zone_name)
+
if rcode_ != Rcode.NOERROR():
- self._log.log_message("info", "transfer of '%s/IN' failed: %s",
- zone_name, rcode_.to_text())
+ logger.info(XFROUT_AXFR_TRANSFER_FAILED, zone_name,
+ zone_class_str, rcode_.to_text())
return self. _reply_query_with_error_rcode(msg, sock_fd, rcode_)
try:
- self._log.log_message("info", "transfer of '%s/IN': AXFR started" % zone_name)
+ logger.info(XFROUT_AXFR_TRANSFER_STARTED, zone_name, zone_class_str)
self._reply_xfrout_query(msg, sock_fd, zone_name)
- self._log.log_message("info", "transfer of '%s/IN': AXFR end" % zone_name)
except Exception as err:
- self._log.log_message("error", str(err))
+ logger.error(XFROUT_AXFR_TRANSFER_ERROR, zone_name,
+ zone_class_str, str(err))
+ pass
+ logger.info(XFROUT_AXFR_TRANSFER_DONE, zone_name, zone_class_str)
self._server.decrease_transfers_counter()
return
@@ -240,37 +295,43 @@ class XfroutSession():
rrset_.add_rdata(rdata_)
return rrset_
- def _send_message_with_last_soa(self, msg, sock_fd, rrset_soa, message_upper_len):
+ def _send_message_with_last_soa(self, msg, sock_fd, rrset_soa, message_upper_len,
+ count_since_last_tsig_sign):
'''Add the SOA record to the end of message. If it can't be
added, a new message should be created to send out the last soa .
'''
rrset_len = get_rrset_len(rrset_soa)
- if message_upper_len + rrset_len < XFROUT_MAX_MESSAGE_SIZE:
- msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
- else:
+ if (count_since_last_tsig_sign == TSIG_SIGN_EVERY_NTH and
+ message_upper_len + rrset_len >= XFROUT_MAX_MESSAGE_SIZE):
+ # If tsig context exist, sign the packet with serial number TSIG_SIGN_EVERY_NTH
+ self._send_message(sock_fd, msg, self._tsig_ctx)
+ msg = self._clear_message(msg)
+ elif (count_since_last_tsig_sign != TSIG_SIGN_EVERY_NTH and
+ message_upper_len + rrset_len + self._tsig_len >= XFROUT_MAX_MESSAGE_SIZE):
self._send_message(sock_fd, msg)
msg = self._clear_message(msg)
- msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
- self._send_message(sock_fd, msg)
+ # If tsig context exist, sign the last packet
+ msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
+ self._send_message(sock_fd, msg, self._tsig_ctx)
def _reply_xfrout_query(self, msg, sock_fd, zone_name):
#TODO, there should be a better way to insert rrset.
+ count_since_last_tsig_sign = TSIG_SIGN_EVERY_NTH
msg.make_response()
msg.set_header_flag(Message.HEADERFLAG_AA)
soa_record = sqlite3_ds.get_zone_soa(zone_name, self._server.get_db_file())
rrset_soa = self._create_rrset_from_db_record(soa_record)
msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
- message_upper_len = get_rrset_len(rrset_soa)
+ message_upper_len = get_rrset_len(rrset_soa) + self._tsig_len
for rr_data in sqlite3_ds.get_zone_datas(zone_name, self._server.get_db_file()):
if self._server._shutdown_event.is_set(): # Check if xfrout is shutdown
- self._log.log_message("info", "xfrout process is being shutdown")
+ logger.info(XFROUT_STOPPING)
return
-
# TODO: RRType.SOA() ?
if RRType(rr_data[5]) == RRType("SOA"): #ignore soa record
continue
@@ -286,17 +347,30 @@ class XfroutSession():
message_upper_len += rrset_len
continue
- self._send_message(sock_fd, msg)
+ # If tsig context exist, sign every N packets
+ if count_since_last_tsig_sign == TSIG_SIGN_EVERY_NTH:
+ count_since_last_tsig_sign = 0
+ self._send_message(sock_fd, msg, self._tsig_ctx)
+ else:
+ self._send_message(sock_fd, msg)
+
+ count_since_last_tsig_sign += 1
msg = self._clear_message(msg)
msg.add_rrset(Message.SECTION_ANSWER, rrset_) # Add the rrset to the new message
- message_upper_len = rrset_len
- self._send_message_with_last_soa(msg, sock_fd, rrset_soa, message_upper_len)
+ # Reserve tsig space for signed packet
+ if count_since_last_tsig_sign == TSIG_SIGN_EVERY_NTH:
+ message_upper_len = rrset_len + self._tsig_len
+ else:
+ message_upper_len = rrset_len
+
+ self._send_message_with_last_soa(msg, sock_fd, rrset_soa, message_upper_len,
+ count_since_last_tsig_sign)
class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
'''The unix domain socket server which accept xfr query sent from auth server.'''
- def __init__(self, sock_file, handle_class, shutdown_event, config_data, cc, log):
+ def __init__(self, sock_file, handle_class, shutdown_event, config_data, cc):
self._remove_unused_sock_file(sock_file)
self._sock_file = sock_file
socketserver_mixin.NoPollMixIn.__init__(self)
@@ -305,7 +379,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
self._transfers_counter = 0
self._shutdown_event = shutdown_event
self._write_sock, self._read_sock = socket.socketpair()
- self._log = log
+ #self._log = log
self.update_config_data(config_data)
self._cc = cc
@@ -333,7 +407,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
try:
request, client_address = self.get_request()
except socket.error:
- self._log.log_message("error", "Failed to fetch request")
+ logger.error(XFROUT_FETCH_REQUEST_ERROR)
return
# Check self._shutdown_event to ensure the real shutdown comes.
@@ -347,7 +421,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
(rlist, wlist, xlist) = ([], [], [])
continue
else:
- self._log.log_message("error", "Error with select(): %s" %e)
+ logger.error(XFROUT_SOCKET_SELECT_ERROR, str(e))
break
# self.server._shutdown_event will be set by now, if it is not a false
@@ -357,9 +431,8 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
try:
self.process_request(request)
- except:
- self._log.log_message("error", "Exception happened during processing of %s"
- % str(client_address))
+ except Exception as pre:
+ log.error(XFROUT_PROCESS_REQUEST_ERROR, str(pre))
break
def _handle_request_noblock(self):
@@ -377,8 +450,8 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
# This may happen when one xfrout process try to connect to
# xfrout unix socket server, to check whether there is another
# xfrout running.
- if sock_fd == XFR_FD_RECEIVE_FAIL:
- self._log.log_message("error", "Failed to receive the file descriptor for XFR connection")
+ if sock_fd == FD_COMM_ERROR:
+ logger.error(XFROUT_RECEIVE_FILE_DESCRIPTOR_ERROR)
return
# receive request msg
@@ -395,7 +468,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
def finish_request(self, sock_fd, request_data):
'''Finish one request by instantiating RequestHandlerClass.'''
- self.RequestHandlerClass(sock_fd, request_data, self, self._log)
+ self.RequestHandlerClass(sock_fd, request_data, self, self.tsig_key_ring)
def _remove_unused_sock_file(self, sock_file):
'''Try to remove the socket file. If the file is being used
@@ -403,8 +476,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
If it's not a socket file or nobody is listening
, it will be removed. If it can't be removed, exit from python. '''
if self._sock_file_in_use(sock_file):
- self._log.log_message("error", "Fail to start xfrout process, unix socket file '%s'"
- " is being used by another xfrout process\n" % sock_file)
+ logger.error(XFROUT_UNIX_SOCKET_FILE_IN_USE, sock_file)
sys.exit(0)
else:
if not os.path.exists(sock_file):
@@ -413,7 +485,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
try:
os.unlink(sock_file)
except OSError as err:
- self._log.log_message("error", "[b10-xfrout] Fail to remove file %s: %s\n" % (sock_file, err))
+ logger.error(XFROUT_REMOVE_OLD_UNIX_SOCKET_FILE_ERROR, sock_file, str(err))
sys.exit(0)
def _sock_file_in_use(self, sock_file):
@@ -434,16 +506,32 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
try:
os.unlink(self._sock_file)
except Exception as e:
- self._log.log_message('error', str(e))
+ logger.error(XFROUT_REMOVE_UNIX_SOCKET_FILE_ERROR, self._sock_file, str(e))
+ pass
def update_config_data(self, new_config):
'''Apply the new config setting of xfrout module. '''
- self._log.log_message('info', 'update config data start.')
+ logger.info(XFROUT_NEW_CONFIG)
self._lock.acquire()
self._max_transfers_out = new_config.get('transfers_out')
- self._log.log_message('info', 'max transfer out : %d', self._max_transfers_out)
+ self.set_tsig_key_ring(new_config.get('tsig_key_ring'))
self._lock.release()
- self._log.log_message('info', 'update config data complete.')
+ logger.info(XFROUT_NEW_CONFIG_DONE)
+
+ def set_tsig_key_ring(self, key_list):
+ """Set the tsig_key_ring , given a TSIG key string list representation. """
+
+ # XXX add values to configure zones/tsig options
+ self.tsig_key_ring = TSIGKeyRing()
+ # If key string list is empty, create a empty tsig_key_ring
+ if not key_list:
+ return
+
+ for key_item in key_list:
+ try:
+ self.tsig_key_ring.add(TSIGKey(key_item))
+ except InvalidParameter as ipe:
+ logger.error(XFROUT_BAD_TSIG_KEY_STRING, str(key_item))
def get_db_file(self):
file, is_default = self._cc.get_remote_config_value("Auth", "database_file")
@@ -475,16 +563,16 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
class XfroutServer:
def __init__(self):
self._unix_socket_server = None
- self._log = None
+ #self._log = None
self._listen_sock_file = UNIX_SOCKET_FILE
self._shutdown_event = threading.Event()
- self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler)
+ self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler, None, True)
self._config_data = self._cc.get_full_config()
self._cc.start()
self._cc.add_remote_config(AUTH_SPECFILE_LOCATION);
- self._log = isc.log.NSLogger(self._config_data.get('log_name'), self._config_data.get('log_file'),
- self._config_data.get('log_severity'), self._config_data.get('log_versions'),
- self._config_data.get('log_max_bytes'), True)
+ #self._log = isc.log.NSLogger(self._config_data.get('log_name'), self._config_data.get('log_file'),
+ # self._config_data.get('log_severity'), self._config_data.get('log_versions'),
+ # self._config_data.get('log_max_bytes'), True)
self._start_xfr_query_listener()
self._start_notifier()
@@ -492,13 +580,13 @@ class XfroutServer:
'''Start a new thread to accept xfr query. '''
self._unix_socket_server = UnixSockServer(self._listen_sock_file, XfroutSession,
self._shutdown_event, self._config_data,
- self._cc, self._log);
+ self._cc)
listener = threading.Thread(target=self._unix_socket_server.serve_forever)
listener.start()
def _start_notifier(self):
datasrc = self._unix_socket_server.get_db_file()
- self._notifier = notify_out.NotifyOut(datasrc, self._log)
+ self._notifier = notify_out.NotifyOut(datasrc)
self._notifier.dispatcher()
def send_notify(self, zone_name, zone_class):
@@ -513,8 +601,8 @@ class XfroutServer:
continue
self._config_data[key] = new_config[key]
- if self._log:
- self._log.update_config(new_config)
+ #if self._log:
+ # self._log.update_config(new_config)
if self._unix_socket_server:
self._unix_socket_server.update_config_data(self._config_data)
@@ -543,7 +631,7 @@ class XfroutServer:
def command_handler(self, cmd, args):
if cmd == "shutdown":
- self._log.log_message("info", "Received shutdown command.")
+ logger.info(XFROUT_RECEIVED_SHUTDOWN_COMMAND)
self.shutdown()
answer = create_answer(0)
@@ -551,8 +639,7 @@ class XfroutServer:
zone_name = args.get('zone_name')
zone_class = args.get('zone_class')
if zone_name and zone_class:
- self._log.log_message("info", "zone '%s/%s': receive notify others command" \
- % (zone_name, zone_class))
+ logger.info(XFROUT_NOTIFY_COMMAND, zone_name, zone_class)
self.send_notify(zone_name, zone_class)
answer = create_answer(0)
else:
@@ -595,15 +682,11 @@ if '__main__' == __name__:
xfrout_server = XfroutServer()
xfrout_server.run()
except KeyboardInterrupt:
- sys.stderr.write("[b10-xfrout] exit xfrout process\n")
+ logger.INFO(XFROUT_STOPPED_BY_KEYBOARD)
except SessionError as e:
- sys.stderr.write("[b10-xfrout] Error creating xfrout, "
- "is the command channel daemon running?\n")
+ logger.error(XFROUT_CC_SESSION_ERROR, str(e))
except SessionTimeout as e:
- sys.stderr.write("[b10-xfrout] Error creating xfrout, "
- "is the configuration manager running?\n")
- except ModuleCCSessionError as e:
- sys.stderr.write("[b10-xfrout] exit xfrout process:%s\n" % str(e))
+ logger.error(XFROUT_CC_SESSION_TIMEOUT_ERROR)
if xfrout_server:
xfrout_server.shutdown()
diff --git a/src/bin/xfrout/xfrout.spec.pre.in b/src/bin/xfrout/xfrout.spec.pre.in
index 941db72..2efa3d7 100644
--- a/src/bin/xfrout/xfrout.spec.pre.in
+++ b/src/bin/xfrout/xfrout.spec.pre.in
@@ -37,6 +37,18 @@
"item_type": "integer",
"item_optional": false,
"item_default": 1048576
+ },
+ {
+ "item_name": "tsig_key_ring",
+ "item_type": "list",
+ "item_optional": true,
+ "item_default": [],
+ "list_item_spec" :
+ {
+ "item_name": "tsig_key",
+ "item_type": "string",
+ "item_optional": true
+ }
}
],
"commands": [
diff --git a/src/bin/xfrout/xfrout_messages.mes b/src/bin/xfrout/xfrout_messages.mes
new file mode 100644
index 0000000..2dada54
--- /dev/null
+++ b/src/bin/xfrout/xfrout_messages.mes
@@ -0,0 +1,140 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# No namespace declaration - these constants go in the global namespace
+# of the xfrout messages python module.
+
+% XFROUT_AXFR_TRANSFER_DONE transfer of %1/%2 complete
+The transfer of the given zone has been completed successfully, or was
+aborted due to a shutdown event.
+
+% XFROUT_AXFR_TRANSFER_ERROR error transferring zone %1/%2: %3
+An uncaught exception was encountered while sending the response to
+an AXFR query. The error message of the exception is included in the
+log message, but this error most likely points to incomplete exception
+handling in the code.
+
+% XFROUT_AXFR_TRANSFER_FAILED transfer of %1/%2 failed, rcode: %3
+A transfer out for the given zone failed. An error response is sent
+to the client. The given rcode is the rcode that is set in the error
+response. This is either NOTAUTH (we are not authoritative for the
+zone), SERVFAIL (our internal database is missing the SOA record for
+the zone), or REFUSED (the limit of simultaneous outgoing AXFR
+transfers, as specified by the configuration value
+Xfrout/max_transfers_out, has been reached).
+# Still a TODO, but when implemented, REFUSED can also mean
+# the client is not allowed to transfer the zone
+
+% XFROUT_AXFR_TRANSFER_STARTED transfer of zone %1/%2 has started
+A transfer out of the given zone has started.
+
+% XFROUT_BAD_TSIG_KEY_STRING bad TSIG key string: %1
+The TSIG key string as read from the configuration does not represent
+a valid TSIG key.
+
+% XFROUT_CC_SESSION_ERROR error reading from cc channel: %1
+There was a problem reading from the command and control channel. The
+most likely cause is that the msgq daemon is not running.
+
+% XFROUT_CC_SESSION_TIMEOUT_ERROR timeout waiting for cc response
+There was a problem reading a response from antoher module over the
+command and control channel. The most likely cause is that the
+configuration manager b10-cfgmgr is not running.
+
+% XFROUT_FETCH_REQUEST_ERROR socket error while fetching a request from the auth daemon
+There was a socket error while contacting the b10-auth daemon to
+fetch a transfer request. The auth daemon may have shutdown.
+
+% XFROUT_HANDLE_QUERY_ERROR error while handling query: %1
+There was a general error handling an xfrout query. The error is shown
+in the message. In principle this error should not appear, and points
+to an oversight catching exceptions in the right place. However, to
+ensure the daemon keeps running, this error is caught and reported.
+
+% XFROUT_IMPORT error importing python module: %1
+There was an error importing a python module. One of the modules needed
+by xfrout could not be found. This suggests that either some libraries
+are missing on the system, or the PYTHONPATH variable is not correct.
+The specific place where this library needs to be depends on your
+system and your specific installation.
+
+% XFROUT_NEW_CONFIG Update xfrout configuration
+New configuration settings have been sent from the configuration
+manager. The xfrout daemon will now apply them.
+
+% XFROUT_NEW_CONFIG_DONE Update xfrout configuration done
+The xfrout daemon is now done reading the new configuration settings
+received from the configuration manager.
+
+% XFROUT_NOTIFY_COMMAND received command to send notifies for %1/%2
+The xfrout daemon received a command on the command channel that
+NOTIFY packets should be sent for the given zone.
+
+% XFROUT_PARSE_QUERY_ERROR error parsing query: %1
+There was a parse error while reading an incoming query. The parse
+error is shown in the log message. A remote client sent a packet we
+do not understand or support. The xfrout request will be ignored.
+In general, this should only occur for unexpected problems like
+memory allocation failures, as the query should already have been
+parsed by the b10-auth daemon, before it was passed here.
+
+% XFROUT_PROCESS_REQUEST_ERROR error processing transfer request: %2
+There was an error processing a transfer request. The error is included
+in the log message, but at this point no specific information other
+than that could be given. This points to incomplete exception handling
+in the code.
+
+% XFROUT_RECEIVE_FILE_DESCRIPTOR_ERROR error receiving the file descriptor for an XFR connection
+There was an error receiving the file descriptor for the transfer
+request. Normally, the request is received by b10-auth, and passed on
+to the xfrout daemon, so it can answer directly. However, there was a
+problem receiving this file descriptor. The request will be ignored.
+
+% XFROUT_RECEIVED_SHUTDOWN_COMMAND shutdown command received
+The xfrout daemon received a shutdown command from the command channel
+and will now shut down.
+
+% XFROUT_REMOVE_UNIX_SOCKET_FILE_ERROR error clearing unix socket file %1: %2
+When shutting down, the xfrout daemon tried to clear the unix socket
+file used for communication with the auth daemon. It failed to remove
+the file. The reason for the failure is given in the error message.
+
+% XFROUT_REMOVE_OLD_UNIX_SOCKET_FILE_ERROR error removing unix socket file %1: %2
+The unix socket file xfrout needs for contact with the auth daemon
+already exists, and needs to be removed first, but there is a problem
+removing it. It is likely that we do not have permission to remove
+this file. The specific error is show in the log message. The xfrout
+daemon will shut down.
+
+% XFROUT_SOCKET_SELECT_ERROR error while calling select() on request socket: %1
+There was an error while calling select() on the socket that informs
+the xfrout daemon that a new xfrout request has arrived. This should
+be a result of rare local error such as memory allocation failure and
+shouldn't happen under normal conditions. The error is included in the
+log message.
+
+% XFROUT_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
+There was a keyboard interrupt signal to stop the xfrout daemon. The
+daemon will now shut down.
+
+% XFROUT_STOPPING the xfrout daemon is shutting down
+The current transfer is aborted, as the xfrout daemon is shutting down.
+
+% XFROUT_UNIX_SOCKET_FILE_IN_USE another xfrout process seems to be using the unix socket file %1
+While starting up, the xfrout daemon tried to clear the unix domain
+socket needed for contacting the b10-auth daemon to pass requests
+on, but the file is in use. The most likely cause is that another
+xfrout daemon process is still running. This xfrout daemon (the one
+printing this message) will not start.
+
diff --git a/src/bin/zonemgr/Makefile.am b/src/bin/zonemgr/Makefile.am
index 410279a..8ab5f7a 100644
--- a/src/bin/zonemgr/Makefile.am
+++ b/src/bin/zonemgr/Makefile.am
@@ -26,3 +26,8 @@ b10-zonemgr: zonemgr.py
$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
-e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" zonemgr.py >$@
chmod a+x $@
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/bin/zonemgr/b10-zonemgr.8 b/src/bin/zonemgr/b10-zonemgr.8
index fbd0602..bfc0a7b 100644
--- a/src/bin/zonemgr/b10-zonemgr.8
+++ b/src/bin/zonemgr/b10-zonemgr.8
@@ -2,12 +2,12 @@
.\" Title: b10-zonemgr
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: October 18, 2010
+.\" Date: May 19, 2011
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "B10\-ZONEMGR" "8" "October 18, 2010" "BIND10" "BIND10"
+.TH "B10\-ZONEMGR" "8" "May 19, 2011" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -46,11 +46,6 @@ receives its configurations from
The configurable settings are:
.PP
-\fIjitter_scope\fR
-defines the random jitter range subtracted from the refresh and retry timers to avoid many zones from refreshing at the same time\&. The refresh or retry time actually used is a random time between the defined refresh or retry time and it multiplied by the
-\fIjitter_scope\fR\&. This is re\-evaluated after each refresh or retry\&. This value is a real number and the maximum is 0\&.5 (half of the refresh or retry time)\&. The default is 0\&.25\&. Set to 0 to disable the jitter\&.
-.PP
-
\fIlowerbound_refresh\fR
defines the minimum SOA REFRESH time in seconds\&. The default is 10\&.
.PP
@@ -59,10 +54,36 @@ defines the minimum SOA REFRESH time in seconds\&. The default is 10\&.
defines the minimum SOA RETRY time in seconds\&. The default is 5\&.
.PP
+\fIrefresh_jitter\fR
+This value is a real number\&. The maximum amount is 0\&.5\&. The default is 0\&.25\&.
+.PP
+
+\fIreload_jitter\fR
+This value is a real number\&. The default is 0\&.75\&.
+.PP
+
\fImax_transfer_timeout\fR
defines the maximum amount of time in seconds for a transfer\&.
The default is 14400 (4 hours)\&.
.PP
+
+\fIsecondary_zones\fR
+is a list of slave zones that the
+\fBb10\-zonemgr\fR
+should keep timers for\&. The list items include the
+\fIname\fR
+(which defines the zone name) and the
+\fIclass\fR
+(which defaults to
+\(lqIN\(rq)\&.
+.PP
+(A deprecated configuration is
+\fIjitter_scope\fR
+which is superceded by
+\fIrefresh_jitter\fR
+and
+\fIreload_jitter\fR\&.)
+.PP
The configuration commands are:
.PP
@@ -107,5 +128,5 @@ The
daemon was designed in July 2010 by CNNIC for the ISC BIND 10 project\&.
.SH "COPYRIGHT"
.br
-Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2011 Internet Systems Consortium, Inc. ("ISC")
.br
diff --git a/src/bin/zonemgr/b10-zonemgr.xml b/src/bin/zonemgr/b10-zonemgr.xml
index 4d796ee..00f5d04 100644
--- a/src/bin/zonemgr/b10-zonemgr.xml
+++ b/src/bin/zonemgr/b10-zonemgr.xml
@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "—">]>
<!--
- - Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-2011 Internet Systems Consortium, Inc. ("ISC")
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>October 18, 2010</date>
+ <date>May 19, 2011</date>
</refentryinfo>
<refmeta>
@@ -36,7 +36,7 @@
<docinfo>
<copyright>
- <year>2010</year>
+ <year>2010-2011</year>
<holder>Internet Systems Consortium, Inc. ("ISC")</holder>
</copyright>
</docinfo>
@@ -92,6 +92,39 @@
<para>
The configurable settings are:
</para>
+
+ <para>
+ <varname>lowerbound_refresh</varname>
+ defines the minimum SOA REFRESH time in seconds.
+ The default is 10.
+ </para>
+
+ <para>
+ <varname>lowerbound_retry</varname>
+ defines the minimum SOA RETRY time in seconds.
+ The default is 5.
+ </para>
+
+ <para>
+ <varname>refresh_jitter</varname>
+ This value is a real number.
+ The maximum amount is 0.5.
+ The default is 0.25.
+ </para>
+<!-- TODO: needs to be documented -->
+<!-- TODO: Set to 0 to disable the jitter. -->
+
+ <para>
+ <varname>reload_jitter</varname>
+ This value is a real number.
+ The default is 0.75.
+ </para>
+<!-- TODO: needs to be documented -->
+<!-- TODO: Set to 0 to disable the jitter. -->
+<!-- what does 0 do? -->
+<!-- TODO: no max? -->
+
+<!-- TODO: remove this. This is old removed config
<para>
<varname>jitter_scope</varname>
defines the random jitter range subtracted from the refresh
@@ -106,16 +139,8 @@
The default is 0.25.
Set to 0 to disable the jitter.
</para>
- <para>
- <varname>lowerbound_refresh</varname>
- defines the minimum SOA REFRESH time in seconds.
- The default is 10.
- </para>
- <para>
- <varname>lowerbound_retry</varname>
- defines the minimum SOA RETRY time in seconds.
- The default is 5.
- </para>
+-->
+
<para>
<varname>max_transfer_timeout</varname>
defines the maximum amount of time in seconds for a transfer.
@@ -123,6 +148,21 @@
The default is 14400 (4 hours).
</para>
+<!-- TODO: this duplicates list in Xfrin too -->
+ <para>
+ <varname>secondary_zones</varname> is a list of slave zones
+ that the <command>b10-zonemgr</command> should keep timers for.
+ The list items include the <varname>name</varname> (which
+ defines the zone name) and the <varname>class</varname>
+ (which defaults to <quote>IN</quote>).
+ </para>
+
+ <para>
+ (A deprecated configuration is <varname>jitter_scope</varname>
+ which is superceded by <varname>refresh_jitter</varname>
+ and <varname>reload_jitter</varname>.)
+ </para>
+
<!-- TODO: formating -->
<para>
The configuration commands are:
diff --git a/src/bin/zonemgr/tests/Makefile.am b/src/bin/zonemgr/tests/Makefile.am
index 496c1a4..97f9b5e 100644
--- a/src/bin/zonemgr/tests/Makefile.am
+++ b/src/bin/zonemgr/tests/Makefile.am
@@ -3,6 +3,13 @@ PYTESTS = zonemgr_test.py
EXTRA_DIST = $(PYTESTS)
CLEANFILES = initdb.file
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
if ENABLE_PYTHON_COVERAGE
@@ -12,6 +19,7 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
+ $(LIBRARY_PATH_PLACEHOLDER) \
env PYTHONPATH=$(abs_top_builddir)/src/bin/zonemgr:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
diff --git a/src/bin/zonemgr/tests/zonemgr_test.py b/src/bin/zonemgr/tests/zonemgr_test.py
index 70dc1b0..496ce6b 100644
--- a/src/bin/zonemgr/tests/zonemgr_test.py
+++ b/src/bin/zonemgr/tests/zonemgr_test.py
@@ -21,16 +21,18 @@ import os
import tempfile
from zonemgr import *
-ZONE_NAME_CLASS1_IN = ("sd.cn.", "IN")
-ZONE_NAME_CLASS2_CH = ("tw.cn", "CH")
-ZONE_NAME_CLASS3_IN = ("example.com", "IN")
-ZONE_NAME_CLASS1_CH = ("sd.cn.", "CH")
-ZONE_NAME_CLASS2_IN = ("tw.cn", "IN")
+ZONE_NAME_CLASS1_IN = ("example.net.", "IN")
+ZONE_NAME_CLASS1_CH = ("example.net.", "CH")
+ZONE_NAME_CLASS2_IN = ("example.org.", "IN")
+ZONE_NAME_CLASS2_CH = ("example.org.", "CH")
+ZONE_NAME_CLASS3_IN = ("example.com.", "IN")
+ZONE_NAME_CLASS3_CH = ("example.com.", "CH")
MAX_TRANSFER_TIMEOUT = 14400
LOWERBOUND_REFRESH = 10
-LOWERBOUND_RETRY = 5
-JITTER_SCOPE = 0.10
+LOWERBOUND_RETRY = 5
+REFRESH_JITTER = 0.10
+RELOAD_JITTER = 0.75
class ZonemgrTestException(Exception):
pass
@@ -46,36 +48,64 @@ class MySession():
def group_recvmsg(self, nonblock, seq):
return None, None
+class FakeConfig:
+ def __init__(self):
+ self.zone_list = []
+ self.set_zone_list_from_name_classes([ZONE_NAME_CLASS1_IN,
+ ZONE_NAME_CLASS2_CH])
+ def set_zone_list_from_name_classes(self, zones):
+ self.zone_list = map(lambda nc: {"name": nc[0], "class": nc[1]}, zones)
+ def get(self, name):
+ if name == 'lowerbound_refresh':
+ return LOWERBOUND_REFRESH
+ elif name == 'lowerbound_retry':
+ return LOWERBOUND_RETRY
+ elif name == 'max_transfer_timeout':
+ return MAX_TRANSFER_TIMEOUT
+ elif name == 'refresh_jitter':
+ return REFRESH_JITTER
+ elif name == 'reload_jitter':
+ return RELOAD_JITTER
+ elif name == 'secondary_zones':
+ return self.zone_list
+ else:
+ raise ValueError('Uknown config option')
+
class MyZonemgrRefresh(ZonemgrRefresh):
def __init__(self):
- class FakeConfig:
- def get(self, name):
- if name == 'lowerbound_refresh':
- return LOWERBOUND_REFRESH
- elif name == 'lowerbound_retry':
- return LOWERBOUND_RETRY
- elif name == 'max_transfer_timeout':
- return MAX_TRANSFER_TIMEOUT
- elif name == 'jitter_scope':
- return JITTER_SCOPE
- else:
- raise ValueError('Uknown config option')
self._master_socket, self._slave_socket = socket.socketpair()
+ self._zonemgr_refresh_info = {}
+ self._lowerbound_refresh = 10
+ self._lowerbound_retry = 5
+ self._reload_jitter = 0.75
+ self._refresh_jitter = 0.25
+
+ def get_zone_soa(zone_name, db_file):
+ if zone_name == 'example.net.':
+ return (1, 2, 'example.net.', 'example.net.sd.', 21600, 'SOA', None,
+ 'a.example.net. root.example.net. 2009073106 7200 3600 2419200 21600')
+ elif zone_name == 'example.org.':
+ return (1, 2, 'example.org.', 'example.org.sd.', 21600, 'SOA', None,
+ 'a.example.org. root.example.org. 2009073112 7200 3600 2419200 21600')
+ else:
+ return None
+ sqlite3_ds.get_zone_soa = get_zone_soa
+
ZonemgrRefresh.__init__(self, MySession(), "initdb.file",
self._slave_socket, FakeConfig())
current_time = time.time()
- self._zonemgr_refresh_info = {
- ('sd.cn.', 'IN'): {
+ self._zonemgr_refresh_info = {
+ ('example.net.', 'IN'): {
'last_refresh_time': current_time,
- 'next_refresh_time': current_time + 6500,
- 'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600',
+ 'next_refresh_time': current_time + 6500,
+ 'zone_soa_rdata': 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600',
'zone_state': 0},
- ('tw.cn', 'CH'): {
- 'last_refresh_time': current_time,
- 'next_refresh_time': current_time + 6900,
- 'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073112 7200 3600 2419200 21600',
+ ('example.org.', 'CH'): {
+ 'last_refresh_time': current_time,
+ 'next_refresh_time': current_time + 6900,
+ 'zone_soa_rdata': 'a.example.org. root.example.org. 2009073112 7200 3600 2419200 21600',
'zone_state': 0}
- }
+ }
class TestZonemgrRefresh(unittest.TestCase):
def setUp(self):
@@ -87,10 +117,10 @@ class TestZonemgrRefresh(unittest.TestCase):
max = 100025.120
jitter = 0
self.assertEqual(max, self.zone_refresh._random_jitter(max, jitter))
- jitter = max / 4
+ jitter = 0.3 * max
for i in range (0, 150):
- self.assertTrue((3 * max / 4) <= self.zone_refresh._random_jitter(max, jitter))
- self.assertTrue(self.zone_refresh._random_jitter(max, jitter) <= max)
+ self.assertTrue((max - jitter) <= self.zone_refresh._random_jitter(max, jitter))
+ self.assertTrue(self.zone_refresh._random_jitter(max, jitter) <= max)
i += 1;
def test_get_current_time(self):
@@ -111,7 +141,7 @@ class TestZonemgrRefresh(unittest.TestCase):
self.zone_refresh._set_zone_refresh_timer(ZONE_NAME_CLASS1_IN)
zone_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
time2 = time.time()
- self.assertTrue((time1 + 7200 * 3 / 4) <= zone_timeout)
+ self.assertTrue((time1 + 7200 * (1 - self.zone_refresh._refresh_jitter)) <= zone_timeout)
self.assertTrue(zone_timeout <= time2 + 7200)
def test_set_zone_retry_timer(self):
@@ -119,7 +149,7 @@ class TestZonemgrRefresh(unittest.TestCase):
self.zone_refresh._set_zone_retry_timer(ZONE_NAME_CLASS1_IN)
zone_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
time2 = time.time()
- self.assertTrue((time1 + 3600 * 3 / 4) <= zone_timeout)
+ self.assertTrue((time1 + 3600 * (1 - self.zone_refresh._refresh_jitter)) <= zone_timeout)
self.assertTrue(zone_timeout <= time2 + 3600)
def test_zone_not_exist(self):
@@ -128,6 +158,7 @@ class TestZonemgrRefresh(unittest.TestCase):
self.assertFalse(self.zone_refresh._zone_not_exist(ZONE_NAME_CLASS2_CH))
self.assertTrue(self.zone_refresh._zone_not_exist(ZONE_NAME_CLASS2_IN))
self.assertTrue(self.zone_refresh._zone_not_exist(ZONE_NAME_CLASS3_IN))
+ self.assertTrue(self.zone_refresh._zone_not_exist(ZONE_NAME_CLASS3_CH))
def test_set_zone_notify_timer(self):
time1 = time.time()
@@ -150,20 +181,20 @@ class TestZonemgrRefresh(unittest.TestCase):
self.assertTrue(self.zone_refresh._zone_is_expired(ZONE_NAME_CLASS1_IN))
def test_get_zone_soa_rdata(self):
- soa_rdata1 = 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600'
- soa_rdata2 = 'a.dns.cn. root.cnnic.cn. 2009073112 7200 3600 2419200 21600'
+ soa_rdata1 = 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600'
+ soa_rdata2 = 'a.example.org. root.example.org. 2009073112 7200 3600 2419200 21600'
self.assertEqual(soa_rdata1, self.zone_refresh._get_zone_soa_rdata(ZONE_NAME_CLASS1_IN))
self.assertRaises(KeyError, self.zone_refresh._get_zone_soa_rdata, ZONE_NAME_CLASS1_CH)
self.assertEqual(soa_rdata2, self.zone_refresh._get_zone_soa_rdata(ZONE_NAME_CLASS2_CH))
self.assertRaises(KeyError, self.zone_refresh._get_zone_soa_rdata, ZONE_NAME_CLASS2_IN)
-
+
def test_zonemgr_reload_zone(self):
- soa_rdata = 'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600'
+ soa_rdata = 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600'
# We need to restore this not to harm other tests
old_get_zone_soa = sqlite3_ds.get_zone_soa
def get_zone_soa(zone_name, db_file):
- return (1, 2, 'sd.cn.', 'cn.sd.', 21600, 'SOA', None,
- 'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600')
+ return (1, 2, 'example.net.', 'example.net.sd.', 21600, 'SOA', None,
+ 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600')
sqlite3_ds.get_zone_soa = get_zone_soa
self.zone_refresh.zonemgr_reload_zone(ZONE_NAME_CLASS1_IN)
@@ -212,7 +243,7 @@ class TestZonemgrRefresh(unittest.TestCase):
current_time = time.time()
self.zone_refresh._set_zone_refresh_timeout(ZONE_NAME_CLASS1_IN, current_time)
refresh_time = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["refresh_timeout"]
- self.assertEqual(current_time, refresh_time)
+ self.assertEqual(current_time, refresh_time)
def test_get_zone_next_refresh_time(self):
current_time = time.time()
@@ -234,25 +265,26 @@ class TestZonemgrRefresh(unittest.TestCase):
current_time = time.time()
self.zone_refresh._set_zone_last_refresh_time(ZONE_NAME_CLASS1_IN, current_time)
last_refresh_time = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["last_refresh_time"]
- self.assertEqual(current_time, last_refresh_time)
+ self.assertEqual(current_time, last_refresh_time)
def test_send_command(self):
self.assertRaises(ZonemgrTestException, self.zone_refresh._send_command, "Unknown", "Notify", None)
def test_zone_mgr_is_empty(self):
self.assertFalse(self.zone_refresh._zone_mgr_is_empty())
- self.zone_refresh._zonemgr_refresh_info = {}
+ self.zone_refresh._zonemgr_refresh_info = {}
self.assertTrue(self.zone_refresh._zone_mgr_is_empty())
def test_zonemgr_add_zone(self):
- soa_rdata = 'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600'
+ soa_rdata = 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600'
# This needs to be restored. The following test actually failed if we left
# this unclean
old_get_zone_soa = sqlite3_ds.get_zone_soa
+ time1 = time.time()
def get_zone_soa(zone_name, db_file):
- return (1, 2, 'sd.cn.', 'cn.sd.', 21600, 'SOA', None,
- 'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600')
+ return (1, 2, 'example.net.', 'example.net.sd.', 21600, 'SOA', None,
+ 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600')
sqlite3_ds.get_zone_soa = get_zone_soa
@@ -260,10 +292,14 @@ class TestZonemgrRefresh(unittest.TestCase):
self.zone_refresh.zonemgr_add_zone(ZONE_NAME_CLASS1_IN)
self.assertEqual(1, len(self.zone_refresh._zonemgr_refresh_info))
zone_soa_rdata = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_soa_rdata"]
- self.assertEqual(soa_rdata, zone_soa_rdata)
+ self.assertEqual(soa_rdata, zone_soa_rdata)
self.assertEqual(ZONE_OK, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"])
self.assertTrue("last_refresh_time" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN].keys())
self.assertTrue("next_refresh_time" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN].keys())
+ time2 = time.time()
+ zone_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
+ self.assertTrue((time1 + 900 * (1 - self.zone_refresh._reload_jitter)) <= zone_timeout)
+ self.assertTrue(zone_timeout <= time2 + 900)
def get_zone_soa2(zone_name, db_file):
return None
@@ -272,64 +308,42 @@ class TestZonemgrRefresh(unittest.TestCase):
ZONE_NAME_CLASS1_IN)
sqlite3_ds.get_zone_soa = old_get_zone_soa
- def test_build_zonemgr_refresh_info(self):
- soa_rdata = 'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600'
-
- def get_zones_info(db_file):
- return [("sd.cn.", "IN")]
-
- def get_zone_soa(zone_name, db_file):
- return (1, 2, 'sd.cn.', 'cn.sd.', 21600, 'SOA', None,
- 'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600')
-
- sqlite3_ds.get_zones_info = get_zones_info
- sqlite3_ds.get_zone_soa = get_zone_soa
-
- self.zone_refresh._zonemgr_refresh_info = {}
- self.zone_refresh._build_zonemgr_refresh_info()
- self.assertEqual(1, len(self.zone_refresh._zonemgr_refresh_info))
- zone_soa_rdata = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_soa_rdata"]
- self.assertEqual(soa_rdata, zone_soa_rdata)
- self.assertEqual(ZONE_OK, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"])
- self.assertTrue("last_refresh_time" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN].keys())
- self.assertTrue("next_refresh_time" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN].keys())
-
def test_zone_handle_notify(self):
self.zone_refresh.zone_handle_notify(ZONE_NAME_CLASS1_IN,"127.0.0.1")
notify_master = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["notify_master"]
- self.assertEqual("127.0.0.1", notify_master)
+ self.assertEqual("127.0.0.1", notify_master)
zone_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
current_time = time.time()
self.assertTrue(zone_timeout <= current_time)
self.assertRaises(ZonemgrException, self.zone_refresh.zone_handle_notify,\
- ("org.cn.", "IN"), "127.0.0.1")
+ ZONE_NAME_CLASS3_CH, "127.0.0.1")
self.assertRaises(ZonemgrException, self.zone_refresh.zone_handle_notify,\
ZONE_NAME_CLASS3_IN, "127.0.0.1")
def test_zone_refresh_success(self):
- soa_rdata = 'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600'
+ soa_rdata = 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600'
def get_zone_soa(zone_name, db_file):
- return (1, 2, 'sd.cn.', 'cn.sd.', 21600, 'SOA', None,
- 'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600')
+ return (1, 2, 'example.net.', 'example.net.sd.', 21600, 'SOA', None,
+ 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600')
sqlite3_ds.get_zone_soa = get_zone_soa
time1 = time.time()
self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"] = ZONE_REFRESHING
self.zone_refresh.zone_refresh_success(ZONE_NAME_CLASS1_IN)
time2 = time.time()
zone_soa_rdata = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_soa_rdata"]
- self.assertEqual(soa_rdata, zone_soa_rdata)
+ self.assertEqual(soa_rdata, zone_soa_rdata)
next_refresh_time = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
- self.assertTrue((time1 + 3 * 1800 / 4) <= next_refresh_time)
+ self.assertTrue((time1 + 1800 * (1 - self.zone_refresh._refresh_jitter)) <= next_refresh_time)
self.assertTrue(next_refresh_time <= time2 + 1800)
self.assertEqual(ZONE_OK, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"])
last_refresh_time = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["last_refresh_time"]
self.assertTrue(time1 <= last_refresh_time)
self.assertTrue(last_refresh_time <= time2)
- self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_success, ("org.cn.", "CH"))
- self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_success, ZONE_NAME_CLASS3_IN)
+ self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_success, ("example.test.", "CH"))
+ self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_success, ZONE_NAME_CLASS3_IN)
def test_zone_refresh_fail(self):
- soa_rdata = 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600'
+ soa_rdata = 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600'
time1 = time.time()
self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"] = ZONE_REFRESHING
self.zone_refresh.zone_refresh_fail(ZONE_NAME_CLASS1_IN)
@@ -337,80 +351,80 @@ class TestZonemgrRefresh(unittest.TestCase):
zone_soa_rdata = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_soa_rdata"]
self.assertEqual(soa_rdata, zone_soa_rdata)
next_refresh_time = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["next_refresh_time"]
- self.assertTrue((time1 + 3 * 3600 / 4) <= next_refresh_time)
+ self.assertTrue(((time1 + 3600 * (1 - self.zone_refresh._refresh_jitter))) <= next_refresh_time)
self.assertTrue(next_refresh_time <= time2 + 3600)
self.assertEqual(ZONE_OK, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"])
- self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["last_refresh_time"] = time1 - 2419200
+ self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["last_refresh_time"] = time1 - 2419200
self.zone_refresh.zone_refresh_fail(ZONE_NAME_CLASS1_IN)
self.assertEqual(ZONE_EXPIRED, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"])
- self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_fail, ("org.cn.", "CH"))
- self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_fail, ZONE_NAME_CLASS3_IN)
+ self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_fail, ZONE_NAME_CLASS3_CH)
+ self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_fail, ZONE_NAME_CLASS3_IN)
def test_find_need_do_refresh_zone(self):
time1 = time.time()
- self.zone_refresh._zonemgr_refresh_info = {
- ("sd.cn.","IN"):{
+ self.zone_refresh._zonemgr_refresh_info = {
+ ("example.net.","IN"):{
'last_refresh_time': time1,
- 'next_refresh_time': time1 + 7200,
- 'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600',
+ 'next_refresh_time': time1 + 7200,
+ 'zone_soa_rdata': 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600',
'zone_state': ZONE_OK},
- ("tw.cn","CH"):{
- 'last_refresh_time': time1 - 7200,
- 'next_refresh_time': time1,
- 'refresh_timeout': time1 + MAX_TRANSFER_TIMEOUT,
- 'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073112 7200 3600 2419200 21600',
+ ("example.org.","CH"):{
+ 'last_refresh_time': time1 - 7200,
+ 'next_refresh_time': time1,
+ 'refresh_timeout': time1 + MAX_TRANSFER_TIMEOUT,
+ 'zone_soa_rdata': 'a.example.org. root.example.org. 2009073112 7200 3600 2419200 21600',
'zone_state': ZONE_REFRESHING}
}
zone_need_refresh = self.zone_refresh._find_need_do_refresh_zone()
self.assertEqual(ZONE_NAME_CLASS1_IN, zone_need_refresh)
- self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS2_CH]["refresh_timeout"] = time1
+ self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS2_CH]["refresh_timeout"] = time1
zone_need_refresh = self.zone_refresh._find_need_do_refresh_zone()
self.assertEqual(ZONE_NAME_CLASS2_CH, zone_need_refresh)
def test_do_refresh(self):
time1 = time.time()
self.zone_refresh._zonemgr_refresh_info = {
- ("sd.cn.", "IN"):{
+ ("example.net.", "IN"):{
'last_refresh_time': time1 - 7200,
- 'next_refresh_time': time1 - 1,
- 'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600',
+ 'next_refresh_time': time1 - 1,
+ 'zone_soa_rdata': 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600',
'zone_state': ZONE_OK}
}
self.zone_refresh._do_refresh(ZONE_NAME_CLASS1_IN)
time2 = time.time()
zone_state = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"]
self.assertEqual(ZONE_REFRESHING, zone_state)
- refresh_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["refresh_timeout"]
+ refresh_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["refresh_timeout"]
self.assertTrue(time1 + MAX_TRANSFER_TIMEOUT <= refresh_timeout)
- self.assertTrue(time2 + MAX_TRANSFER_TIMEOUT >= refresh_timeout)
+ self.assertTrue(time2 + MAX_TRANSFER_TIMEOUT >= refresh_timeout)
self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["notify_master"] = "127.0.0.1"
self.zone_refresh._do_refresh(ZONE_NAME_CLASS1_IN)
time2 = time.time()
zone_state = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"]
- self.assertEqual(ZONE_REFRESHING, zone_state)
- refresh_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["refresh_timeout"]
+ self.assertEqual(ZONE_REFRESHING, zone_state)
+ refresh_timeout = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["refresh_timeout"]
self.assertTrue(time1 + MAX_TRANSFER_TIMEOUT <= refresh_timeout)
self.assertTrue(time2 + MAX_TRANSFER_TIMEOUT >= refresh_timeout)
self.assertFalse("notify_master" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN].keys())
def test_run_timer(self):
- """This case will run timer in daemon thread.
- The zone's next_refresh_time is less than now, so zonemgr will do zone refresh
- immediately. The zone's state will become "refreshing".
+ """This case will run timer in daemon thread.
+ The zone's next_refresh_time is less than now, so zonemgr will do zone refresh
+ immediately. The zone's state will become "refreshing".
"""
time1 = time.time()
self.zone_refresh._zonemgr_refresh_info = {
- ("sd.cn.", "IN"):{
+ ("example.net.", "IN"):{
'last_refresh_time': time1 - 7200,
- 'next_refresh_time': time1 - 1,
- 'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600',
+ 'next_refresh_time': time1 - 1,
+ 'zone_soa_rdata': 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600',
'zone_state': ZONE_OK}
}
- self.zone_refresh._check_sock = self.zone_refresh._master_socket
+ self.zone_refresh._check_sock = self.zone_refresh._master_socket
listener = self.zone_refresh.run_timer(daemon=True)
# Shut down the timer thread
self.zone_refresh.shutdown()
@@ -420,26 +434,109 @@ class TestZonemgrRefresh(unittest.TestCase):
self.assertTrue(zone_state == ZONE_REFRESHING)
def test_update_config_data(self):
+ # make sure it doesn't fail if we only provide secondary zones
+ config_data = {
+ "secondary_zones": [ { "name": "example.net.",
+ "class": "IN" } ]
+ }
+ self.zone_refresh.update_config_data(config_data)
+
+ # update all values
+ config_data = {
+ "lowerbound_refresh" : 60,
+ "lowerbound_retry" : 30,
+ "max_transfer_timeout" : 19800,
+ "refresh_jitter" : 0.25,
+ "reload_jitter" : 0.75,
+ "secondary_zones": []
+ }
+ self.zone_refresh.update_config_data(config_data)
+ self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
+ self.assertEqual(30, self.zone_refresh._lowerbound_retry)
+ self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
+ self.assertEqual(0.25, self.zone_refresh._refresh_jitter)
+ self.assertEqual(0.75, self.zone_refresh._reload_jitter)
+
+ # make sure they are not reset when we only update one
+ config_data = {
+ "reload_jitter" : 0.35,
+ }
+ self.zone_refresh.update_config_data(config_data)
+ self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
+ self.assertEqual(30, self.zone_refresh._lowerbound_retry)
+ self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
+ self.assertEqual(0.25, self.zone_refresh._refresh_jitter)
+ self.assertEqual(0.35, self.zone_refresh._reload_jitter)
+
+ # and make sure we restore the previous config if something
+ # goes wrong
+ config_data = {
+ "lowerbound_refresh" : 61,
+ "lowerbound_retry" : 31,
+ "max_transfer_timeout" : 19801,
+ "refresh_jitter" : 0.21,
+ "reload_jitter" : 0.71,
+ "secondary_zones": [ { "name": "doesnotexist",
+ "class": "IN" } ]
+ }
+ self.assertRaises(ZonemgrException,
+ self.zone_refresh.update_config_data,
+ config_data)
+ self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
+ self.assertEqual(30, self.zone_refresh._lowerbound_retry)
+ self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
+ self.assertEqual(0.25, self.zone_refresh._refresh_jitter)
+ self.assertEqual(0.35, self.zone_refresh._reload_jitter)
+
+ # Make sure we accept 0 as a value
config_data = {
"lowerbound_refresh" : 60,
"lowerbound_retry" : 30,
"max_transfer_timeout" : 19800,
- "jitter_scope" : 0.25
+ "refresh_jitter" : 0,
+ "reload_jitter" : 0.75,
+ "secondary_zones": []
}
self.zone_refresh.update_config_data(config_data)
self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
self.assertEqual(30, self.zone_refresh._lowerbound_retry)
self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
- self.assertEqual(0.25, self.zone_refresh._jitter_scope)
+ self.assertEqual(0, self.zone_refresh._refresh_jitter)
+ self.assertEqual(0.75, self.zone_refresh._reload_jitter)
def test_shutdown(self):
- self.zone_refresh._check_sock = self.zone_refresh._master_socket
+ self.zone_refresh._check_sock = self.zone_refresh._master_socket
listener = self.zone_refresh.run_timer()
self.assertTrue(listener.is_alive())
# Shut down the timer thread
self.zone_refresh.shutdown()
self.assertFalse(listener.is_alive())
+ def test_secondary_zones(self):
+ """Test that we can modify the list of secondary zones"""
+ config = FakeConfig()
+ config.zone_list = []
+ # First, remove everything
+ self.zone_refresh.update_config_data(config)
+ self.assertEqual(self.zone_refresh._zonemgr_refresh_info, {})
+ # Put something in
+ config.set_zone_list_from_name_classes([ZONE_NAME_CLASS1_IN])
+ self.zone_refresh.update_config_data(config)
+ self.assertTrue(("example.net.", "IN") in
+ self.zone_refresh._zonemgr_refresh_info)
+ # This one does not exist
+ config.set_zone_list_from_name_classes(["example.net", "CH"])
+ self.assertRaises(ZonemgrException,
+ self.zone_refresh.update_config_data, config)
+ # So it should not affect the old ones
+ self.assertTrue(("example.net.", "IN") in
+ self.zone_refresh._zonemgr_refresh_info)
+ # Make sure it works even when we "accidentally" forget the final dot
+ config.set_zone_list_from_name_classes([("example.net", "IN")])
+ self.zone_refresh.update_config_data(config)
+ self.assertTrue(("example.net.", "IN") in
+ self.zone_refresh._zonemgr_refresh_info)
+
def tearDown(self):
sys.stderr= self.stderr_backup
@@ -447,7 +544,7 @@ class TestZonemgrRefresh(unittest.TestCase):
class MyCCSession():
def __init__(self):
pass
-
+
def get_remote_config_value(self, module_name, identifier):
if module_name == "Auth" and identifier == "database_file":
return "initdb.file", False
@@ -464,10 +561,12 @@ class MyZonemgr(Zonemgr):
self._cc = MySession()
self._module_cc = MyCCSession()
self._config_data = {
- "lowerbound_refresh" : 10,
- "lowerbound_retry" : 5,
+ "lowerbound_refresh" : 10,
+ "lowerbound_retry" : 5,
"max_transfer_timeout" : 14400,
- "jitter_scope" : 0.1
+ "refresh_jitter" : 0.1,
+ "reload_jitter" : 0.75,
+ "secondary_zones": []
}
def _start_zone_refresh_timer(self):
@@ -480,30 +579,42 @@ class TestZonemgr(unittest.TestCase):
def test_config_handler(self):
config_data1 = {
- "lowerbound_refresh" : 60,
- "lowerbound_retry" : 30,
+ "lowerbound_refresh" : 60,
+ "lowerbound_retry" : 30,
"max_transfer_timeout" : 14400,
- "jitter_scope" : 0.1
+ "refresh_jitter" : 0.1,
+ "reload_jitter" : 0.75,
+ "secondary_zones": []
}
- self.zonemgr.config_handler(config_data1)
+ self.assertEqual(self.zonemgr.config_handler(config_data1),
+ {"result": [0]})
self.assertEqual(config_data1, self.zonemgr._config_data)
- config_data2 = {"zone_name" : "sd.cn.", "port" : "53", "master" : "192.168.1.1"}
+ config_data2 = {"zone_name" : "example.net.", "port" : "53", "master" : "192.168.1.1"}
self.zonemgr.config_handler(config_data2)
self.assertEqual(config_data1, self.zonemgr._config_data)
# jitter should not be bigger than half of the original value
- config_data3 = {"jitter_scope" : 0.7}
+ config_data3 = {"refresh_jitter" : 0.7}
self.zonemgr.config_handler(config_data3)
- self.assertEqual(0.5, self.zonemgr._config_data.get("jitter_scope"))
+ self.assertEqual(0.5, self.zonemgr._config_data.get("refresh_jitter"))
+ # The zone doesn't exist in database, it should be rejected
+ self.zonemgr._zone_refresh = ZonemgrRefresh(None, "initdb.file", None,
+ config_data1)
+ config_data1["secondary_zones"] = [{"name": "nonexistent.example",
+ "class": "IN"}]
+ self.assertNotEqual(self.zonemgr.config_handler(config_data1),
+ {"result": [0]})
+ # As it is rejected, the old value should be kept
+ self.assertEqual(0.5, self.zonemgr._config_data.get("refresh_jitter"))
def test_get_db_file(self):
self.assertEqual("initdb.file", self.zonemgr.get_db_file())
-
+
def test_parse_cmd_params(self):
- params1 = {"zone_name" : "org.cn", "zone_class" : "CH", "master" : "127.0.0.1"}
- answer1 = (("org.cn", "CH"), "127.0.0.1")
+ params1 = {"zone_name" : "example.com.", "zone_class" : "CH", "master" : "127.0.0.1"}
+ answer1 = (ZONE_NAME_CLASS3_CH, "127.0.0.1")
self.assertEqual(answer1, self.zonemgr._parse_cmd_params(params1, ZONE_NOTIFY_COMMAND))
- params2 = {"zone_name" : "org.cn", "zone_class" : "CH"}
- answer2 = ("org.cn", "CH")
+ params2 = {"zone_name" : "example.com.", "zone_class" : "IN"}
+ answer2 = ZONE_NAME_CLASS3_IN
self.assertEqual(answer2, self.zonemgr._parse_cmd_params(params2, ZONE_XFRIN_SUCCESS_COMMAND))
self.assertRaises(ZonemgrException, self.zonemgr._parse_cmd_params, params2, ZONE_NOTIFY_COMMAND)
params1 = {"zone_class" : "CH"}
@@ -511,12 +622,12 @@ class TestZonemgr(unittest.TestCase):
def test_config_data_check(self):
# jitter should not be bigger than half of the original value
- config_data2 = {"jitter_scope" : 0.2}
- config_data3 = {"jitter_scope" : 0.6}
+ config_data2 = {"refresh_jitter" : 0.2}
+ config_data3 = {"refresh_jitter" : 0.6}
self.zonemgr._config_data_check(config_data2)
- self.assertEqual(0.2, config_data2.get("jitter_scope"))
+ self.assertEqual(0.2, config_data2.get("refresh_jitter"))
self.zonemgr._config_data_check(config_data3)
- self.assertEqual(0.5, config_data3.get("jitter_scope"))
+ self.assertEqual(0.5, config_data3.get("refresh_jitter"))
def tearDown(self):
pass
diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in
index f9659a3..c6e3163 100755
--- a/src/bin/zonemgr/zonemgr.py.in
+++ b/src/bin/zonemgr/zonemgr.py.in
@@ -1,7 +1,6 @@
#!@PYTHON@
# Copyright (C) 2010 Internet Systems Consortium.
-# Copyright (C) 2010 CZ NIC
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -101,9 +100,13 @@ class ZonemgrRefresh:
self._cc = cc
self._check_sock = slave_socket
self._db_file = db_file
- self.update_config_data(config_data)
self._zonemgr_refresh_info = {}
- self._build_zonemgr_refresh_info()
+ self._lowerbound_refresh = None
+ self._lowerbound_retry = None
+ self._max_transfer_timeout = None
+ self._refresh_jitter = None
+ self._reload_jitter = None
+ self.update_config_data(config_data)
self._running = False
def _random_jitter(self, max, jitter):
@@ -127,19 +130,19 @@ class ZonemgrRefresh:
def _set_zone_refresh_timer(self, zone_name_class):
"""Set zone next refresh time after zone refresh success.
- now + refresh - jitter <= next_refresh_time <= now + refresh
+ now + refresh - refresh_jitter <= next_refresh_time <= now + refresh
"""
zone_refresh_time = float(self._get_zone_soa_rdata(zone_name_class).split(" ")[REFRESH_OFFSET])
zone_refresh_time = max(self._lowerbound_refresh, zone_refresh_time)
- self._set_zone_timer(zone_name_class, zone_refresh_time, self._jitter_scope * zone_refresh_time)
+ self._set_zone_timer(zone_name_class, zone_refresh_time, self._refresh_jitter * zone_refresh_time)
def _set_zone_retry_timer(self, zone_name_class):
"""Set zone next refresh time after zone refresh fail.
- now + retry - jitter <= next_refresh_time <= now + retry
+ now + retry - retry_jitter <= next_refresh_time <= now + retry
"""
zone_retry_time = float(self._get_zone_soa_rdata(zone_name_class).split(" ")[RETRY_OFFSET])
zone_retry_time = max(self._lowerbound_retry, zone_retry_time)
- self._set_zone_timer(zone_name_class, zone_retry_time, self._jitter_scope * zone_retry_time)
+ self._set_zone_timer(zone_name_class, zone_retry_time, self._refresh_jitter * zone_retry_time)
def _set_zone_notify_timer(self, zone_name_class):
"""Set zone next refresh time after receiving notify
@@ -149,16 +152,13 @@ class ZonemgrRefresh:
def _zone_not_exist(self, zone_name_class):
""" Zone doesn't belong to zonemgr"""
- if zone_name_class in self._zonemgr_refresh_info.keys():
- return False
- return True
+ return not zone_name_class in self._zonemgr_refresh_info
def zone_refresh_success(self, zone_name_class):
"""Update zone info after zone refresh success"""
if (self._zone_not_exist(zone_name_class)):
raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't "
"belong to zonemgr" % zone_name_class)
- return
self.zonemgr_reload_zone(zone_name_class)
self._set_zone_refresh_timer(zone_name_class)
self._set_zone_state(zone_name_class, ZONE_OK)
@@ -169,7 +169,6 @@ class ZonemgrRefresh:
if (self._zone_not_exist(zone_name_class)):
raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't "
"belong to zonemgr" % zone_name_class)
- return
# Is zone expired?
if (self._zone_is_expired(zone_name_class)):
self._set_zone_state(zone_name_class, ZONE_EXPIRED)
@@ -182,7 +181,6 @@ class ZonemgrRefresh:
if (self._zone_not_exist(zone_name_class)):
raise ZonemgrException("[b10-zonemgr] Notified zone (%s, %s) "
"doesn't belong to zonemgr" % zone_name_class)
- return
self._set_zone_notifier_master(zone_name_class, master)
self._set_zone_notify_timer(zone_name_class)
@@ -193,6 +191,7 @@ class ZonemgrRefresh:
def zonemgr_add_zone(self, zone_name_class):
""" Add a zone into zone manager."""
+ log_msg("Loading zone (%s, %s)" % zone_name_class)
zone_info = {}
zone_soa = sqlite3_ds.get_zone_soa(str(zone_name_class[0]), self._db_file)
if not zone_soa:
@@ -200,17 +199,11 @@ class ZonemgrRefresh:
zone_info["zone_soa_rdata"] = zone_soa[7]
zone_info["zone_state"] = ZONE_OK
zone_info["last_refresh_time"] = self._get_current_time()
- zone_info["next_refresh_time"] = self._get_current_time() + \
- float(zone_soa[7].split(" ")[REFRESH_OFFSET])
self._zonemgr_refresh_info[zone_name_class] = zone_info
-
- def _build_zonemgr_refresh_info(self):
- """ Build zonemgr refresh info map."""
- log_msg("Start loading zone into zonemgr.")
- for zone_name, zone_class in sqlite3_ds.get_zones_info(self._db_file):
- zone_name_class = (zone_name, zone_class)
- self.zonemgr_add_zone(zone_name_class)
- log_msg("Finish loading zone into zonemgr.")
+ # Imposes some random jitters to avoid many zones need to do refresh at the same time.
+ zone_reload_jitter = float(zone_soa[7].split(" ")[RETRY_OFFSET])
+ zone_reload_jitter = max(self._lowerbound_retry, zone_reload_jitter)
+ self._set_zone_timer(zone_name_class, zone_reload_jitter, self._reload_jitter * zone_reload_jitter)
def _zone_is_expired(self, zone_name_class):
"""Judge whether a zone is expired or not."""
@@ -416,10 +409,73 @@ class ZonemgrRefresh:
def update_config_data(self, new_config):
""" update ZonemgrRefresh config """
- self._lowerbound_refresh = new_config.get('lowerbound_refresh')
- self._lowerbound_retry = new_config.get('lowerbound_retry')
- self._max_transfer_timeout = new_config.get('max_transfer_timeout')
- self._jitter_scope = new_config.get('jitter_scope')
+ # TODO: we probably want to store all this info in a nice
+ # class, so that we don't have to backup and restore every
+ # single value.
+ # TODO2: We also don't use get_default_value yet
+ backup = self._zonemgr_refresh_info.copy()
+
+ # Get a new value, but only if it is defined (commonly used below)
+ # We don't use "value or default", because if value would be
+ # 0, we would take default
+ def val_or_default(value, default):
+ if value is not None:
+ return value
+ else:
+ return default
+
+ # store the values so we can restore them if there is a problem
+ lowerbound_refresh_backup = self._lowerbound_refresh
+ self._lowerbound_refresh = val_or_default(
+ new_config.get('lowerbound_refresh'), self._lowerbound_refresh)
+
+ lowerbound_retry_backup = self._lowerbound_retry
+ self._lowerbound_retry = val_or_default(
+ new_config.get('lowerbound_retry'), self._lowerbound_retry)
+
+ max_transfer_timeout_backup = self._max_transfer_timeout
+ self._max_transfer_timeout = val_or_default(
+ new_config.get('max_transfer_timeout'), self._max_transfer_timeout)
+
+ refresh_jitter_backup = self._refresh_jitter
+ self._refresh_jitter = val_or_default(
+ new_config.get('refresh_jitter'), self._refresh_jitter)
+
+ reload_jitter_backup = self._reload_jitter
+ self._reload_jitter = val_or_default(
+ new_config.get('reload_jitter'), self._reload_jitter)
+ try:
+ required = {}
+ secondary_zones = new_config.get('secondary_zones')
+ if secondary_zones is not None:
+ # Add new zones
+ for secondary_zone in new_config.get('secondary_zones'):
+ name = secondary_zone['name']
+ # Be tolerant to sclerotic users who forget the final dot
+ if name[-1] != '.':
+ name = name + '.'
+ name_class = (name, secondary_zone['class'])
+ required[name_class] = True
+ # Add it only if it isn't there already
+ if not name_class in self._zonemgr_refresh_info:
+ self.zonemgr_add_zone(name_class)
+ # Drop the zones that are no longer there
+ # Do it in two phases, python doesn't like deleting while iterating
+ to_drop = []
+ for old_zone in self._zonemgr_refresh_info:
+ if not old_zone in required:
+ to_drop.append(old_zone)
+ for drop in to_drop:
+ del self._zonemgr_refresh_info[drop]
+ # If we are not able to find it in database, restore the original
+ except:
+ self._zonemgr_refresh_info = backup
+ self._lowerbound_refresh = lowerbound_refresh_backup
+ self._lowerbound_retry = lowerbound_retry_backup
+ self._max_transfer_timeout = max_transfer_timeout_backup
+ self._refresh_jitter = refresh_jitter_backup
+ self._reload_jitter = reload_jitter_backup
+ raise
class Zonemgr:
"""Zone manager class."""
@@ -472,26 +528,37 @@ class Zonemgr:
def config_handler(self, new_config):
""" Update config data. """
answer = create_answer(0)
+ ok = True
+ complete = self._config_data.copy()
for key in new_config:
- if key not in self._config_data:
+ if key not in complete:
answer = create_answer(1, "Unknown config data: " + str(key))
+ ok = False
continue
- self._config_data[key] = new_config[key]
+ complete[key] = new_config[key]
- self._config_data_check(self._config_data)
- if (self._zone_refresh):
- self._zone_refresh.update_config_data(self._config_data)
+ self._config_data_check(complete)
+ if self._zone_refresh is not None:
+ try:
+ self._zone_refresh.update_config_data(complete)
+ except Exception as e:
+ answer = create_answer(1, str(e))
+ ok = False
+ if ok:
+ self._config_data = complete
return answer
def _config_data_check(self, config_data):
- """Check whether the new config data is valid or
- not. """
+ """Check whether the new config data is valid or
+ not. It contains only basic logic, not full check against
+ database."""
# jitter should not be bigger than half of the original value
- if config_data.get('jitter_scope') > 0.5:
- config_data['jitter_scope'] = 0.5
- log_msg("[b10-zonemgr] jitter_scope is too big, its value will "
- "be set to 0.5")
+ if config_data.get('refresh_jitter') > 0.5:
+ config_data['refresh_jitter'] = 0.5
+ log_msg("[b10-zonemgr] refresh_jitter is too big, its value will "
+ "be set to 0.5")
+
def _parse_cmd_params(self, args, command):
zone_name = args.get("zone_name")
diff --git a/src/bin/zonemgr/zonemgr.spec.pre.in b/src/bin/zonemgr/zonemgr.spec.pre.in
index 0539ef3..36f02df 100644
--- a/src/bin/zonemgr/zonemgr.spec.pre.in
+++ b/src/bin/zonemgr/zonemgr.spec.pre.in
@@ -12,19 +12,51 @@
"item_name": "lowerbound_retry",
"item_type": "integer",
"item_optional": false,
- "item_default": 5
+ "item_default": 5
},
{
"item_name": "max_transfer_timeout",
"item_type": "integer",
"item_optional": false,
- "item_default": 14400
+ "item_default": 14400
},
{
- "item_name": "jitter_scope",
+ "item_name": "refresh_jitter",
"item_type": "real",
"item_optional": false,
"item_default": 0.25
+ },
+ {
+ "item_name": "reload_jitter",
+ "item_type": "real",
+ "item_optional": false,
+ "item_default": 0.75
+ },
+ {
+ "item_name": "secondary_zones",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "secondary_zone",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ {
+ "item_name": "class",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "IN"
+ },
+ {
+ "item_name": "name",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ }
+ ]
+ }
}
],
"commands": [
diff --git a/src/cppcheck-suppress.lst b/src/cppcheck-suppress.lst
index 3e4dcd6..a4fea30 100644
--- a/src/cppcheck-suppress.lst
+++ b/src/cppcheck-suppress.lst
@@ -3,13 +3,8 @@
debug
missingInclude
// This is a template, and should be excluded from the check
-unreadVariable:src/lib/dns/rdata/template.cc:59
-// These three trigger warnings due to the incomplete implementation. This is
-// our problem, but we need to suppress the warnings for now.
-functionConst:src/lib/cache/resolver_cache.h
-functionConst:src/lib/cache/message_cache.h
-functionConst:src/lib/cache/rrset_cache.h
+unreadVariable:src/lib/dns/rdata/template.cc:60
// Intentional self assignment tests. Suppress warning about them.
-selfAssignment:src/lib/dns/tests/name_unittest.cc:292
-selfAssignment:src/lib/dns/tests/rdata_unittest.cc:227
-selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:104
+selfAssignment:src/lib/dns/tests/name_unittest.cc:293
+selfAssignment:src/lib/dns/tests/rdata_unittest.cc:228
+selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:137
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 8525b8d..f4bef6b 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -1,2 +1,3 @@
-SUBDIRS = exceptions dns cc config python xfr bench log asiolink \
- nsas cache resolve testutils datasrc server_common
+SUBDIRS = exceptions util log cryptolink dns cc config python xfr \
+ bench asiolink asiodns nsas cache resolve testutils datasrc \
+ acl server_common
diff --git a/src/lib/acl/Makefile.am b/src/lib/acl/Makefile.am
new file mode 100644
index 0000000..f211025
--- /dev/null
+++ b/src/lib/acl/Makefile.am
@@ -0,0 +1,27 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+# The core library
+lib_LTLIBRARIES = libacl.la
+libacl_la_SOURCES = acl.h
+libacl_la_SOURCES += check.h
+libacl_la_SOURCES += ip_check.h ip_check.cc
+libacl_la_SOURCES += logic_check.h
+libacl_la_SOURCES += loader.h loader.cc
+
+libacl_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
+libacl_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la
+libacl_la_LIBADD += $(top_builddir)/src/lib/util/libutil.la
+
+# DNS specialized one
+lib_LTLIBRARIES += libdnsacl.la
+
+libdnsacl_la_SOURCES = dns.h dns.cc
+
+libdnsacl_la_LIBADD = libacl.la
+libdnsacl_la_LIBADD += $(top_builddir)/src/lib/dns/libdns++.la
+
+CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/acl/acl.h b/src/lib/acl/acl.h
new file mode 100644
index 0000000..998b2b0
--- /dev/null
+++ b/src/lib/acl/acl.h
@@ -0,0 +1,140 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef ACL_ACL_H
+#define ACL_ACL_H
+
+#include "check.h"
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+namespace isc {
+namespace acl {
+
+/**
+ * \brief Default actions an ACL could perform.
+ *
+ * This is the default for the ACL class. It is possible to specify any other
+ * data type, as the ACL class does nothing about them, but these look
+ * reasonable, so they are provided for convenience. It is not specified what
+ * exactly these mean and it's up to whoever uses them.
+ */
+enum BasicAction {
+ ACCEPT,
+ REJECT,
+ DROP
+};
+
+/**
+ * \brief The ACL itself.
+ *
+ * It holds bunch of ordered entries, each one consisting of a check (
+ * of any kind, it might be even compound) and an action that is returned
+ * whenever the action matches. They are tested in the order and first
+ * match counts.
+ *
+ * This is non-copyable. It seems that there's no need to copy them (even
+ * when it would be technically possible), so we forbid it just to prevent
+ * copying it by accident. If there really is legitimate use, this restriction
+ * can be removed.
+ *
+ * The class is template. It is possible to specify on which context the checks
+ * match and which actions it returns. The actions must be copyable
+ * for this to work and it is expected to be something small, usually an enum
+ * (but other objects are also possible).
+ *
+ * \note There are protected functions. In fact, you should consider them
+ * private, they are protected so tests can get inside. This class
+ * is not expected to be subclassed in real applications.
+ */
+template<typename Context, typename Action = BasicAction> class ACL :
+ public boost::noncopyable {
+public:
+ /**
+ * \brief Constructor.
+ *
+ * \param default_action It is the action that is returned when the checked
+ * things "falls off" the end of the list (when no rule matched).
+ */
+ ACL(const Action& default_action) : default_action_(default_action)
+ {}
+
+ /**
+ * \brief Pointer to the check.
+ *
+ * We use the shared pointer, because we are not able to copy the checks.
+ * However, we might need to copy the entries (when we concatenate ACLs
+ * together in future).
+ */
+ typedef boost::shared_ptr<const Check<Context> > ConstCheckPtr;
+
+ /**
+ * \brief The actual main function that decides.
+ *
+ * This is the function that takes the entries one by one, checks
+ * the context against conditions and if it matches, returns the
+ * action that belongs to the first matched entry or default action
+ * if nothing matches.
+ * \param context The thing that should be checked. It is directly
+ * passed to the checks.
+ */
+ const Action& execute(const Context& context) const {
+ const typename Entries::const_iterator end(entries_.end());
+ for (typename Entries::const_iterator i(entries_.begin()); i != end;
+ ++i) {
+ if (i->first->matches(context)) {
+ return (i->second);
+ }
+ }
+ return (default_action_);
+ }
+
+ /**
+ * \brief Add new entry at the end of the list.
+ *
+ * \note We currently allow only adding at the end. This is enough for now,
+ * but we may need more when we start implementing some kind optimisations,
+ * including replacements, reorderings and removals.
+ *
+ * \param check The check to test if the thing matches.
+ * \param action The action to return when the thing matches this check.
+ */
+ void append(ConstCheckPtr check, const Action& action) {
+ entries_.push_back(Entry(check, action));
+ }
+private:
+ // Just type abbreviations.
+ typedef std::pair<ConstCheckPtr, Action> Entry;
+ typedef std::vector<Entry> Entries;
+ /// \brief The default action, when nothing mathes.
+ const Action default_action_;
+ /// \brief The entries we have.
+ Entries entries_;
+protected:
+ /**
+ * \brief Get the default action.
+ *
+ * This is for testing purposes only.
+ */
+ const Action& getDefaultAction() const {
+ return (default_action_);
+ }
+};
+
+}
+}
+
+#endif
diff --git a/src/lib/acl/check.h b/src/lib/acl/check.h
new file mode 100644
index 0000000..3297d4b
--- /dev/null
+++ b/src/lib/acl/check.h
@@ -0,0 +1,195 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef ACL_CHECK_H
+#define ACL_CHECK_H
+
+#include <vector>
+#include <typeinfo>
+#include <sstream>
+
+namespace isc {
+namespace acl {
+
+/**
+ * \brief ACL check base class.
+ *
+ * It is intended that all ACL checks are inherited (maybe indirectly) from
+ * this base class. This will allow us to define new types of checks without
+ * changing any of the code that is using it and with the correct
+ * implementation even without changing the thing that parses configuration
+ * and creates instances of the checks.
+ *
+ * It is implemented as a template. This allows easy reuse of the code for
+ * checking of different types of things (packets of different protocols, etc).
+ * We'll implement the loader and compound checks as templates as well (
+ * and just make sure they are instantiated for each type of thing we want
+ * to check). While most of concrete checks will be specific for one protocol
+ * (or whatever the entity we check is), it makes sense to implement some of
+ * these as templates as well (for example the IP address check, for whatever
+ * context that contains member called ip and has the right methods).
+ *
+ * The Context carries whatever information might be checked for that protocol
+ * (eg. the packet, information where it came from, to what port, ...).
+ */
+template<typename Context> class Check {
+protected:
+ /// \brief Constructor.
+ ///
+ /// Just to make sure this thing is not directly instantiated.
+ Check() { }
+public:
+ /**
+ * \brief The check itself.
+ *
+ * The actual check will be performed here. Every concrete child class
+ * will reimplement it and decide based on the context passed if it
+ * matches.
+ *
+ * The caller should expect this method can throw. The list of exceptions
+ * isn't restricted, as we don't know what kind of checks will be needed.
+ * An exception should be considered as it is impossible to check the
+ * condition. It should lead to either blackholing the packet or returning
+ * some 500-like error (ServFail).
+ *
+ * \param context The thing we are trying to match against this check.
+ * \return true if the context satisfies the check, false otherwise.
+ */
+ virtual bool matches(const Context& context) const = 0;
+
+ /**
+ * \brief Cost for unknown cost estimate.
+ *
+ * This indicates that the estimate for cost is not provided. This
+ * is arbitrary large value, meaning "somehow longish time". To be
+ * on the safe side, we guess more and be just happily suprirised
+ * if it turns out to run faster.
+ */
+ static const unsigned UNKNOWN_COST;
+
+ /**
+ * \brief The expected cost of single match.
+ *
+ * This is here to provide some kind of cost information to optimising
+ * routines. It is in units without any real size, just bigger number
+ * means the check takes longer time. It is expected to be linear scale.
+ * It doesn't need to be exact, but better accuracy might lead to better
+ * optimisations. As of writing this, no optimisations exist yet, but
+ * are expected to exist in future.
+ *
+ * The default is UNKNOWN_COST.
+ */
+ virtual unsigned cost() const {
+ return (UNKNOWN_COST);
+ }
+
+ /// \brief Virtual destructor, as we're virtual
+ virtual ~ Check() { }
+
+ /**
+ * \brief Conversion to text.
+ *
+ * This is meant for debugging purposes, it doesn't have to
+ * serialise the whole information stored in this Check.
+ *
+ * If the check is compound, it should not include the subexpressions
+ * (while we're able to build whatever treeish representation using
+ * CompoundCheck::subexpressions, we're not able to separate them
+ * automatically, as this may produce any kind of free-form string).
+ */
+ virtual std::string toText() const {
+ std::stringstream output;
+ output << typeid(*this).name() << "@" << this;
+ return (output.rdbuf()->str());
+ }
+};
+
+// This seems to be the propper way for static template members
+template<typename Context> const unsigned Check<Context>::UNKNOWN_COST = 10000;
+
+/**
+ * \brief Base class for compound checks.
+ *
+ * While some checks will be a match against some property of the information
+ * passed (eg. the sender's IP address must be in some range), others will
+ * combine results of more checks together to get their own. This is base class
+ * for the second type, allowing listing of the subexpressions (mostly for
+ * debugging purposes to print the whole tree of matches and possible future
+ * optimisations which would like to crawl the expression tree).
+ */
+template<typename Context> class CompoundCheck : public Check<Context> {
+public:
+ /// \brief Abbreviated name for list of subexpressions
+ typedef std::vector<const Check<Context>*> Checks;
+
+ /**
+ * \brief Get the list of subexpressions.
+ *
+ * The result contains pointers to the all subexpressions this check holds
+ * (and therefore might call during its own match() function).
+ *
+ * Using shared pointers looks an overkill here. All the checks must be
+ * alive for the whole life of this one and this check will hold their
+ * ownership. Therefore the only thing the caller needs to do is to make
+ * sure this check is not deleted while it's still using the ones from the
+ * result.
+ *
+ * This method must not throw except for the standard allocation exceptions
+ * to allocate the result.
+ */
+ virtual Checks getSubexpressions() const = 0;
+
+ /**
+ * \brief If the result depends only on results of subexpressions.
+ *
+ * Some optimisations might use the fact that a compound expression is
+ * a function of results of its subexpressions (subchecks) only. But
+ * some compound checks might want to look into the provided context in
+ * their match() as well as looking at the results of the subexpressions.
+ *
+ * This function informs the optimisation routines if it is safe to use
+ * these optimisations.
+ *
+ * \return true if the check depends only on results of subexpressions
+ * only, false if it examines the context itself as well.
+ * \note The default implementation returns true, as it is expected to
+ * be the case in majority of cases.
+ */
+ virtual bool pure() const { return (true); }
+
+ /**
+ * \brief Default compound cost function.
+ *
+ * It is simply sum of all subexpressions, as an expected upper bound
+ * on the cost. This expects that the combining itself is cheap relatively
+ * to the checks performed by the subexpressions. In most cases, this
+ * should be good enough, but it can be reimplemented in situations
+ * where most of the subexpressions will be avoided in usual situations.
+ * Replacing the default of 10000 from Check.
+ */
+ virtual unsigned cost() const {
+ Checks checks(getSubexpressions());
+ unsigned result(0);
+ for (typename Checks::const_iterator i(checks.begin());
+ i != checks.end(); ++ i) {
+ result += (*i)->cost();
+ }
+ return (result);
+ }
+};
+
+}
+}
+
+#endif
diff --git a/src/lib/acl/dns.cc b/src/lib/acl/dns.cc
new file mode 100644
index 0000000..cb948eb
--- /dev/null
+++ b/src/lib/acl/dns.cc
@@ -0,0 +1,121 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <cc/data.h>
+
+#include <acl/dns.h>
+#include <acl/ip_check.h>
+#include <acl/loader.h>
+#include <acl/logic_check.h>
+
+using namespace std;
+using boost::shared_ptr;
+using namespace isc::data;
+
+namespace isc {
+namespace acl {
+
+/// The specialization of \c IPCheck for access control with \c RequestContext.
+///
+/// It returns \c true if the remote (source) IP address of the request
+/// matches the expression encapsulated in the \c IPCheck, and returns
+/// \c false if not.
+///
+/// \note The match logic is expected to be extended as we add
+/// more match parameters (at least there's a plan for TSIG key).
+template <>
+bool
+IPCheck<dns::RequestContext>::matches(
+ const dns::RequestContext& request) const
+{
+ return (compare(request.remote_address.getData(),
+ request.remote_address.getFamily()));
+}
+
+namespace dns {
+
+vector<string>
+internal::RequestCheckCreator::names() const {
+ // Probably we should eventually build this vector in a more
+ // sophisticated way. For now, it's simple enough to hardcode
+ // everything.
+ vector<string> supported_names;
+ supported_names.push_back("from");
+ return (supported_names);
+}
+
+shared_ptr<RequestCheck>
+internal::RequestCheckCreator::create(const string& name,
+ ConstElementPtr definition,
+ // unused:
+ const acl::Loader<RequestContext>&)
+{
+ if (!definition) {
+ isc_throw(LoaderError,
+ "NULL pointer is passed to RequestCheckCreator");
+ }
+
+ if (name == "from") {
+ return (shared_ptr<internal::RequestIPCheck>(
+ new internal::RequestIPCheck(definition->stringValue())));
+ } else {
+ // This case shouldn't happen (normally) as it should have been
+ // rejected at the loader level. But we explicitly catch the case
+ // and throw an exception for that.
+ isc_throw(LoaderError, "Invalid check name for RequestCheck: " <<
+ name);
+ }
+}
+
+RequestLoader&
+getRequestLoader() {
+ static RequestLoader* loader(NULL);
+ if (loader == NULL) {
+ // Creator registration may throw, so we first store the new loader
+ // in an auto pointer in order to provide the strong exception
+ // guarantee.
+ auto_ptr<RequestLoader> loader_ptr =
+ auto_ptr<RequestLoader>(new RequestLoader(REJECT));
+
+ // Register default check creator(s)
+ loader_ptr->registerCreator(shared_ptr<internal::RequestCheckCreator>(
+ new internal::RequestCheckCreator()));
+ loader_ptr->registerCreator(
+ shared_ptr<NotCreator<RequestContext> >(
+ new NotCreator<RequestContext>("NOT")));
+ loader_ptr->registerCreator(
+ shared_ptr<LogicCreator<AnyOfSpec, RequestContext> >(
+ new LogicCreator<AnyOfSpec, RequestContext>("ANY")));
+ loader_ptr->registerCreator(
+ shared_ptr<LogicCreator<AllOfSpec, RequestContext> >(
+ new LogicCreator<AllOfSpec, RequestContext>("ALL")));
+
+ // From this point there shouldn't be any exception thrown
+ loader = loader_ptr.release();
+ }
+
+ return (*loader);
+}
+
+}
+}
+}
diff --git a/src/lib/acl/dns.h b/src/lib/acl/dns.h
new file mode 100644
index 0000000..118e5fd
--- /dev/null
+++ b/src/lib/acl/dns.h
@@ -0,0 +1,140 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef ACL_DNS_H
+#define ACL_DNS_H 1
+
+#include <string>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include <cc/data.h>
+
+#include <acl/ip_check.h>
+#include <acl/loader.h>
+
+namespace isc {
+namespace acl {
+namespace dns {
+
+/**
+ * \brief DNS request to be checked.
+ *
+ * This plays the role of Context of the generic template ACLs (in namespace
+ * isc::acl).
+ *
+ * It is a simple structure holding just the bunch of information. Therefore
+ * the names don't end up with an underscore; there are no methods so they
+ * can't be confused with local variables.
+ *
+ * This structure is generally expected to be ephemeral and read-only: It
+ * would be constructed immediately before a particular ACL is checked
+ * and used only for the ACL match purposes. Due to this nature, and since
+ * ACL processing is often performance sensitive (typically it's performed
+ * against all incoming packets), the construction is designed to be
+ * lightweight: it tries to avoid expensive data copies or dynamic memory
+ * allocation as much as possible. Specifically, the constructor can
+ * take a pointer or reference to an object and keeps it as a reference
+ * (not making a local copy). This also means the caller is responsible for
+ * keeping the passed parameters valid while this structure is used.
+ * This should generally be reasonable as this structure is expected to be
+ * used only for a very short period as stated above.
+ *
+ * Based on the minimalist philosophy, the initial implementation only
+ * maintains the remote (source) IP address of the request. The plan is
+ * to add more parameters of the request. A scheduled next step is to
+ * support the TSIG key (if it's included in the request). Other possibilities
+ * are the local (destination) IP address, the remote and local port numbers,
+ * various fields of the DNS request (e.g. a particular header flag value).
+ */
+struct RequestContext {
+ /// The constructor
+ ///
+ /// This is a trivial constructor that perform straightforward
+ /// initialization of the member variables from the given parameters.
+ ///
+ /// \exception None
+ ///
+ /// \parameter remote_address_param The remote IP address
+ explicit RequestContext(const IPAddress& remote_address_param) :
+ remote_address(remote_address_param)
+ {}
+
+ ///
+ /// \name Parameter variables
+ ///
+ /// These member variables must be immutable so that the integrity of
+ /// the structure is kept throughout its lifetime. The easiest way is
+ /// to declare the variable as const. If it's not possible for a
+ /// particular variable, it must be defined as private and accessible
+ /// only via an accessor method.
+ //@{
+ /// \brief The remote IP address (eg. the client's IP address).
+ const IPAddress& remote_address;
+ //@}
+};
+
+/// \brief DNS based check.
+typedef acl::Check<RequestContext> RequestCheck;
+/// \brief DNS based compound check.
+typedef acl::CompoundCheck<RequestContext> CompoundCheck;
+/// \brief DNS based ACL.
+typedef acl::ACL<RequestContext> RequestACL;
+/// \brief DNS based ACL loader.
+typedef acl::Loader<RequestContext> RequestLoader;
+
+/**
+ * \brief Loader singleton access function.
+ *
+ * This function returns a loader of ACLs. It is expected applications
+ * will use this function instead of creating their own loaders, because
+ * one is enough, this one will have registered default checks and it
+ * is known one, so any plugins can registrer additional checks as well.
+ */
+RequestLoader& getRequestLoader();
+
+// The following is essentially private to the implementation and could
+// be hidden in the implementation file. But it's visible via this header
+// file for testing purposes. They are not supposed to be used by normal
+// applications directly, and to signal the intent, they are given inside
+// a separate namespace.
+namespace internal {
+
+// Shortcut typedef
+typedef isc::acl::IPCheck<RequestContext> RequestIPCheck;
+
+class RequestCheckCreator : public acl::Loader<RequestContext>::CheckCreator {
+public:
+ virtual std::vector<std::string> names() const;
+
+ virtual boost::shared_ptr<RequestCheck>
+ create(const std::string& name, isc::data::ConstElementPtr definition,
+ const acl::Loader<RequestContext>& loader);
+
+ /// Until we are sure how the various rules work for this case, we won't
+ /// allow unexpected special interpretation for list definitions.
+ virtual bool allowListAbbreviation() const { return (false); }
+};
+} // end of namespace "internal"
+
+} // end of namespace "dns"
+} // end of namespace "acl"
+} // end of namespace "isc"
+
+#endif
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/acl/ip_check.cc b/src/lib/acl/ip_check.cc
new file mode 100644
index 0000000..76aacca
--- /dev/null
+++ b/src/lib/acl/ip_check.cc
@@ -0,0 +1,141 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <sys/socket.h>
+
+#include <exceptions/exceptions.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <acl/ip_check.h>
+
+using namespace std;
+using namespace isc;
+
+namespace isc {
+namespace acl {
+namespace internal {
+
+uint8_t
+createMask(size_t prefixlen) {
+
+ if (prefixlen == 0) {
+ return (0);
+
+ } else if (prefixlen <= 8) {
+
+ // In the following discussion:
+ //
+ // w is the width of the data type in bits.
+ // m is the value of prefixlen, the number of most signifcant bits we
+ // want to set.
+ // ** is exponentiation (i.e. 2**n is 2 raised to the power of n).
+ //
+ // We note that the value of 2**m - 1 gives a value with the least
+ // significant m bits set. For a data type of width w, this means that
+ // the most signficant (w-m) bits are clear.
+ //
+ // Hence the value 2**(w-m) - 1 gives a result with the least signficant
+ // w-m bits set and the most significant m bits clear. The 1's
+ // complement of this value gives is the result we want.
+ //
+ // Final note: at this point in the logic, m is non-zero, so w-m < w.
+ // This means 1<<(w-m) will fit into a variable of width w bits. In
+ // other words, in the expression below, no term will cause an integer
+ // overflow.
+ return (~((1 << (8 - prefixlen)) - 1));
+ }
+
+ // Mask size is too large. (Note that prefixlen is unsigned, so can't be
+ // negative.)
+ isc_throw(isc::OutOfRange, "prefixlen argument must be between 0 and 8");
+}
+
+pair<string, int>
+splitIPAddress(const string& ipprefix) {
+
+ // Split string into its components - an address and a prefix length.
+ // We initialize by assuming that there is no slash in the string given.
+ string address = ipprefix;
+ string prefixlen = "";
+
+ const size_t slashpos = ipprefix.find('/');
+ if ((ipprefix.size() == 0) || (slashpos == 0) ||
+ (slashpos == (ipprefix.size() - 1))) {
+ // Nothing in prefix, or it starts with or ends with a slash.
+ isc_throw(isc::InvalidParameter, "address prefix of " << ipprefix <<
+ " is not valid");
+
+ } else if (slashpos != string::npos) {
+ // There is a slash somewhere in the string, split the string on it.
+ // Don't worry about multiple slashes - if there are some, they will
+ // appear in the prefixlen segment and will be detected when an attempt
+ // is made to convert it to a number.
+ address = ipprefix.substr(0, slashpos);
+ prefixlen = ipprefix.substr(slashpos + 1);
+ }
+
+ // Set the default value for the prefix length. As the type of the address
+ // is not known at the point this function is called, the maximum
+ // allowable value is also not known. The value of 0 is reserved for
+ // a "match any address" match.
+ int prefix_size = -1;
+
+ // If there is a prefixlength, attempt to convert it.
+ if (!prefixlen.empty()) {
+ try {
+ prefix_size = boost::lexical_cast<int>(prefixlen);
+ if (prefix_size < 0) {
+ isc_throw(isc::InvalidParameter, "address prefix of " <<
+ ipprefix << " is not valid");
+ }
+ } catch (boost::bad_lexical_cast&) {
+ isc_throw(isc::InvalidParameter, "prefix length of '" <<
+ prefixlen << "' is not valid");
+ }
+ }
+
+ return (make_pair(address, prefix_size));
+}
+} // namespace internal
+
+namespace {
+const uint8_t*
+getSockAddrData(const struct sockaddr& sa) {
+ const void* sa_ptr = &sa;
+ const void* data_ptr;
+ if (sa.sa_family == AF_INET) {
+ const struct sockaddr_in* sin =
+ static_cast<const struct sockaddr_in*>(sa_ptr);
+ data_ptr = &sin->sin_addr;
+ } else if (sa.sa_family == AF_INET6) {
+ const struct sockaddr_in6* sin6 =
+ static_cast<const struct sockaddr_in6*>(sa_ptr);
+ data_ptr = &sin6->sin6_addr;
+ } else {
+ isc_throw(BadValue, "Unsupported address family for IPAddress: " <<
+ static_cast<int>(sa.sa_family));
+ }
+ return (static_cast<const uint8_t*>(data_ptr));
+}
+}
+
+IPAddress::IPAddress(const struct sockaddr& sa) :
+ family(sa.sa_family),
+ data(getSockAddrData(sa)),
+ length(family == AF_INET ?
+ sizeof(struct in_addr) : sizeof(struct in6_addr))
+{}
+} // namespace acl
+} // namespace isc
diff --git a/src/lib/acl/ip_check.h b/src/lib/acl/ip_check.h
new file mode 100644
index 0000000..794b943
--- /dev/null
+++ b/src/lib/acl/ip_check.h
@@ -0,0 +1,417 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __IP_CHECK_H
+#define __IP_CHECK_H
+
+#include <sys/socket.h>
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <vector>
+
+#include <boost/static_assert.hpp>
+
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <sys/socket.h> // for AF_INET/AF_INET6
+#include <netinet/in.h>
+
+#include <acl/check.h>
+#include <exceptions/exceptions.h>
+#include <util/strutil.h>
+
+namespace isc {
+namespace acl {
+
+// Free functions. These are not supposed to be used outside this module,
+// but are declared public for testing. To try to conceal them, they are
+// put in an "internal" namespace.
+
+namespace internal {
+
+/// \brief Convert prefix length to mask
+///
+/// Given a prefix length and a data type, return a value of that data type
+/// with the most significant "prefix length" bits set. For example, if the
+/// data type is an uint8_t and the prefix length is 3, the function would
+/// return a uint8_t holding the binary value 11100000. This value is used as
+/// a mask in the address checks.
+///
+/// \param prefixlen number of bits to be set in the mask. This must be
+/// between 0 and 8.
+///
+/// \return uint8_t with the most significant "prefixlen" bits set.
+///
+/// \exception OutOfRange prefixlen is too large for the data type.
+
+uint8_t createMask(size_t prefixlen);
+
+/// \brief Split IP Address Prefix
+///
+/// Splits an IP address prefix (given in the form of "xxxxxx/n" or "xxxxx" into
+/// a string representing the IP address and a number giving the length of the
+/// prefix. (In the latter case, the prefix is equal in length to the width in
+/// width in bits of the data type holding the address.) An exception will be
+/// thrown if the string format is invalid or if the prefix length is invalid.
+///
+/// N.B. This function does NOT check that the address component is a valid IP
+/// address; this is done elsewhere in the address parsing process.
+///
+/// \param ipprefix Address or address prefix. The string should be passed
+/// without leading or trailing spaces.
+///
+/// \return Pair of (string, int) holding the address string and the prefix
+/// length. The second element is -1 if no prefix was given.
+///
+/// \exception InvalidParameter Address prefix not of the expected syntax
+
+std::pair<std::string, int>
+splitIPAddress(const std::string& ipprefix);
+
+} // namespace internal
+
+/// \brief A simple representation of IP address.
+///
+/// This structure provides address family independent interfaces of an
+/// IP(v4 or v6) address, so that the application can perform
+/// \c IPCheck::matches without knowing which version of address it is
+/// handling. (For example, consider the standard socket API: it uses
+/// the generic \c sockaddr structure to represent endpoints).
+///
+/// An object of this class could be constructed from various types of
+/// sources, but in the initial implementation there's only one constructor,
+/// which takes a \c sockaddr structure. For efficiency the \c IPAddress
+/// object only retains a reference to the necessary part of \c sockaddr.
+/// Therefore the corresponding \c sockaddr instance must be valid while the
+/// \c IPAddress object is used.
+///
+/// This class is copyable so that a fixed object can be easily reused for
+/// different addresses. To ensure internal integrity, specific member
+/// variables are kept private and only accessible via read-only accessor
+/// methods. Due to this, it is ensured, for example, that if \c getFamily()
+/// returns \c AF_INET6, \c getLength() always returns 16.
+///
+/// All accessor methods are straightforward and exception free.
+///
+/// In future, we may introduce the default constructor to further improve
+/// reusability.
+struct IPAddress {
+ /// The constructor from socket address structure.
+ ///
+ /// This constructor set up the internal data based on the actual type
+ /// \c sa. For example, if \c sa.sa_family is \c AF_INET, it assumes
+ /// \c sa actually refers to a \c sockaddr_in structure.
+ /// The behavior when this assumption isn't held is undefined.
+ ///
+ /// \param sa A reference to the socket address structure from which the
+ /// \c IPAddress is to be constructed.
+ explicit IPAddress(const struct sockaddr& sa);
+
+ /// Return the address family of the address
+ ///
+ /// It's AF_INET for IPv4 and AF_INET6 for IPv6.
+ int getFamily() const { return (family); }
+
+ /// Return the binary representation of the address in network byte order.
+ ///
+ /// Only the \c getLength() bytes from the returned pointer are ensured
+ /// to be valid. In addition, if the \c sockaddr structure given on
+ /// construction was dynamically allocated, the data is valid only until
+ /// the \c sockaddr is invalidated.
+ const uint8_t* getData() const { return (data); }
+
+ /// Return the length of the address.
+ size_t getLength() const { return (length); }
+private:
+ int family;
+ const uint8_t* data;
+ size_t length;
+};
+
+/// \brief IP Check
+///
+/// This class performs a match between an IP address prefix specified in an ACL
+/// and a given IP address. The check works for both IPv4 and IPv6 addresses.
+///
+/// The class is templated on the type of a context structure passed to the
+/// matches() method, and a template specialisation for that method must be
+/// supplied for the class to be used.
+
+template <typename Context>
+class IPCheck : public Check<Context> {
+private:
+ // Size of uint8_t array needed to hold different address types
+ static const size_t IPV6_SIZE = sizeof(struct in6_addr);
+ static const size_t IPV4_SIZE = sizeof(struct in_addr);
+
+ // Confirm our assumption of relative sizes - this allows us to assume that
+ // an array sized for an IPv6 address can hold an IPv4 address.
+ BOOST_STATIC_ASSERT(sizeof(struct in6_addr) > sizeof(struct in_addr));
+
+public:
+ /// \brief String Constructor
+ ///
+ /// Constructs an IP Check object from an address or address prefix in the
+ /// form <ip-address>/n".
+ ///
+ /// Also allowed are the special keywords "any4" and "any6", which match
+ /// any IPv4 or IPv6 address. These must be specified in lowercase.
+ ///
+ /// \param ipprefix IP address prefix in the form "<ip-address>/n"
+ /// (where the "/n" part is optional and should be valid for the
+ /// address). If "n" is specified as zero, the match is for any
+ /// address in that address family. The address can also be
+ /// given as "any4" or "any6".
+ IPCheck(const std::string& ipprefix) : family_(0) {
+
+ // Ensure array elements are correctly initialized with zeroes.
+ std::fill(address_, address_ + IPV6_SIZE, 0);
+ std::fill(mask_, mask_ + IPV6_SIZE, 0);
+
+ // Only deal with the string after we've removed leading and trailing
+ // spaces.
+ const std::string mod_prefix = isc::util::str::trim(ipprefix);
+
+ // Check for special cases first.
+ if (mod_prefix == "any4") {
+ family_ = AF_INET;
+
+ } else if (mod_prefix == "any6") {
+ family_ = AF_INET6;
+
+ } else {
+
+ // General address prefix. Split into address part and prefix
+ // length.
+ const std::pair<std::string, int> result =
+ internal::splitIPAddress(mod_prefix);
+
+ // Try to convert the address. If successful, the result is in
+ // network-byte order (most significant components at lower
+ // addresses).
+ int status = inet_pton(AF_INET6, result.first.c_str(), address_);
+ if (status == 1) {
+ // It was an IPv6 address.
+ family_ = AF_INET6;
+ } else {
+ // IPv6 interpretation failed, try IPv4.
+ status = inet_pton(AF_INET, result.first.c_str(), address_);
+ if (status == 1) {
+ family_ = AF_INET;
+ }
+ }
+
+ // Handle errors.
+ if (status == 0) {
+ isc_throw(isc::InvalidParameter, "address prefix of " <<
+ ipprefix << " is not valid");
+ } else if (status < 0) {
+ isc_throw(isc::Unexpected, "address conversion of " <<
+ ipprefix << " failed due to a system error");
+ }
+
+ // All done, so set the mask used in the address comparison.
+ setMask(result.second);
+ }
+ }
+
+ /// \brief Destructor
+ virtual ~IPCheck() {}
+
+ /// \brief The check itself
+ ///
+ /// Matches the passed argument to the condition stored here. Different
+ /// specialisations must be provided for different argument types, and the
+ /// program will fail to compile if a required specialisation is not
+ /// provided.
+ ///
+ /// It is expected that matches() will extract the address information from
+ /// the Context structure, and use compare() to actually perform the
+ /// comparison.
+ ///
+ /// \param context Information to be matched
+ virtual bool matches(const Context& context) const;
+
+ /// \brief Estimated cost
+ ///
+ /// Assume that the cost of the match is linear and depends on the
+ /// maximum number of comparison operations.
+ ///
+ /// \return Estimated cost of the comparison
+ virtual unsigned cost() const {
+ return ((family_ == AF_INET) ? IPV4_SIZE : IPV6_SIZE);
+ }
+
+ ///@{
+ /// Access methods - mainly for testing
+
+ /// \return Stored IP address
+ std::vector<uint8_t> getAddress() const {
+ const size_t vector_len = (family_ == AF_INET ? IPV4_SIZE : IPV6_SIZE);
+ return (std::vector<uint8_t>(address_, address_ + vector_len));
+ }
+
+ /// \return Network mask applied to match
+ std::vector<uint8_t> getMask() const {
+ const size_t vector_len = (family_ == AF_INET ? IPV4_SIZE : IPV6_SIZE);
+ return (std::vector<uint8_t>(mask_, mask_ + vector_len));
+ }
+
+ /// \return Prefix length of the match
+ size_t getPrefixlen() const {
+ // Work this out by counting bits in the mask.
+ size_t count = 0;
+ for (size_t i = 0; i < IPV6_SIZE; ++i) {
+ if (mask_[i] == 0xff) {
+ // All bits set in this byte
+ count += 8;
+ continue;
+
+ } else if (mask_[i] != 0) {
+ // Only some bits set in this byte. Count them.
+ uint8_t byte = mask_[i];
+ for (int j = 0; j < 8; ++j) {
+ count += byte & 0x01; // Add one if the bit is set
+ byte >>= 1; // Go for next bit
+ }
+ }
+ break;
+ }
+ return (count);
+ }
+
+ /// \return Address family
+ int getFamily() const {
+ return (family_);
+ }
+ ///@}
+
+protected:
+ /// \brief Comparison
+ ///
+ /// This is the actual comparison function that checks the IP address passed
+ /// to this class with the matching information in the class itself. It is
+ /// expected to be called from matches().
+ ///
+ /// \param testaddr Address (in network byte order) to test against the
+ /// check condition in the class. This is expected to
+ /// be IPV6_SIZE or IPV4_SIZE bytes long.
+ /// \param family Address family of testaddr.
+ ///
+ /// \return true if the address matches, false if it does not.
+ virtual bool compare(const uint8_t* testaddr, int family) const {
+
+ if (family != family_) {
+ // Can't match if the address is of the wrong family
+ return (false);
+ }
+
+ // Simple check failed, so have to do a complete match. To check that
+ // the address given matches the stored network address and mask, we
+ // check the simple condition that:
+ //
+ // address_given & mask_ == stored_address & mask_
+ //
+ // The result is checked for all bytes for which there are bits set in
+ // the mask. We stop at the first non-match (or when we run out of bits
+ // in the mask).
+ //
+ // Note that the mask represents a contiguous set of bits. As such, as
+ // soon as we find a mask byte of zeroes, we have run past the part of
+ // the address where we need to match.
+ //
+ // Note also that when checking an IPv4 address, the constructor has
+ // set all bytes in the mask beyond the first four bytes to zero.
+ // As the loop stops when it encounters a zero mask byte, if the
+ // ACL is for an IPV4 address, the loop will never check more than four
+ // bytes.
+
+ bool match = true;
+ for (int i = 0; match && (i < IPV6_SIZE) && (mask_[i] != 0); ++i) {
+ match = ((testaddr[i] & mask_[i]) == (address_[i] & mask_[i]));
+ }
+ return (match);
+ }
+
+private:
+ /// \brief Set Mask
+ ///
+ /// Sets up the mask from the prefix length. This involves setting
+ /// an individual mask in each byte of the mask array.
+ ///
+ /// The actual allowed value of the prefix length depends on the address
+ /// family.
+ ///
+ /// \param requested Requested prefix length size. If negative, the
+ /// maximum for the address family is assumed. (A negative value
+ /// will arise if the string constructor was used and no mask size
+ /// was given.)
+ void setMask(int requested) {
+
+ // Set the maximum number of bits allowed in the mask, and request
+ // that number of bits if no prefix length was given in the constructor.
+ const int maxmask = 8 * ((family_ == AF_INET) ? IPV4_SIZE : IPV6_SIZE);
+ if (requested < 0) {
+ requested = maxmask;
+ }
+
+ // Validate that the mask is valid.
+ if (requested <= maxmask) {
+
+ // Loop, setting the bits in the set of mask bytes until all the
+ // specified bits have been used up. As both IPv4 and IPv6
+ // addresses are stored in network-byte order, this works in
+ // both cases.
+ size_t bits_left = requested; // Bits remaining to set
+ int i = -1;
+ while (bits_left > 0) {
+ if (bits_left >= 8) {
+ mask_[++i] = ~0; // All bits set
+ bits_left -= 8;
+
+ } else if (bits_left > 0) {
+ mask_[++i] = internal::createMask(bits_left);
+ bits_left = 0;
+ }
+ }
+ } else {
+ isc_throw(isc::OutOfRange,
+ "mask size of " << requested << " is invalid " <<
+ "for the given address family");
+ }
+ }
+
+ // Member variables.
+ uint8_t address_[IPV6_SIZE]; ///< Address in binary form
+ uint8_t mask_[IPV6_SIZE]; ///< Address mask
+ int family_; ///< Address family
+};
+
+// Some compilers seem to need this to be explicitly defined outside the class
+template <typename Context>
+const size_t IPCheck<Context>::IPV6_SIZE;
+
+template <typename Context>
+const size_t IPCheck<Context>::IPV4_SIZE;
+
+} // namespace acl
+} // namespace isc
+
+#endif // __IP_CHECK_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/acl/loader.cc b/src/lib/acl/loader.cc
new file mode 100644
index 0000000..8ca7e28
--- /dev/null
+++ b/src/lib/acl/loader.cc
@@ -0,0 +1,46 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "loader.h"
+
+using namespace std;
+
+namespace isc {
+namespace acl {
+
+BasicAction defaultActionLoader(data::ConstElementPtr actionEl) {
+ try {
+ const string action(actionEl->stringValue());
+ if (action == "ACCEPT") {
+ return (ACCEPT);
+ } else if (action == "REJECT") {
+ return (REJECT);
+ } else if (action == "DROP") {
+ return (DROP);
+ } else {
+ throw LoaderError(__FILE__, __LINE__,
+ string("Unknown action '" + action + "'").
+ c_str(),
+ actionEl);
+ }
+ }
+ catch (const data::TypeError&) {
+ throw LoaderError(__FILE__, __LINE__,
+ "Invalid element type for action, must be string",
+ actionEl);
+ }
+}
+
+}
+}
diff --git a/src/lib/acl/loader.h b/src/lib/acl/loader.h
new file mode 100644
index 0000000..c86373e
--- /dev/null
+++ b/src/lib/acl/loader.h
@@ -0,0 +1,479 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef ACL_LOADER_H
+#define ACL_LOADER_H
+
+#include <exceptions/exceptions.h>
+#include <acl/acl.h>
+#include <cc/data.h>
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include <map>
+
+namespace isc {
+namespace acl {
+
+class AnyOfSpec;
+class AllOfSpec;
+template<typename Mode, typename Context> class LogicOperator;
+
+/**
+ * \brief Exception for bad ACL specifications.
+ *
+ * This will be thrown by the Loader if the ACL description is malformed
+ * in some way.
+ *
+ * It also can hold optional JSON element where was the error detected, so
+ * it can be examined.
+ *
+ * Checks may subclass this exception for similar errors if they see it fit.
+ */
+class LoaderError : public BadValue {
+private:
+ const data::ConstElementPtr element_;
+public:
+ /**
+ * \brief Constructor.
+ *
+ * Should be used with isc_throw if the fourth argument isn't used.
+ *
+ * \param file The file where the throw happened.
+ * \param line Similar as file, just for the line number.
+ * \param what Human readable description of what happened.
+ * \param element This might be passed to hold the JSON element where
+ * the error was detected.
+ */
+ LoaderError(const char* file, size_t line, const char* what,
+ data::ConstElementPtr element = data::ConstElementPtr()) :
+ BadValue(file, line, what),
+ element_(element)
+ {}
+
+ ~ LoaderError() throw() {}
+
+ /**
+ * \brief Get the element.
+ *
+ * This returns the element where the error was detected. Note that it
+ * might be NULL in some situations.
+ */
+ const data::ConstElementPtr& element() const {
+ return (element_);
+ }
+};
+
+/**
+ * \brief Loader of the default actions of ACLs.
+ *
+ * Declared outside the Loader class, as this one does not need to be
+ * templated. This will throw LoaderError if the parameter isn't string
+ * or if it doesn't contain one of the accepted values.
+ *
+ * \param action The JSON representation of the action. It must be a string
+ * and contain one of "ACCEPT", "REJECT" or "DROP.
+ * \note We could define different names or add aliases if needed.
+ */
+BasicAction defaultActionLoader(data::ConstElementPtr action);
+
+/**
+ * \brief Loader of ACLs.
+ *
+ * The goal of this class is to convert JSON description of an ACL to object
+ * of the ACL class (including the checks inside it).
+ *
+ * The class can be used to load the checks only. This is supposed to be used
+ * by compound checks to create the subexpressions.
+ *
+ * To allow any kind of checks to exist in the application, creators are
+ * registered for the names of the checks.
+ *
+ * An ACL definition looks like this:
+ * \verbatim
+ * [
+ * {
+ * "action": "ACCEPT",
+ * "match-type": <parameter>
+ * },
+ * {
+ * "action": "REJECT",
+ * "match-type": <parameter>
+ * "another-match-type": [<parameter1>, <parameter2>]
+* },
+* {
+* "action": "DROP"
+* }
+ * ]
+ * \endverbatim
+ *
+ * This is a list of elements. Each element must have an "action"
+ * entry/keyword. That one specifies which action is returned if this
+ * element matches (the value of the key is passed to the action loader
+ * (see the constructor). It may be any piece of JSON which the action
+ * loader expects.
+ *
+ * The rest of the element are matches. The left side is the name of the
+ * match type (for example match for source IP address or match for message
+ * size). The <parameter> is whatever is needed to describe the match and
+ * depends on the match type, the loader passes it verbatim to creator
+ * of that match type.
+ *
+ * There may be multiple match types in single element. In such case, all
+ * of the matches must match for the element to take action (so, in the second
+ * element, both "match-type" and "another-match-type" must be satisfied).
+ * If there's no match in the element, the action is taken/returned without
+ * conditions, every time (makes sense as the last entry, as the ACL will
+ * never get past it).
+ *
+ * The second entry shows another thing - if there's a list as the value
+ * for some match and the match itself is not expecting a list, it is taken
+ * as an "or" - a match for at last one of the choices in the list must match.
+ * So, for the second entry, both "match-type" and "another-match-type" must
+ * be satisfied, but the another one is satisfied by either parameter1 or
+ * parameter2.
+ */
+template<typename Context, typename Action = BasicAction> class Loader {
+public:
+ /**
+ * \brief Constructor.
+ *
+ * \param default_action The default action for created ACLs.
+ * \param actionLoader is the loader which will be used to convert actions
+ * from their JSON representation. The default value is suitable for
+ * the BasicAction enum. If you did not specify the second
+ * template argument, you don't need to specify this loader.
+ */
+ Loader(const Action& defaultAction,
+ const boost::function1<Action, data::ConstElementPtr>
+ &actionLoader = &defaultActionLoader) :
+ default_action_(defaultAction),
+ action_loader_(actionLoader)
+ {}
+
+ /**
+ * \brief Creator of the checks.
+ *
+ * This can be registered within the Loader and will be used to create the
+ * checks. It is expected multiple creators (for multiple types, one can
+ * handle even multiple names) will be created and registered to support
+ * range of things we could check. This allows for customizing/extending
+ * the loader.
+ */
+ class CheckCreator {
+ public:
+ /** \brief Virtual class needs virtual destructor */
+ virtual ~CheckCreator() {}
+
+ /**
+ * \brief List of names supported by this loader.
+ *
+ * List of all names for which this loader is able to create the
+ * checks. There can be multiple names, to support both aliases
+ * to the same checks and creators capable of creating multiple
+ * types of checks.
+ */
+ virtual std::vector<std::string> names() const = 0;
+
+ /**
+ * \brief Creates the check.
+ *
+ * This function does the actual creation. It is passed all the
+ * relevant data and is supposed to return shared pointer to the
+ * check.
+ *
+ * It is expected to throw the LoaderError exception when the
+ * definition is invalid.
+ *
+ * \param name The type name of the check. If the creator creates
+ * only one type of check, it can safely ignore this parameter.
+ * \param definition The part of JSON describing the parameters of
+ * check. As there's no way for the loader to know how the
+ * parameters might look like, they are not checked in any way.
+ * Therefore it's up to the creator (or the check being created)
+ * to validate the data and throw if it is bad.
+ * \param Current loader calling this creator. This can be used
+ * to load subexpressions in case of compound check.
+ */
+ virtual boost::shared_ptr<Check<Context> > create(
+ const std::string& name, data::ConstElementPtr definition,
+ const Loader<Context, Action>& loader) = 0;
+
+ /**
+ * \brief Is list or-abbreviation allowed?
+ *
+ * If this returns true and the parameter (eg. the value we check
+ * against, the one that is passed as the second parameter of create)
+ * is list, the loader will call the create method with each element of
+ * the list and aggregate all the results in OR compound check. If it
+ * is false, the parameter is passed verbatim no matter if it is or
+ * isn't a list. For example, IP check will have this as true (so
+ * multiple IP addresses can be passed as options), but AND operator
+ * will return false and handle the list of subexpressions itself.
+ *
+ * The rationale behind this is that it is common to specify list of
+ * something that matches (eg. list of IP addresses).
+ */
+ virtual bool allowListAbbreviation() const {
+ return (true);
+ }
+ };
+
+ /**
+ * \brief Register another check creator.
+ *
+ * Adds a creator to the list of known ones. The creator's list of names
+ * must be disjoint with the names already known to the creator or the
+ * LoaderError exception is thrown. In such case, the creator is not
+ * registered under any of the names. In case of other exceptions, like
+ * bad_alloc, only weak exception safety is guaranteed.
+ *
+ * \param creator Shared pointer to the creator.
+ * \note We don't support deregistration yet, but it is expected it will
+ * be needed in future, when we have some kind of plugins. These
+ * plugins might want to unload, in which case they would need to
+ * deregister their creators. It is expected they would pass the same
+ * pointer to such method as they pass here.
+ */
+ void registerCreator(boost::shared_ptr<CheckCreator> creator) {
+ // First check we can insert all the names
+ typedef std::vector<std::string> Strings;
+ const Strings names(creator->names());
+ for (Strings::const_iterator i(names.begin()); i != names.end();
+ ++i) {
+ if (creators_.find(*i) != creators_.end()) {
+ isc_throw(LoaderError, "The loader already contains creator "
+ "named " << *i);
+ }
+ }
+ // Now insert them
+ for (Strings::const_iterator i(names.begin()); i != names.end();
+ ++i) {
+ creators_[*i] = creator;
+ }
+ }
+
+ /**
+ * \brief Load a check.
+ *
+ * This parses a check dict (block, the one element of ACL) and calls a
+ * creator (or creators, if more than one check is found inside) for it. It
+ * ignores the "action" key, as it is a reserved keyword used to specify
+ * actions inside the ACL.
+ *
+ * This may throw LoaderError if it is not a dict or if some of the type
+ * names is not known (there's no creator registered for it). The
+ * exceptions from creators aren't caught.
+ *
+ * \param description The JSON description of the check.
+ */
+ boost::shared_ptr<Check<Context> > loadCheck(const data::ConstElementPtr&
+ description) const
+ {
+ // Get the description as a map
+ typedef std::map<std::string, data::ConstElementPtr> Map;
+ Map map;
+ try {
+ map = description->mapValue();
+ }
+ catch (const data::TypeError&) {
+ isc_throw_1(LoaderError, "Check description is not a map",
+ description);
+ }
+ // Call the internal part with extracted map
+ return (loadCheck(description, map));
+ }
+
+ /**
+ * \brief Load an ACL.
+ *
+ * This parses an ACL list, creates the checks and actions of each element
+ * and returns it.
+ *
+ * No exceptions from \c loadCheck (therefore from whatever creator is
+ * used) and from the actionLoader passed to constructor are caught.
+ *
+ * \exception InvalidParameter The given element is NULL (most likely a
+ * caller's bug)
+ * \exception LoaderError The given element isn't a list or the
+ * "action" key is missing in some element
+ *
+ * \param description The JSON list of ACL.
+ *
+ * \return The newly created ACL object
+ */
+ boost::shared_ptr<ACL<Context, Action> > load(const data::ConstElementPtr&
+ description) const
+ {
+ if (!description) {
+ isc_throw(isc::InvalidParameter,
+ "Null description is passed to ACL loader");
+ }
+
+ // We first check it's a list, so we can use the list reference
+ // (the list may be huge)
+ if (description->getType() != data::Element::list) {
+ isc_throw_1(LoaderError, "ACL not a list", description);
+ }
+ // First create an empty ACL
+ const List &list(description->listValue());
+ boost::shared_ptr<ACL<Context, Action> > result(
+ new ACL<Context, Action>(default_action_));
+ // Run trough the list of elements
+ for (List::const_iterator i(list.begin()); i != list.end(); ++i) {
+ Map map;
+ try {
+ map = (*i)->mapValue();
+ }
+ catch (const data::TypeError&) {
+ isc_throw_1(LoaderError, "ACL element not a map", *i);
+ }
+ // Create an action for the element
+ const Map::const_iterator action(map.find("action"));
+ if (action == map.end()) {
+ isc_throw_1(LoaderError, "No action in ACL element", *i);
+ }
+ const Action acValue(action_loader_(action->second));
+ // Now create the check if there's one
+ if (map.size() >= 2) { // One is the action, another one the check
+ result->append(loadCheck(*i, map), acValue);
+ } else {
+ // In case there's no check, this matches every time. We
+ // simulate it by our own private "True" check.
+ result->append(boost::shared_ptr<Check<Context> >(new True()),
+ acValue);
+ }
+ }
+ return (result);
+ }
+
+private:
+ // Some type aliases to save typing
+ typedef std::map<std::string, boost::shared_ptr<CheckCreator> > Creators;
+ typedef std::map<std::string, data::ConstElementPtr> Map;
+ typedef std::vector<data::ConstElementPtr> List;
+ // Private members
+ Creators creators_;
+ const Action default_action_;
+ const boost::function1<Action, data::ConstElementPtr> action_loader_;
+
+ /**
+ * \brief Internal version of loadCheck.
+ *
+ * This is the internal part, shared between load and loadCheck.
+ * \param description The bit of JSON (used in exceptions).
+ * \param map The extracted map describing the check. It does change
+ * the map.
+ */
+ boost::shared_ptr<Check<Context> > loadCheck(const data::ConstElementPtr&
+ description, Map& map) const
+ {
+ // Remove the action keyword
+ map.erase("action");
+ // Now, do we have any definition? Or is it and abbreviation?
+ switch (map.size()) {
+ case 0:
+ isc_throw_1(LoaderError, "Check description is empty",
+ description);
+ case 1: {
+ // Get the first and only item
+ const Map::const_iterator checkDesc(map.begin());
+ const std::string& name(checkDesc->first);
+ const typename Creators::const_iterator
+ creatorIt(creators_.find(name));
+ if (creatorIt == creators_.end()) {
+ isc_throw_1(LoaderError, "No creator for ACL check " <<
+ name, description);
+ }
+ if (creatorIt->second->allowListAbbreviation() &&
+ checkDesc->second->getType() == data::Element::list) {
+ // Or-abbreviated form - create an OR and put everything
+ // inside.
+ const std::vector<data::ConstElementPtr>&
+ params(checkDesc->second->listValue());
+ boost::shared_ptr<LogicOperator<AnyOfSpec, Context> >
+ oper(new LogicOperator<AnyOfSpec, Context>);
+ for (std::vector<data::ConstElementPtr>::const_iterator
+ i(params.begin());
+ i != params.end(); ++i) {
+ oper->addSubexpression(
+ creatorIt->second->create(name, *i, *this));
+ }
+ return (oper);
+ }
+ // Create the check and return it
+ return (creatorIt->second->create(name, checkDesc->second,
+ *this));
+ }
+ default: {
+ // This is the AND-abbreviated form. We need to create an
+ // AND (or "ALL") operator, loop trough the whole map and
+ // fill it in. We do a small trick - we create bunch of
+ // single-item maps, call this loader recursively (therefore
+ // it will get into the "case 1" branch, where there is
+ // the actual loading) and use the results to fill the map.
+ //
+ // We keep the description the same, there's nothing we could
+ // take out (we could create a new one, but that would be
+ // confusing, as it is used for error messages only).
+ boost::shared_ptr<LogicOperator<AllOfSpec, Context> >
+ oper(new LogicOperator<AllOfSpec, Context>);
+ for (Map::const_iterator i(map.begin()); i != map.end(); ++i) {
+ Map singleSubexpr;
+ singleSubexpr.insert(*i);
+ oper->addSubexpression(loadCheck(description,
+ singleSubexpr));
+ }
+ return (oper);
+ }
+ }
+ }
+
+ /**
+ * \brief Check that always matches.
+ *
+ * This one is used internally for ACL elements without condition. We may
+ * want to make this publicly accesible sometime maybe, but for now,
+ * there's no need.
+ */
+ class True : public Check<Context> {
+ public:
+ virtual bool matches(const Context&) const { return (true); };
+ virtual unsigned cost() const { return (1); }
+ // We don't write "true" here, as this one was created using empty
+ // input
+ virtual std::string toText() const { return ""; }
+ };
+};
+
+}
+}
+
+/*
+ * This include at the end of the file is unusual. But we need to include it,
+ * we use template classes from there. However, they need to be present only
+ * at instantiation of our class, which will happen below this header.
+ *
+ * The problem is, the header uses us as well, therefore there's a circular
+ * dependency. If we loaded it at the beginning and someone loaded us first,
+ * the logic_check header wouldn't have our definitions. This way, no matter
+ * in which order they are loaded, the definitions from this header will be
+ * above the ones from logic_check.
+ */
+#include "logic_check.h"
+
+#endif
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/acl/logic_check.h b/src/lib/acl/logic_check.h
new file mode 100644
index 0000000..92441e8
--- /dev/null
+++ b/src/lib/acl/logic_check.h
@@ -0,0 +1,286 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef ACL_LOGIC_CHECK_H
+#define ACL_LOGIC_CHECK_H
+
+#include "check.h"
+#include "loader.h"
+
+namespace isc {
+namespace acl {
+
+/// \brief Constants for the AnyOf implementation
+class AnyOfSpec {
+public:
+ static bool start() { return (false); }
+ static bool terminate(const bool another) {
+ return (another);
+ }
+};
+
+/// \brief Constants for the AllOf implementation
+class AllOfSpec {
+public:
+ static bool start() { return (true); }
+ static bool terminate(const bool another) {
+ return (!another);
+ }
+};
+
+/**
+ * \brief Logic operators
+ *
+ * This class implements the AllOf and AnyOf compound checks. As their
+ * behaviour is almost the same, the same template class is used. Which
+ * one it is depends on the Mode template parameter. The Mode should be
+ * one of AnyOfSpec or AllOfSpec, which provide some commands for the
+ * internal implementation. It would be nice to provide typedefs for
+ * them, but it is impossible to do so, as we have the Context template
+ * parameter as well and C++ doesn't like templated typedefs.
+ *
+ * The object holds several subexpressions and returns true if all
+ * of the subexpressions return true (in case of AllOfSpec Mode) or
+ * at last one of them return true (in case of AnyOfSpec Mode). If
+ * some subexpression guarantees the result (eg. some returns false
+ * in case of AllOfSpec), the rest is not tried for performance
+ * reasons.
+ */
+template<typename Mode, typename Context>
+class LogicOperator : public CompoundCheck<Context> {
+public:
+ /**
+ * \brief Add another subexpression.
+ *
+ * This adds another subexpression to the list of checked expressions.
+ * This is usually done shortly after the creation, before using the
+ * check for matches.
+ *
+ * Currently there's no way to place the expression into arbitrary place
+ * or to remove it. It might turn out it would be needed in future to
+ * optimise or it might even turn out we need shared pointers for it.
+ *
+ * \param expr The new expression to put inside.
+ */
+ void addSubexpression(const boost::shared_ptr<Check<Context> >& expr) {
+ checks_.push_back(expr);
+ }
+ /**
+ * \brief The current list of subexpressions.
+ */
+ virtual typename CompoundCheck<Context>::Checks getSubexpressions() const {
+ typename CompoundCheck<Context>::Checks result;
+ for (typename Checks::const_iterator i(checks_.begin());
+ i != checks_.end(); ++i) {
+ result.push_back(i->get());
+ }
+ return (result);
+ }
+ /**
+ * \brief The match of the check.
+ *
+ * Runs the subexpressions, one by one, and then decides based on that
+ * what to return.
+ */
+ virtual bool matches(const Context& context) const {
+ /*
+ * This might look slightly complicated. However, this is just
+ * generalized version of multi-and or multi-or. The usual
+ * implementation of multi-and starts with true and if one with
+ * false is found, it turns to be false forever and false is
+ * returned. It is exactly the other way around with or.
+ *
+ * So, if we ever find one that makes it the other one than start
+ * (false in case of and, true in case of or), we can just stop and
+ * return that one right away. If it meets no such expression, we
+ * get to the end and return the default.
+ */
+ for (typename Checks::const_iterator i(checks_.begin());
+ i != checks_.end(); ++i) {
+ if (Mode::terminate((*i)->matches(context))) {
+ return (!Mode::start());
+ }
+ }
+ return (Mode::start());
+ }
+private:
+ /// \brief List of subexpressions
+ typedef typename std::vector<boost::shared_ptr<Check<Context> > > Checks;
+ Checks checks_;
+};
+
+/**
+ * \brief Creator for the LogicOperator compound check.
+ *
+ * This class can load the ANY and ALL operators from JSON. They expect
+ * a list of subexpressions as a parameter, eg. like this:
+ *
+ * \verbatim
+ * {"ANY": [
+ * {"ip": "1.2.3.4"},
+ * {"ip": "5.6.7.8"}
+ * ]}
+ * \endverbatim
+ *
+ * It uses the loader to load the subexpressions, therefore whatever is
+ * supported there is supported here as well.
+ *
+ * The Mode template parameter has the same meaning as with LogicOperator,
+ * it is used to know which operators to create.
+ */
+template<typename Mode, typename Context, typename Action = BasicAction>
+class LogicCreator : public Loader<Context, Action>::CheckCreator {
+public:
+ /**
+ * \brief Constructor.
+ *
+ * \param name The name for which the loader will work. In practice,
+ * it will usually be ANY or ALL (depending on the mode), but
+ * anything else can be used as well.
+ */
+ LogicCreator(const std::string& name) :
+ name_(name)
+ {}
+ /// \brief Returns vector containing the name.
+ virtual std::vector<std::string> names() const {
+ std::vector<std::string> result;
+ result.push_back(name_);
+ return (result);
+ }
+ /**
+ * \brief Converts a JSON description into the logic operator.
+ *
+ * This is the place where the actual loading happens. It creates
+ * the logic operator and calls the loader on each of the list
+ * elements, placing the result into the logic operator.
+ *
+ * The first parameter is ignored and is there only to match interface.
+ *
+ * \param definition The JSON definition of the subexpressions. This must
+ * be a list (if it isn't, the LoaderError is thrown) and the elements
+ * must be loadable by the loader (the exceptions from it are not
+ * caught).
+ * \param loader The loader to use for loading of subexpressions.
+ */
+ virtual boost::shared_ptr<Check<Context> > create(const std::string&,
+ data::ConstElementPtr
+ definition,
+ const Loader<Context,
+ Action>& loader)
+ {
+ std::vector<data::ConstElementPtr> subexprs;
+ try {
+ subexprs = definition->listValue();
+ }
+ catch (const data::TypeError&) {
+ isc_throw_1(LoaderError, "Logic operator takes list", definition);
+ }
+ boost::shared_ptr<LogicOperator<Mode, Context> >
+ result(new LogicOperator<Mode, Context>);
+ for (std::vector<data::ConstElementPtr>::const_iterator
+ i(subexprs.begin());
+ i != subexprs.end(); ++i) {
+ result->addSubexpression(loader.loadCheck(*i));
+ }
+ return (result);
+ }
+ virtual bool allowListAbbreviation() const { return (false); }
+private:
+ const std::string name_;
+};
+
+/**
+ * \brief The NOT operator for ACLs.
+ *
+ * This simply returns the negation of whatever returns the subexpression.
+ */
+template<typename Context>
+class NotOperator : public CompoundCheck<Context> {
+public:
+ /**
+ * \brief Constructor
+ *
+ * \param expr The subexpression to be negated by this NOT.
+ */
+ NotOperator(const boost::shared_ptr<Check<Context> >& expr) :
+ expr_(expr)
+ { }
+ /**
+ * \brief The list of subexpressions
+ *
+ * \return The vector will contain single value and it is the expression
+ * passed by constructor.
+ */
+ virtual typename CompoundCheck<Context>::Checks getSubexpressions() const {
+ typename CompoundCheck<Context>::Checks result;
+ result.push_back(expr_.get());
+ return (result);
+ }
+ /// \brief The matching function
+ virtual bool matches(const Context& context) const {
+ return (!expr_->matches(context));
+ }
+private:
+ /// \brief The subexpression
+ const boost::shared_ptr<Check<Context> > expr_;
+};
+
+template<typename Context, typename Action = BasicAction>
+class NotCreator : public Loader<Context, Action>::CheckCreator {
+public:
+ /**
+ * \brief Constructor
+ *
+ * \param name The name of the NOT operator to be loaded as.
+ */
+ NotCreator(const std::string& name) :
+ name_(name)
+ { }
+ /**
+ * \brief List of the names this loads
+ *
+ * \return Single-value vector containing the name passed to the
+ * constructor.
+ */
+ virtual std::vector<std::string> names() const {
+ std::vector<std::string> result;
+ result.push_back(name_);
+ return (result);
+ }
+ /// \brief Create the check.
+ virtual boost::shared_ptr<Check<Context> > create(const std::string&,
+ data::ConstElementPtr
+ definition,
+ const Loader<Context,
+ Action>& loader)
+ {
+ return (boost::shared_ptr<Check<Context> >(new NotOperator<Context>(
+ loader.loadCheck(definition))));
+ }
+ /**
+ * \brief Or-abbreviated form.
+ *
+ * This returns false. In theory, the NOT operator could be used with
+ * the abbreviated form, but it would be confusing. Such syntax is
+ * therefore explicitly forbidden.
+ */
+ virtual bool allowListAbbreviation() const { return (false); }
+public:
+ const std::string name_;
+};
+
+}
+}
+
+#endif
diff --git a/src/lib/acl/tests/Makefile.am b/src/lib/acl/tests/Makefile.am
new file mode 100644
index 0000000..ce1aec5
--- /dev/null
+++ b/src/lib/acl/tests/Makefile.am
@@ -0,0 +1,38 @@
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += acl_test.cc
+run_unittests_SOURCES += check_test.cc
+run_unittests_SOURCES += dns_test.cc
+run_unittests_SOURCES += ip_check_unittest.cc
+run_unittests_SOURCES += loader_test.cc
+run_unittests_SOURCES += logcheck.h
+run_unittests_SOURCES += creators.h
+run_unittests_SOURCES += logic_check_test.cc
+run_unittests_SOURCES += sockaddr.h
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/acl/libacl.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/acl/libdnsacl.la
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/acl/tests/acl_test.cc b/src/lib/acl/tests/acl_test.cc
new file mode 100644
index 0000000..15ffef6
--- /dev/null
+++ b/src/lib/acl/tests/acl_test.cc
@@ -0,0 +1,90 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <boost/shared_ptr.hpp>
+
+#include "logcheck.h"
+
+using namespace isc::acl;
+using namespace isc::acl::tests;
+using boost::shared_ptr;
+
+namespace {
+
+// Test version of the Acl class. It adds few methods to examine the protected
+// data, but does not change the implementation.
+class TestACL : public ACL<Log> {
+public:
+ TestACL() :
+ ACL<Log>(DROP)
+ {}
+ // Check the stored default action there
+ void checkDefaultAction(BasicAction ac) {
+ EXPECT_EQ(getDefaultAction(), ac);
+ }
+};
+
+// The test fixture. Contains some members so they don't need to be manually
+// created each time and some convenience functions.
+class ACLTest : public ::testing::Test {
+public:
+ ACLTest() :
+ next_check_(0)
+ {}
+ TestACL acl_;
+ Log log_;
+ size_t next_check_;
+ boost::shared_ptr<Check<Log> > getCheck(bool accepts) {
+ return (shared_ptr<Check<Log> >(new ConstCheck(accepts,
+ next_check_++)));
+ }
+};
+
+/*
+ * This tests the default action and that nothing is run if nothing is
+ * inserted (it's hard to imagine otherwise though).
+ *
+ * We use the default ACL unchanged from the test class.
+ */
+TEST_F(ACLTest, emptyRule) {
+ acl_.checkDefaultAction(DROP);
+ EXPECT_EQ(DROP, acl_.execute(log_));
+ // No test was run
+ log_.checkFirst(0);
+}
+
+/*
+ * This tests the default action in case no check matches.
+ */
+TEST_F(ACLTest, noMatch) {
+ acl_.append(getCheck(false), ACCEPT);
+ acl_.append(getCheck(false), REJECT);
+ EXPECT_EQ(DROP, acl_.execute(log_));
+ // The first two checks were actually run (and didn't match)
+ log_.checkFirst(2);
+}
+
+/*
+ * Checks that it takes the first matching check and returns the
+ * value. Also checks that the others aren't run at all.
+ */
+TEST_F(ACLTest, firstMatch) {
+ acl_.append(getCheck(false), ACCEPT);
+ acl_.append(getCheck(true), REJECT);
+ acl_.append(getCheck(true), ACCEPT);
+ EXPECT_EQ(REJECT, acl_.execute(log_));
+ log_.checkFirst(2);
+}
+
+}
diff --git a/src/lib/acl/tests/check_test.cc b/src/lib/acl/tests/check_test.cc
new file mode 100644
index 0000000..e83e8f2
--- /dev/null
+++ b/src/lib/acl/tests/check_test.cc
@@ -0,0 +1,70 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+#include <acl/check.h>
+
+using namespace isc::acl;
+
+namespace {
+
+// This test has two function. For one, it checks the default implementations
+// do what they should and it makes sure the template actually compiles
+// (as templates are syntax-checked upon instantiation).
+
+// This is a test check that just passes the boolean it gets.
+class Pass : public Check<bool> {
+public:
+ virtual bool matches(const bool& value) const { return (value); }
+};
+
+// This is a simple test compound check. It contains two Pass checks
+// and passes result of the first one.
+
+class First : public CompoundCheck<bool> {
+public:
+ // The internal checks are public, so we can check the addresses
+ Pass first, second;
+ virtual Checks getSubexpressions() const {
+ Checks result;
+ result.push_back(&first);
+ result.push_back(&second);
+ return (result);
+ }
+ virtual bool matches(const bool& value) const {
+ return (first.matches(value));
+ }
+};
+
+TEST(Check, defaultCheckValues) {
+ Pass p;
+ EXPECT_EQ(Check<bool>::UNKNOWN_COST, p.cost());
+ EXPECT_TRUE(p.matches(true));
+ EXPECT_FALSE(p.matches(false));
+ // The exact text is compiler dependant, but we check it returns something
+ // and can be compiled
+ EXPECT_FALSE(p.toText().empty());
+}
+
+TEST(Check, defaultCompoundValues) {
+ First f;
+ EXPECT_EQ(2 * Check<bool>::UNKNOWN_COST, f.cost());
+ EXPECT_TRUE(f.pure());
+ First::Checks c(f.getSubexpressions());
+ ASSERT_EQ(2, c.size());
+ EXPECT_EQ(&f.first, c[0]);
+ EXPECT_EQ(&f.second, c[1]);
+}
+
+}
diff --git a/src/lib/acl/tests/creators.h b/src/lib/acl/tests/creators.h
new file mode 100644
index 0000000..584df71
--- /dev/null
+++ b/src/lib/acl/tests/creators.h
@@ -0,0 +1,158 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// This is not a public header, but some code shared between tests
+// This one contains various creators to test the loader and other creators
+
+#ifndef CREATORS_H
+#define CREATORS_H
+
+#include "logcheck.h"
+
+#include <cc/data.h>
+#include <acl/loader.h>
+#include <string>
+
+namespace isc {
+namespace acl {
+namespace tests {
+
+// A check that doesn't check anything but remembers it's own name
+// and data
+class NamedCheck : public Check<Log> {
+public:
+ NamedCheck(const std::string& name, isc::data::ConstElementPtr data) :
+ name_(name),
+ data_(data)
+ {}
+ virtual bool matches(const Log&) const { return (true); }
+ const std::string name_;
+ const isc::data::ConstElementPtr data_;
+};
+
+// The creator of NamedCheck
+class NamedCreator : public Loader<Log>::CheckCreator {
+public:
+ NamedCreator(const std::string& name, bool abbreviatedList = true) :
+ abbreviated_list_(abbreviatedList)
+ {
+ names_.push_back(name);
+ }
+ NamedCreator(const std::vector<std::string>& names) :
+ names_(names),
+ abbreviated_list_(true)
+ {}
+ std::vector<std::string> names() const {
+ return (names_);
+ }
+ boost::shared_ptr<Check<Log> > create(const std::string& name,
+ isc::data::ConstElementPtr data,
+ const Loader<Log>&)
+ {
+ bool found(false);
+ for (std::vector<std::string>::const_iterator i(names_.begin());
+ i != names_.end(); ++i) {
+ if (*i == name) {
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found) << "Name " << name << " passed to creator which "
+ "doesn't handle it.";
+ return (boost::shared_ptr<Check<Log> >(new NamedCheck(name, data)));
+ }
+ bool allowListAbbreviation() const {
+ return (abbreviated_list_);
+ }
+private:
+ std::vector<std::string> names_;
+ const bool abbreviated_list_;
+};
+
+// To be thrown in tests internally
+class TestCreatorError {};
+
+// This will throw every time it should create something
+class ThrowCreator : public Loader<Log>::CheckCreator {
+public:
+ std::vector<std::string> names() const {
+ std::vector<std::string> result;
+ result.push_back("throw");
+ return (result);
+ }
+ boost::shared_ptr<Check<Log> > create(const std::string&,
+ isc::data::ConstElementPtr,
+ const Loader<Log>&)
+ {
+ throw TestCreatorError();
+ }
+};
+
+// This throws whenever the match is called on it
+class ThrowCheck : public Check<Log> {
+public:
+ virtual bool matches(const Log&) const {
+ throw TestCreatorError();
+ }
+};
+
+// And creator for it
+class ThrowCheckCreator : public Loader<Log>::CheckCreator {
+public:
+ std::vector<std::string> names() const {
+ std::vector<std::string> result;
+ result.push_back("throwcheck");
+ return (result);
+ }
+ boost::shared_ptr<Check<Log> > create(const std::string&,
+ isc::data::ConstElementPtr,
+ const Loader<Log>&)
+ {
+ return (boost::shared_ptr<Check<Log> >(new ThrowCheck()));
+ }
+};
+
+class LogCreator : public Loader<Log>::CheckCreator {
+public:
+ std::vector<std::string> names() const {
+ std::vector<std::string> result;
+ result.push_back("logcheck");
+ return (result);
+ }
+ /*
+ * For simplicity, we just take two values as a list, first is the
+ * logging cell used, the second is result of the check. No error checking
+ * is done, if there's bug in the test, it will throw TypeError for us.
+ */
+ boost::shared_ptr<Check<Log> > create(const std::string&,
+ isc::data::ConstElementPtr definition,
+ const Loader<Log>&)
+ {
+ std::vector<isc::data::ConstElementPtr> list(definition->listValue());
+ int logpos(list[0]->intValue());
+ bool accept(list[1]->boolValue());
+ return (boost::shared_ptr<ConstCheck>(new ConstCheck(accept, logpos)));
+ }
+ // We take a list, so don't interpret it for us
+ virtual bool allowListAbbreviation() const { return (false); }
+};
+
+}
+}
+}
+#endif
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/acl/tests/dns_test.cc b/src/lib/acl/tests/dns_test.cc
new file mode 100644
index 0000000..3a42af0
--- /dev/null
+++ b/src/lib/acl/tests/dns_test.cc
@@ -0,0 +1,205 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <vector>
+#include <string>
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <cc/data.h>
+#include <acl/dns.h>
+#include <acl/loader.h>
+#include <acl/check.h>
+#include <acl/ip_check.h>
+
+#include "sockaddr.h"
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using boost::scoped_ptr;
+using namespace isc::data;
+using namespace isc::acl;
+using namespace isc::acl::dns;
+using isc::acl::LoaderError;
+
+namespace {
+
+TEST(DNSACL, getRequestLoader) {
+ dns::RequestLoader* l(&getRequestLoader());
+ ASSERT_TRUE(l != NULL);
+ EXPECT_EQ(l, &getRequestLoader());
+ EXPECT_NO_THROW(l->load(Element::fromJSON("[{\"action\": \"DROP\"}]")));
+
+ // Confirm it can load the ACl syntax acceptable to a default creator.
+ // Tests to see whether the loaded rules work correctly will be in
+ // other dedicated tests below.
+ EXPECT_NO_THROW(l->load(Element::fromJSON("[{\"action\": \"DROP\","
+ " \"from\": \"192.0.2.1\"}]")));
+}
+
+class RequestCheckCreatorTest : public ::testing::Test {
+protected:
+ dns::internal::RequestCheckCreator creator_;
+
+ typedef boost::shared_ptr<const dns::RequestCheck> ConstRequestCheckPtr;
+ ConstRequestCheckPtr check_;
+};
+
+TEST_F(RequestCheckCreatorTest, names) {
+ ASSERT_EQ(1, creator_.names().size());
+ EXPECT_EQ("from", creator_.names()[0]);
+}
+
+TEST_F(RequestCheckCreatorTest, allowListAbbreviation) {
+ EXPECT_FALSE(creator_.allowListAbbreviation());
+}
+
+// The following two tests check the creator for the form of
+// 'from: "IP prefix"'. We don't test many variants of prefixes, which
+// are done in the tests for IPCheck.
+TEST_F(RequestCheckCreatorTest, createIPv4Check) {
+ check_ = creator_.create("from", Element::fromJSON("\"192.0.2.1\""),
+ getRequestLoader());
+ const dns::internal::RequestIPCheck& ipcheck_ =
+ dynamic_cast<const dns::internal::RequestIPCheck&>(*check_);
+ EXPECT_EQ(AF_INET, ipcheck_.getFamily());
+ EXPECT_EQ(32, ipcheck_.getPrefixlen());
+ const vector<uint8_t> check_address(ipcheck_.getAddress());
+ ASSERT_EQ(4, check_address.size());
+ const uint8_t expected_address[] = { 192, 0, 2, 1 };
+ EXPECT_TRUE(equal(check_address.begin(), check_address.end(),
+ expected_address));
+}
+
+TEST_F(RequestCheckCreatorTest, createIPv6Check) {
+ check_ = creator_.create("from",
+ Element::fromJSON("\"2001:db8::5300/120\""),
+ getRequestLoader());
+ const dns::internal::RequestIPCheck& ipcheck_ =
+ dynamic_cast<const dns::internal::RequestIPCheck&>(*check_);
+ EXPECT_EQ(AF_INET6, ipcheck_.getFamily());
+ EXPECT_EQ(120, ipcheck_.getPrefixlen());
+ const vector<uint8_t> check_address(ipcheck_.getAddress());
+ ASSERT_EQ(16, check_address.size());
+ const uint8_t expected_address[] = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x53, 0x00 };
+ EXPECT_TRUE(equal(check_address.begin(), check_address.end(),
+ expected_address));
+}
+
+TEST_F(RequestCheckCreatorTest, badCreate) {
+ // Invalid name
+ EXPECT_THROW(creator_.create("bad", Element::fromJSON("\"192.0.2.1\""),
+ getRequestLoader()), LoaderError);
+
+ // Invalid type of parameter
+ EXPECT_THROW(creator_.create("from", Element::fromJSON("4"),
+ getRequestLoader()),
+ isc::data::TypeError);
+ EXPECT_THROW(creator_.create("from", Element::fromJSON("[]"),
+ getRequestLoader()),
+ isc::data::TypeError);
+
+ // Syntax error for IPCheck
+ EXPECT_THROW(creator_.create("from", Element::fromJSON("\"bad\""),
+ getRequestLoader()),
+ isc::InvalidParameter);
+
+ // NULL pointer
+ EXPECT_THROW(creator_.create("from", ConstElementPtr(), getRequestLoader()),
+ LoaderError);
+}
+
+class RequestCheckTest : public ::testing::Test {
+protected:
+ typedef boost::shared_ptr<const dns::RequestCheck> ConstRequestCheckPtr;
+
+ // A helper shortcut to create a single IP check for the given prefix.
+ ConstRequestCheckPtr createIPCheck(const string& prefix) {
+ return (creator_.create("from", Element::fromJSON(
+ string("\"") + prefix + string("\"")),
+ getRequestLoader()));
+ }
+
+ // create a one time request context for a specific test. Note that
+ // getSockaddr() uses a static storage, so it cannot be called more than
+ // once in a single test.
+ const dns::RequestContext& getRequest4() {
+ ipaddr.reset(new IPAddress(tests::getSockAddr("192.0.2.1")));
+ request.reset(new dns::RequestContext(*ipaddr));
+ return (*request);
+ }
+ const dns::RequestContext& getRequest6() {
+ ipaddr.reset(new IPAddress(tests::getSockAddr("2001:db8::1")));
+ request.reset(new dns::RequestContext(*ipaddr));
+ return (*request);
+ }
+
+private:
+ scoped_ptr<IPAddress> ipaddr;
+ scoped_ptr<dns::RequestContext> request;
+ dns::internal::RequestCheckCreator creator_;
+};
+
+TEST_F(RequestCheckTest, checkIPv4) {
+ // Exact match
+ EXPECT_TRUE(createIPCheck("192.0.2.1")->matches(getRequest4()));
+ // Exact match (negative)
+ EXPECT_FALSE(createIPCheck("192.0.2.53")->matches(getRequest4()));
+ // Prefix match
+ EXPECT_TRUE(createIPCheck("192.0.2.0/24")->matches(getRequest4()));
+ // Prefix match (negative)
+ EXPECT_FALSE(createIPCheck("192.0.1.0/24")->matches(getRequest4()));
+ // Address family mismatch (the first 4 bytes of the IPv6 address has the
+ // same binary representation as the client's IPv4 address, which
+ // shouldn't confuse the match logic)
+ EXPECT_FALSE(createIPCheck("c000:0201::")->matches(getRequest4()));
+}
+
+TEST_F(RequestCheckTest, checkIPv6) {
+ // The following are a set of tests of the same concept as checkIPv4
+ EXPECT_TRUE(createIPCheck("2001:db8::1")->matches(getRequest6()));
+ EXPECT_FALSE(createIPCheck("2001:db8::53")->matches(getRequest6()));
+ EXPECT_TRUE(createIPCheck("2001:db8::/64")->matches(getRequest6()));
+ EXPECT_FALSE(createIPCheck("2001:db8:1::/64")->matches(getRequest6()));
+ EXPECT_FALSE(createIPCheck("32.1.13.184")->matches(getRequest6()));
+}
+
+// The following tests test only the creators are registered, they are tested
+// elsewhere
+
+TEST(DNSACL, notLoad) {
+ EXPECT_NO_THROW(getRequestLoader().loadCheck(isc::data::Element::fromJSON(
+ "{\"NOT\": {\"from\": \"192.0.2.1\"}}")));
+}
+
+TEST(DNSACL, allLoad) {
+ EXPECT_NO_THROW(getRequestLoader().loadCheck(isc::data::Element::fromJSON(
+ "{\"ALL\": [{\"from\": \"192.0.2.1\"}]}")));
+}
+
+TEST(DNSACL, anyLoad) {
+ EXPECT_NO_THROW(getRequestLoader().loadCheck(isc::data::Element::fromJSON(
+ "{\"ANY\": [{\"from\": \"192.0.2.1\"}]}")));
+}
+
+}
diff --git a/src/lib/acl/tests/ip_check_unittest.cc b/src/lib/acl/tests/ip_check_unittest.cc
new file mode 100644
index 0000000..8b8c498
--- /dev/null
+++ b/src/lib/acl/tests/ip_check_unittest.cc
@@ -0,0 +1,617 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+
+#include <gtest/gtest.h>
+#include <acl/ip_check.h>
+
+#include "sockaddr.h"
+
+using namespace isc::acl;
+using namespace isc::acl::internal;
+using namespace std;
+
+namespace {
+const size_t IPV4_SIZE = 4;
+const size_t IPV6_SIZE = 16;
+
+// Simple struct holding either an IPV4 or IPV6 address. This is the "Context"
+// used for the tests.
+//
+// The structure is also used for converting an IPV4 address to a four-byte
+// array.
+struct GeneralAddress {
+ int family; // Family of the address
+ vector<uint8_t> addr; // Address type. Size indicates what it holds
+
+ // Convert uint32_t address in host-byte order to a uint8_t vector in
+ // network-byte order.
+ vector<uint8_t> convertUint32(uint32_t address) {
+ BOOST_STATIC_ASSERT(sizeof(uint32_t) == IPV4_SIZE);
+
+ vector<uint8_t> result(IPV4_SIZE);
+
+ // Address is in network-byte order, so copy to the array. The
+ // MS byte is at the lowest address.
+ result[3] = address & 0xff;
+ result[2] = (address >> 8) & 0xff;
+ result[1] = (address >> 16) & 0xff;
+ result[0] = (address >> 24) & 0xff;
+
+ return (result);
+ }
+
+ // Convenience constructor for V4 address. As it is not marked as explicit,
+ // it allows the automatic promotion of a uint32_t to a GeneralAddress data
+ // type in calls to matches().
+ GeneralAddress(uint32_t address) : family(AF_INET), addr()
+ {
+ addr = convertUint32(address);
+ }
+
+ // Convenience constructor for V6 address. As it is not marked as explicit,
+ // it allows the automatic promotion of a vector<uint8_t> to a
+ // GeneralAddress data type in calls to matches().
+ GeneralAddress(const vector<uint8_t>& address) : family(AF_INET6),
+ addr(address)
+ {
+ if (address.size() != IPV6_SIZE) {
+ isc_throw(isc::InvalidParameter, "vector passed to GeneralAddress "
+ "constructor is " << address.size() << " bytes long - it "
+ "should be " << IPV6_SIZE << " bytes instead");
+ }
+ }
+
+ // A couple of convenience methods for checking equality with different
+ // representations of an address.
+
+ // Check that the IPV4 address is the same as that given.
+ bool equals(uint32_t address) {
+ if (family == AF_INET) {
+ const vector<uint8_t> byte_address = convertUint32(address);
+ return (equal(byte_address.begin(), byte_address.end(),
+ addr.begin()));
+ }
+ return (false);
+ }
+
+ // Check that the array is equal to that given.
+ bool equals(const vector<uint8_t>& byte_address) {
+ if (addr.size() == byte_address.size()) {
+ return (equal(byte_address.begin(), byte_address.end(),
+ addr.begin()));
+ }
+ return (false);
+ }
+};
+} // Unnamed namespace
+
+// Provide a specialisation of the IPCheck::matches() method for the
+// GeneralAddress class.
+
+namespace isc {
+namespace acl {
+template <>
+bool IPCheck<GeneralAddress>::matches(const GeneralAddress& address) const {
+ return (compare(&address.addr[0], address.family));
+}
+} // namespace acl
+} // namespace isc
+
+namespace {
+/// *** Free Function Tests ***
+
+// Test the createMask() function.
+TEST(IPFunctionCheck, CreateMask) {
+
+ // Invalid arguments should throw.
+ EXPECT_THROW(createMask(9), isc::OutOfRange);
+
+ // Check on all possible 8-bit values.
+ uint16_t expected = 0xff00;
+ for (size_t i = 0; i <= 8; ++i, expected >>= 1) {
+ EXPECT_EQ(static_cast<uint8_t>(expected & 0xff), createMask(i));
+ }
+}
+
+// Test the splitIPAddress() function.
+TEST(IPFunctionCheck, SplitIPAddress) {
+ pair<string, uint32_t> result;
+
+ result = splitIPAddress("192.0.2.1");
+ EXPECT_EQ(string("192.0.2.1"), result.first);
+ EXPECT_EQ(-1, result.second);
+
+ result = splitIPAddress("192.0.2.1/24");
+ EXPECT_EQ(string("192.0.2.1"), result.first);
+ EXPECT_EQ(24, result.second);
+
+ result = splitIPAddress("2001:db8::/128");
+ EXPECT_EQ(string("2001:db8::"), result.first);
+ EXPECT_EQ(128, result.second);
+
+ result = splitIPAddress("192.0.2.1/0");
+ EXPECT_EQ(string("192.0.2.1"), result.first);
+ EXPECT_EQ(0, result.second);
+
+ EXPECT_THROW(splitIPAddress("192.0.2.43/27 "), isc::InvalidParameter);
+ EXPECT_THROW(splitIPAddress("192.0.2.43/-1"), isc::InvalidParameter);
+ EXPECT_THROW(splitIPAddress("192.0.2.43//1"), isc::InvalidParameter);
+ EXPECT_THROW(splitIPAddress("192.0.2.43/1/"), isc::InvalidParameter);
+ EXPECT_THROW(splitIPAddress("/192.0.2.43/1"), isc::InvalidParameter);
+ EXPECT_THROW(splitIPAddress("2001:db8::/xxxx"), isc::InvalidParameter);
+ EXPECT_THROW(splitIPAddress("2001:db8::/32/s"), isc::InvalidParameter);
+ EXPECT_THROW(splitIPAddress("1/"), isc::InvalidParameter);
+ EXPECT_THROW(splitIPAddress("/1"), isc::InvalidParameter);
+ EXPECT_THROW(splitIPAddress(" 1/ "), isc::InvalidParameter);
+}
+
+TEST(IPAddress, constructIPv4) {
+ IPAddress ipaddr(tests::getSockAddr("192.0.2.1"));
+ const char expected_data[4] = { 192, 0, 2, 1 };
+ EXPECT_EQ(AF_INET, ipaddr.getFamily());
+ EXPECT_EQ(4, ipaddr.getLength());
+ EXPECT_EQ(0, memcmp(expected_data, ipaddr.getData(), 4));
+}
+
+TEST(IPAddress, constructIPv6) {
+ IPAddress ipaddr(tests::getSockAddr("2001:db8:1234:abcd::53"));
+ const char expected_data[16] = { 0x20, 0x01, 0x0d, 0xb8, 0x12, 0x34, 0xab,
+ 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x53 };
+ EXPECT_EQ(AF_INET6, ipaddr.getFamily());
+ EXPECT_EQ(16, ipaddr.getLength());
+ EXPECT_EQ(0, memcmp(expected_data, ipaddr.getData(), 16));
+}
+
+TEST(IPAddress, badConstruct) {
+ struct sockaddr sa;
+ sa.sa_family = AF_UNSPEC;
+ EXPECT_THROW(IPAddress ipaddr(sa), isc::BadValue);
+}
+
+// *** IPv4 Tests ***
+
+TEST(IPCheck, V4StringConstructor) {
+
+ // Constructor with no prefix length given (32 is assumed).
+ IPCheck<GeneralAddress> acl1("192.0.2.255");
+ EXPECT_EQ(32, acl1.getPrefixlen());
+ EXPECT_EQ(AF_INET, acl1.getFamily());
+
+ vector<uint8_t> stored1 = acl1.getAddress();
+ EXPECT_EQ(IPV4_SIZE, stored1.size());
+ GeneralAddress expected1(0xc00002ff);
+ EXPECT_TRUE(expected1.equals(stored1));
+
+ // Constructor with valid mask given
+ IPCheck<GeneralAddress> acl2("192.0.2.0/24");
+ EXPECT_EQ(24, acl2.getPrefixlen());
+ EXPECT_EQ(AF_INET, acl2.getFamily());
+
+ vector<uint8_t> stored2 = acl2.getAddress();
+ EXPECT_EQ(IPV4_SIZE, stored2.size());
+ GeneralAddress expected2(0xc0000200);
+ EXPECT_TRUE(expected2.equals(stored2));
+
+ // More valid masks
+ IPCheck<GeneralAddress> acl3("192.0.2.1/0");
+ EXPECT_EQ(0, acl3.getPrefixlen());
+ EXPECT_EQ(AF_INET, acl3.getFamily());
+
+ vector<uint8_t> stored3 = acl3.getAddress();
+ EXPECT_EQ(IPV4_SIZE, stored3.size());
+ GeneralAddress expected3(0xc0000201);
+ EXPECT_TRUE(expected3.equals(stored3));
+
+ IPCheck<GeneralAddress> acl4("192.0.2.2/32");
+ EXPECT_EQ(32, acl4.getPrefixlen());
+ EXPECT_EQ(AF_INET, acl4.getFamily());
+
+ vector<uint8_t> stored4 = acl4.getAddress();
+ EXPECT_EQ(IPV4_SIZE, stored4.size());
+ GeneralAddress expected4(0xc0000202);
+ EXPECT_TRUE(expected4.equals(stored4));
+
+ // Any match
+ IPCheck<GeneralAddress> acl5("any4");
+ EXPECT_EQ(0, acl5.getPrefixlen());
+ EXPECT_EQ(AF_INET, acl5.getFamily());
+
+ vector<uint8_t> stored5 = acl5.getAddress();
+ EXPECT_EQ(IPV4_SIZE, stored5.size());
+ GeneralAddress expected5(0);
+ EXPECT_TRUE(expected5.equals(stored5));
+
+ // Invalid prefix lengths
+ EXPECT_THROW(IPCheck<GeneralAddress>("192.0.2.0/33"), isc::OutOfRange);
+
+ // ... and invalid strings
+ EXPECT_THROW(IPCheck<GeneralAddress>("192.0.2.0/-1"),
+ isc::InvalidParameter);
+ EXPECT_THROW(IPCheck<GeneralAddress>("192.0.2.0/24/3"),
+ isc::InvalidParameter);
+ EXPECT_THROW(IPCheck<GeneralAddress>("192.0.2.0/ww"),
+ isc::InvalidParameter);
+ EXPECT_THROW(IPCheck<GeneralAddress>("aa.255.255.0/ww"),
+ isc::InvalidParameter);
+}
+
+TEST(IPCheck, V4CopyConstructor) {
+ IPCheck<GeneralAddress> acl1("192.0.2.1/24");
+ IPCheck<GeneralAddress> acl2(acl1);
+
+ EXPECT_EQ(acl1.getPrefixlen(), acl2.getPrefixlen());
+ EXPECT_EQ(acl1.getFamily(), acl2.getFamily());
+
+ vector<uint8_t> net1 = acl1.getMask();
+ vector<uint8_t> net2 = acl2.getMask();
+ EXPECT_EQ(net1.size(), net2.size());
+ EXPECT_TRUE(equal(net1.begin(), net1.end(), net2.begin()));
+
+ net1 = acl1.getAddress();
+ net2 = acl2.getAddress();
+ EXPECT_EQ(net1.size(), net2.size());
+ EXPECT_TRUE(equal(net1.begin(), net1.end(), net2.begin()));
+}
+
+TEST(IPCheck, V4AssignmentOperator) {
+ IPCheck<GeneralAddress> acl1("192.0.2.0/24");
+ IPCheck<GeneralAddress> acl2("192.0.2.128/25");
+ acl2 = acl1;
+
+ EXPECT_EQ(acl1.getPrefixlen(), acl2.getPrefixlen());
+ EXPECT_EQ(acl1.getFamily(), acl2.getFamily());
+
+ vector<uint8_t> net1 = acl1.getMask();
+ vector<uint8_t> net2 = acl2.getMask();
+ EXPECT_EQ(net1.size(), net2.size());
+ EXPECT_TRUE(equal(net1.begin(), net1.end(), net2.begin()));
+
+ net1 = acl1.getAddress();
+ net2 = acl2.getAddress();
+ EXPECT_EQ(net1.size(), net2.size());
+ EXPECT_TRUE(equal(net1.begin(), net1.end(), net2.begin()));
+}
+
+// Check that the comparison works - note that "matches" just calls the
+// internal compare() code. (Also note that the argument to matches() will be
+// automatically converted to the GeneralAddress data type used for the tests
+// because of its constructor taking a uint32_t argument.
+
+TEST(IPCheck, V4Compare) {
+ // Exact address - match if given address matches stored address.
+ IPCheck<GeneralAddress> acl1("192.0.2.255/32");
+ EXPECT_TRUE(acl1.matches(0xc00002ff));
+ EXPECT_FALSE(acl1.matches(0xc00002fe));
+ EXPECT_FALSE(acl1.matches(0x13457f13));
+
+ IPCheck<GeneralAddress> acl2("192.0.2.255/27");
+ EXPECT_TRUE(acl2.matches(0xc00002ff));
+ EXPECT_TRUE(acl2.matches(0xc00002fe));
+ EXPECT_TRUE(acl2.matches(0xc00002ee));
+ EXPECT_FALSE(acl2.matches(0xc00002de));
+ EXPECT_FALSE(acl2.matches(0xd00002fe));
+ EXPECT_FALSE(acl2.matches(0x13457f13));
+
+ // Match if "any4" is specified
+ IPCheck<GeneralAddress> acl3("any4");
+ EXPECT_TRUE(acl3.matches(0xc00002ff));
+ EXPECT_TRUE(acl3.matches(0xc00002fe));
+ EXPECT_TRUE(acl3.matches(0xc00002ee));
+ EXPECT_TRUE(acl3.matches(0xc00002de));
+ EXPECT_TRUE(acl3.matches(0xd00002fe));
+ EXPECT_TRUE(acl3.matches(0x13457f13));
+
+ IPCheck<GeneralAddress> acl4("0.0.0.0/0");
+ EXPECT_TRUE(acl4.matches(0xc00002ff));
+ EXPECT_TRUE(acl4.matches(0xc00002fe));
+ EXPECT_TRUE(acl4.matches(0xc00002ee));
+ EXPECT_TRUE(acl4.matches(0xc00002de));
+ EXPECT_TRUE(acl4.matches(0xd00002fe));
+ EXPECT_TRUE(acl4.matches(0x13457f13));
+
+ IPCheck<GeneralAddress> acl5("192.0.2.255/0");
+ EXPECT_TRUE(acl5.matches(0xc00002ff));
+ EXPECT_TRUE(acl5.matches(0xc00002fe));
+ EXPECT_TRUE(acl5.matches(0xc00002ee));
+ EXPECT_TRUE(acl5.matches(0xc00002de));
+ EXPECT_TRUE(acl5.matches(0xd00002fe));
+ EXPECT_TRUE(acl5.matches(0x13457f13));
+}
+
+// *** IPV6 Tests ***
+
+// Some constants used in the tests
+
+const char* V6ADDR_1_STRING = "2001:0db8:1122:3344:5566:7788:99aa:bbcc";
+const uint8_t V6ADDR_1[] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x11, 0x22, 0x33, 0x44,
+ 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc
+};
+
+const char* V6ADDR_2_STRING = "2001:0db8::dead:beef";
+const uint8_t V6ADDR_2[] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
+};
+
+// Identical to V6ADDR_2 to 48 bits
+const uint8_t V6ADDR_2_48[] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0xff, 0x66,
+ 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
+};
+
+// Identical to V6ADDR_2 to 49 bits
+const uint8_t V6ADDR_2_49[] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x7f, 0x66,
+ 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
+};
+
+// Identical to V6ADDR_2 to 50 bits
+const uint8_t V6ADDR_2_50[] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x3f, 0x66,
+ 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
+};
+
+// Identical to V6ADDR_2 to 51 bits
+const uint8_t V6ADDR_2_51[] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x1f, 0x66,
+ 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
+};
+
+// Identical to V6ADDR_2 to 51 bits
+const uint8_t V6ADDR_2_52[] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x0f, 0x66,
+ 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
+};
+
+// Identical to V6ADDR_2 to 127 bits
+const uint8_t V6ADDR_2_127[] = {
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xee
+};
+
+const uint8_t V6ADDR_3[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+};
+
+const uint8_t V6ADDR_4[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+TEST(IPCheck, V6StringConstructor) {
+ IPCheck<GeneralAddress> acl1(V6ADDR_1_STRING);
+ vector<uint8_t> address = acl1.getAddress();
+
+ EXPECT_EQ(128, acl1.getPrefixlen());
+ EXPECT_EQ(AF_INET6, acl1.getFamily());
+ EXPECT_EQ(IPV6_SIZE, address.size());
+ EXPECT_TRUE(equal(address.begin(), address.end(), V6ADDR_1));
+
+ IPCheck<GeneralAddress> acl2(string(V6ADDR_2_STRING) + string("/51"));
+ address = acl2.getAddress();
+ EXPECT_EQ(IPV6_SIZE, address.size());
+ EXPECT_EQ(51, acl2.getPrefixlen());
+ EXPECT_EQ(AF_INET6, acl2.getFamily());
+ EXPECT_TRUE(equal(address.begin(), address.end(), V6ADDR_2));
+
+ IPCheck<GeneralAddress> acl3(string(V6ADDR_2_STRING) + string("/127"));
+ address = acl3.getAddress();
+ EXPECT_EQ(IPV6_SIZE, address.size());
+ EXPECT_EQ(127, acl3.getPrefixlen());
+ EXPECT_EQ(AF_INET6, acl3.getFamily());
+ EXPECT_TRUE(equal(address.begin(), address.end(), V6ADDR_2));
+
+ IPCheck<GeneralAddress> acl4("::1");
+ address = acl4.getAddress();
+ EXPECT_EQ(IPV6_SIZE, address.size());
+ EXPECT_EQ(128, acl4.getPrefixlen());
+ EXPECT_EQ(AF_INET6, acl4.getFamily());
+ EXPECT_TRUE(equal(address.begin(), address.end(), V6ADDR_3));
+
+ // Any match. In these cases, the address should all be zeroes.
+ IPCheck<GeneralAddress> acl5("any6");
+ address = acl5.getAddress();
+ EXPECT_EQ(IPV6_SIZE, address.size());
+ EXPECT_EQ(0, acl5.getPrefixlen());
+ EXPECT_EQ(AF_INET6, acl5.getFamily());
+ EXPECT_TRUE(equal(address.begin(), address.end(), V6ADDR_4));
+
+ IPCheck<GeneralAddress> acl6("::/0");
+ address = acl6.getAddress();
+ EXPECT_EQ(0, acl6.getPrefixlen());
+ EXPECT_EQ(AF_INET6, acl6.getFamily());
+ EXPECT_TRUE(equal(address.begin(), address.end(), V6ADDR_4));
+
+ // Some invalid strings
+ EXPECT_THROW(IPCheck<GeneralAddress>("::1/129"), isc::OutOfRange);
+ EXPECT_THROW(IPCheck<GeneralAddress>("::1/24/3"), isc::InvalidParameter);
+ EXPECT_THROW(IPCheck<GeneralAddress>(":::1/24"), isc::InvalidParameter);
+ EXPECT_THROW(IPCheck<GeneralAddress>("2001:0db8::abcd/ww"),
+ isc::InvalidParameter);
+ EXPECT_THROW(IPCheck<GeneralAddress>("2xx1:0db8::abcd/32"),
+ isc::InvalidParameter);
+}
+
+TEST(IPCheck, V6CopyConstructor) {
+ IPCheck<GeneralAddress> acl1(string(V6ADDR_2_STRING) + string("/52"));
+ IPCheck<GeneralAddress> acl2(acl1);
+
+ vector<uint8_t> acl1_address = acl1.getAddress();
+ vector<uint8_t> acl2_address = acl1.getAddress();
+ EXPECT_EQ(sizeof(V6ADDR_1), acl1_address.size());
+ EXPECT_EQ(acl1_address.size(), acl2_address.size());
+ EXPECT_TRUE(equal(acl1_address.begin(), acl1_address.end(),
+ acl2_address.begin()));
+
+ EXPECT_EQ(acl1.getPrefixlen(), acl2.getPrefixlen());
+
+ vector<uint8_t> acl1_mask = acl1.getMask();
+ vector<uint8_t> acl2_mask = acl1.getMask();
+ EXPECT_EQ(sizeof(V6ADDR_1), acl1_mask.size());
+ EXPECT_EQ(acl1_mask.size(), acl2_mask.size());
+ EXPECT_TRUE(equal(acl1_mask.begin(), acl1_mask.end(),
+ acl2_mask.begin()));
+}
+
+TEST(IPCheck, V6AssignmentOperator) {
+ IPCheck<GeneralAddress> acl1(string(V6ADDR_2_STRING) + string("/52"));
+ IPCheck<GeneralAddress> acl2(string(V6ADDR_1_STRING) + string("/48"));
+
+ acl2 = acl1;
+
+ vector<uint8_t> acl1_address = acl1.getAddress();
+ vector<uint8_t> acl2_address = acl2.getAddress();
+ EXPECT_EQ(sizeof(V6ADDR_1), acl1_address.size());
+ EXPECT_EQ(acl1_address.size(), acl2_address.size());
+ EXPECT_TRUE(equal(acl1_address.begin(), acl1_address.end(),
+ acl2_address.begin()));
+
+ EXPECT_EQ(acl1.getPrefixlen(), acl2.getPrefixlen());
+
+ vector<uint8_t> acl1_mask = acl1.getMask();
+ vector<uint8_t> acl2_mask = acl2.getMask();
+ EXPECT_EQ(sizeof(V6ADDR_1), acl1_mask.size());
+ EXPECT_EQ(acl1_mask.size(), acl2_mask.size());
+ EXPECT_TRUE(equal(acl1_mask.begin(), acl1_mask.end(),
+ acl2_mask.begin()));
+}
+
+TEST(IPCheck, V6Compare) {
+ // Set up some data.
+ vector<uint8_t> v6addr_2(V6ADDR_2, V6ADDR_2 + IPV6_SIZE);
+ vector<uint8_t> v6addr_2_48(V6ADDR_2_48, V6ADDR_2_48 + IPV6_SIZE);
+ vector<uint8_t> v6addr_2_49(V6ADDR_2_49, V6ADDR_2_49 + IPV6_SIZE);
+ vector<uint8_t> v6addr_2_50(V6ADDR_2_50, V6ADDR_2_50 + IPV6_SIZE);
+ vector<uint8_t> v6addr_2_51(V6ADDR_2_51, V6ADDR_2_51 + IPV6_SIZE);
+ vector<uint8_t> v6addr_2_52(V6ADDR_2_52, V6ADDR_2_52 + IPV6_SIZE);
+ vector<uint8_t> v6addr_2_127(V6ADDR_2_127, V6ADDR_2_127 + IPV6_SIZE);
+ vector<uint8_t> v6addr_3(V6ADDR_3, V6ADDR_3 + IPV6_SIZE);
+
+ // Exact address - match if given address matches stored address.
+ IPCheck<GeneralAddress> acl1(string(V6ADDR_2_STRING) + string("/128"));
+ EXPECT_TRUE(acl1.matches(v6addr_2));
+ EXPECT_FALSE(acl1.matches(v6addr_2_127));
+ EXPECT_FALSE(acl1.matches(v6addr_2_52));
+ EXPECT_FALSE(acl1.matches(v6addr_2_51));
+ EXPECT_FALSE(acl1.matches(v6addr_2_50));
+ EXPECT_FALSE(acl1.matches(v6addr_2_49));
+ EXPECT_FALSE(acl1.matches(v6addr_2_48));
+ EXPECT_FALSE(acl1.matches(v6addr_3));
+
+ // Match to various prefixes.
+ IPCheck<GeneralAddress> acl2(string(V6ADDR_2_STRING) + string("/127"));
+ EXPECT_TRUE(acl2.matches(v6addr_2));
+ EXPECT_TRUE(acl2.matches(v6addr_2_127));
+ EXPECT_FALSE(acl2.matches(v6addr_2_52));
+ EXPECT_FALSE(acl2.matches(v6addr_2_51));
+ EXPECT_FALSE(acl2.matches(v6addr_2_50));
+ EXPECT_FALSE(acl2.matches(v6addr_2_49));
+ EXPECT_FALSE(acl2.matches(v6addr_2_48));
+ EXPECT_FALSE(acl2.matches(v6addr_3));
+
+ IPCheck<GeneralAddress> acl3(string(V6ADDR_2_STRING) + string("/52"));
+ EXPECT_TRUE(acl3.matches(v6addr_2));
+ EXPECT_TRUE(acl3.matches(v6addr_2_127));
+ EXPECT_TRUE(acl3.matches(v6addr_2_52));
+ EXPECT_FALSE(acl3.matches(v6addr_2_51));
+ EXPECT_FALSE(acl3.matches(v6addr_2_50));
+ EXPECT_FALSE(acl3.matches(v6addr_2_49));
+ EXPECT_FALSE(acl3.matches(v6addr_2_48));
+ EXPECT_FALSE(acl3.matches(v6addr_3));
+
+ IPCheck<GeneralAddress> acl4(string(V6ADDR_2_STRING) + string("/51"));
+ EXPECT_TRUE(acl4.matches(v6addr_2));
+ EXPECT_TRUE(acl4.matches(v6addr_2_127));
+ EXPECT_TRUE(acl4.matches(v6addr_2_52));
+ EXPECT_TRUE(acl4.matches(v6addr_2_51));
+ EXPECT_FALSE(acl4.matches(v6addr_2_50));
+ EXPECT_FALSE(acl4.matches(v6addr_2_49));
+ EXPECT_FALSE(acl4.matches(v6addr_2_48));
+ EXPECT_FALSE(acl4.matches(v6addr_3));
+
+ IPCheck<GeneralAddress> acl5(string(V6ADDR_2_STRING) + string("/50"));
+ EXPECT_TRUE(acl5.matches(v6addr_2));
+ EXPECT_TRUE(acl5.matches(v6addr_2_127));
+ EXPECT_TRUE(acl5.matches(v6addr_2_52));
+ EXPECT_TRUE(acl5.matches(v6addr_2_51));
+ EXPECT_TRUE(acl5.matches(v6addr_2_50));
+ EXPECT_FALSE(acl5.matches(v6addr_2_49));
+ EXPECT_FALSE(acl5.matches(v6addr_2_48));
+ EXPECT_FALSE(acl5.matches(v6addr_3));
+
+ IPCheck<GeneralAddress> acl6(string(V6ADDR_2_STRING) + string("/0"));
+ EXPECT_TRUE(acl6.matches(v6addr_2));
+ EXPECT_TRUE(acl6.matches(v6addr_2_127));
+ EXPECT_TRUE(acl6.matches(v6addr_2_52));
+ EXPECT_TRUE(acl6.matches(v6addr_2_51));
+ EXPECT_TRUE(acl6.matches(v6addr_2_50));
+ EXPECT_TRUE(acl6.matches(v6addr_2_49));
+ EXPECT_TRUE(acl6.matches(v6addr_2_48));
+ EXPECT_TRUE(acl6.matches(v6addr_3));
+
+ // Match on any address
+ IPCheck<GeneralAddress> acl7("any6");
+ EXPECT_TRUE(acl7.matches(v6addr_2));
+ EXPECT_TRUE(acl7.matches(v6addr_2_127));
+ EXPECT_TRUE(acl7.matches(v6addr_2_52));
+ EXPECT_TRUE(acl7.matches(v6addr_2_51));
+ EXPECT_TRUE(acl7.matches(v6addr_2_50));
+ EXPECT_TRUE(acl7.matches(v6addr_2_49));
+ EXPECT_TRUE(acl7.matches(v6addr_2_48));
+}
+
+// *** Mixed-mode tests - mainly to check that no exception is thrown ***
+
+TEST(IPCheck, MixedMode) {
+
+ // ACL has a V4 address specified, check against a V6 address.
+ IPCheck<GeneralAddress> acl1("192.0.2.255/24");
+ GeneralAddress test1(vector<uint8_t>(V6ADDR_1, V6ADDR_1 + IPV6_SIZE));
+ EXPECT_NO_THROW(acl1.matches(test1));
+ EXPECT_FALSE(acl1.matches(test1));
+
+ // Now the reverse - the ACL is specified with a V6 address.
+ IPCheck<GeneralAddress> acl2(V6ADDR_2_STRING);
+ GeneralAddress test2(0x12345678);
+ EXPECT_FALSE(acl2.matches(test2));
+
+ // Ensure only a V4 address matches "any4".
+ IPCheck<GeneralAddress> acl3("any4");
+ EXPECT_FALSE(acl3.matches(test1));
+ EXPECT_TRUE(acl3.matches(test2));
+
+ // ... and check the reverse
+ IPCheck<GeneralAddress> acl4("any6");
+ EXPECT_TRUE(acl4.matches(test1));
+ EXPECT_FALSE(acl4.matches(test2));
+
+ // Check where the bit pattern of an IPv4 address matches that of an IPv6
+ // one.
+ IPCheck<GeneralAddress> acl5("2001:db8::/32");
+ GeneralAddress test5(0x20010db8);
+ EXPECT_FALSE(acl5.matches(test5));
+
+ // ... and where the reverse is true. (2001:db8 corresponds to 32.1.13.184).
+ IPCheck<GeneralAddress> acl6("32.1.13.184");
+ GeneralAddress test6(vector<uint8_t>(V6ADDR_1, V6ADDR_1 + IPV6_SIZE));
+ EXPECT_FALSE(acl6.matches(test6));
+}
+} // Unnamed namespace
diff --git a/src/lib/acl/tests/loader_test.cc b/src/lib/acl/tests/loader_test.cc
new file mode 100644
index 0000000..1705c0a
--- /dev/null
+++ b/src/lib/acl/tests/loader_test.cc
@@ -0,0 +1,383 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "creators.h"
+#include <exceptions/exceptions.h>
+#include <acl/loader.h>
+#include <string>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace boost;
+using namespace isc::acl;
+using namespace isc::acl::tests;
+using isc::data::Element;
+using isc::data::ConstElementPtr;
+
+namespace {
+
+// We don't use the EXPECT_THROW macro, as it doesn't allow us
+// to examine the exception. We want to check the element is stored
+// there as well.
+void testActionLoaderException(const string& JSON) {
+ SCOPED_TRACE("Should throw with input: " + JSON);
+ ConstElementPtr elem(Element::fromJSON(JSON));
+ try {
+ defaultActionLoader(elem);
+ FAIL() << "It did not throw";
+ }
+ catch (const LoaderError& error) {
+ // Yes, comparing for pointer equality, that is enough, it
+ // should return the exact instance of the JSON object
+ EXPECT_EQ(elem, error.element());
+ }
+}
+
+// Test the defaultActionLoader function
+TEST(LoaderHelpers, DefaultActionLoader) {
+ // First the three valid inputs
+ EXPECT_EQ(ACCEPT, defaultActionLoader(Element::fromJSON("\"ACCEPT\"")));
+ EXPECT_EQ(REJECT, defaultActionLoader(Element::fromJSON("\"REJECT\"")));
+ EXPECT_EQ(DROP, defaultActionLoader(Element::fromJSON("\"DROP\"")));
+ // Now few invalid ones
+ // String, but unknown one
+ testActionLoaderException("\"UNKNOWN\"");
+ testActionLoaderException("42");
+ testActionLoaderException("true");
+ testActionLoaderException("null");
+ testActionLoaderException("[]");
+ testActionLoaderException("{}");
+}
+
+class LoaderTest : public ::testing::Test {
+public:
+ LoaderTest() :
+ loader_(REJECT)
+ {}
+ Loader<Log> loader_;
+ Log log_;
+ // Some convenience functions to set up
+
+ // Create a NamedCreator, convert to shared pointer
+ shared_ptr<NamedCreator> namedCreator(const string& name,
+ bool abbreviatedList = true)
+ {
+ return (shared_ptr<NamedCreator>(new NamedCreator(name,
+ abbreviatedList)));
+ }
+ // Create and add a NamedCreator
+ void addNamed(const string& name, bool abbreviatedList = true) {
+ EXPECT_NO_THROW(loader_.registerCreator(
+ namedCreator(name, abbreviatedList)));
+ }
+ template<class Result> shared_ptr<Result> loadCheckAny(const string&
+ definition)
+ {
+ SCOPED_TRACE("Loading check " + definition);
+ shared_ptr<Check<Log> > loaded;
+ EXPECT_NO_THROW(loaded = loader_.loadCheck(
+ Element::fromJSON(definition)));
+ shared_ptr<Result> result(dynamic_pointer_cast<Result>(
+ loaded));
+ EXPECT_TRUE(result);
+ return (result);
+ }
+ // Load a check and convert it to named check to examine it
+ shared_ptr<NamedCheck> loadCheck(const string& definition) {
+ return (loadCheckAny<NamedCheck>(definition));
+ }
+ // The loadCheck throws an exception
+ void checkException(const string& JSON) {
+ SCOPED_TRACE("Loading check exception: " + JSON);
+ ConstElementPtr input(Element::fromJSON(JSON));
+ // Not using EXPECT_THROW, we want to examine the exception
+ try {
+ loader_.loadCheck(input);
+ FAIL() << "Should have thrown";
+ }
+ catch (const LoaderError& e) {
+ // It should be identical copy, so checking pointers
+ EXPECT_EQ(input, e.element());
+ }
+ }
+ // Insert the throw, throwcheck and logcheck checks into the loader
+ void aclSetup() {
+ try {
+ loader_.registerCreator(shared_ptr<ThrowCreator>(new
+ ThrowCreator()));
+ loader_.registerCreator(shared_ptr<ThrowCheckCreator>(
+ new ThrowCheckCreator()));
+ loader_.registerCreator(shared_ptr<LogCreator>(new LogCreator()));
+ }
+ // We ignore this exception here, because it happens when we try to
+ // insert the creators multiple times. This is harmless.
+ catch (const LoaderError&) {}
+ }
+ // Create an ACL, run it, check it's result and how many first
+ // log items it marked
+ //
+ // Works with preset names throw and logcheck
+ void aclRun(const string& JSON, BasicAction expectedResult,
+ size_t logged)
+ {
+ SCOPED_TRACE("Running ACL for " + JSON);
+ aclSetup();
+ shared_ptr<ACL<Log> > acl;
+ EXPECT_NO_THROW(acl = loader_.load(Element::fromJSON(JSON)));
+ EXPECT_EQ(expectedResult, acl->execute(log_));
+ log_.checkFirst(logged);
+ }
+ // Check it throws an error when creating the ACL
+ void aclException(const string& JSON) {
+ SCOPED_TRACE("Trying to load bad " + JSON);
+ aclSetup();
+ EXPECT_THROW(loader_.load(Element::fromJSON(JSON)), LoaderError);
+ }
+ // Check that the subexpression is NamedCheck with correct data
+ void isSubexprNamed(const CompoundCheck<Log>* compound, size_t index,
+ const string& name, ConstElementPtr data)
+ {
+ if (index < compound->getSubexpressions().size()) {
+ const NamedCheck*
+ check(dynamic_cast<const NamedCheck*>(compound->
+ getSubexpressions()
+ [index]));
+ ASSERT_TRUE(check) << "The subexpression is of different type";
+ EXPECT_EQ(name, check->name_);
+ EXPECT_TRUE(data->equals(*check->data_));
+ }
+ }
+};
+
+// Test that it does not accept duplicate creator
+TEST_F(LoaderTest, CreatorDuplicity) {
+ addNamed("name");
+ EXPECT_THROW(loader_.registerCreator(namedCreator("name")), LoaderError);
+}
+
+// Test that when it does not accept a duplicate, nothing is inserted
+TEST_F(LoaderTest, CreatorDuplicateUnchanged) {
+ addNamed("name1");
+ vector<string> names;
+ names.push_back("name2");
+ names.push_back("name1");
+ names.push_back("name3");
+ EXPECT_THROW(loader_.registerCreator(
+ shared_ptr<NamedCreator>(new NamedCreator(names))), LoaderError);
+ // It should now reject both name2 and name3 as not known
+ checkException("{\"name2\": null}");
+ checkException("{\"name3\": null}");
+}
+
+// Test that we can register a creator and load a check with the name
+TEST_F(LoaderTest, SimpleCheckLoad) {
+ addNamed("name");
+ shared_ptr<NamedCheck> check(loadCheck("{\"name\": 42}"));
+ EXPECT_EQ("name", check->name_);
+ EXPECT_TRUE(check->data_->equals(*Element::fromJSON("42")));
+}
+
+// As above, but there are multiple creators registered within the loader
+TEST_F(LoaderTest, MultiCreatorCheckLoad) {
+ addNamed("name1");
+ addNamed("name2");
+ shared_ptr<NamedCheck> check(loadCheck("{\"name2\": 42}"));
+ EXPECT_EQ("name2", check->name_);
+ EXPECT_TRUE(check->data_->equals(*Element::fromJSON("42")));
+}
+
+// Similar to above, but there's a creator with multiple names
+TEST_F(LoaderTest, MultiNameCheckLoad) {
+ addNamed("name1");
+ vector<string> names;
+ names.push_back("name2");
+ names.push_back("name3");
+ EXPECT_NO_THROW(loader_.registerCreator(shared_ptr<NamedCreator>(
+ new NamedCreator(names))));
+ shared_ptr<NamedCheck> check(loadCheck("{\"name3\": 42}"));
+ EXPECT_EQ("name3", check->name_);
+ EXPECT_TRUE(check->data_->equals(*Element::fromJSON("42")));
+}
+
+// Invalid format is rejected
+TEST_F(LoaderTest, InvalidFormatCheck) {
+ checkException("[]");
+ checkException("42");
+ checkException("\"hello\"");
+ checkException("null");
+}
+
+// Empty check is rejected
+TEST_F(LoaderTest, EmptyCheck) {
+ checkException("{}");
+}
+
+// The name isn't known
+TEST_F(LoaderTest, UnkownName) {
+ checkException("{\"unknown\": null}");
+}
+
+// Exception from the creator is propagated
+TEST_F(LoaderTest, CheckPropagate) {
+ loader_.registerCreator(shared_ptr<ThrowCreator>(new ThrowCreator()));
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"throw\": null}")),
+ TestCreatorError);
+}
+
+// The abbreviated form of check
+TEST_F(LoaderTest, AndAbbrev) {
+ addNamed("name1");
+ addNamed("name2");
+ shared_ptr<LogicOperator<AllOfSpec, Log> > oper(
+ loadCheckAny<LogicOperator<AllOfSpec, Log> >("{\"name1\": 1, \"name2\": 2}"));
+ // If we don't have anything loaded, the rest would crash. It is already
+ // reported from within loadCheckAny if it isn't loaded.
+ if (oper) {
+ // The subexpressions are correct
+ EXPECT_EQ(2, oper->getSubexpressions().size());
+ // Note: this test relies on the ordering in which map returns it's
+ // elements, which is in the lexicographical order of the strings.
+ // This is not required from our interface, but is easier to write
+ // the test.
+ isSubexprNamed(&*oper, 0, "name1", Element::fromJSON("1"));
+ isSubexprNamed(&*oper, 1, "name2", Element::fromJSON("2"));
+ }
+}
+
+// The abbreviated form of parameters
+TEST_F(LoaderTest, OrAbbrev) {
+ addNamed("name1");
+ shared_ptr<LogicOperator<AnyOfSpec, Log> > oper(
+ loadCheckAny<LogicOperator<AnyOfSpec, Log> >("{\"name1\": [1, 2]}"));
+ // If we don't have anything loaded, the rest would crash. It is already
+ // reported from within loadCheckAny if it isn't loaded.
+ if (oper) {
+ // The subexpressions are correct
+ EXPECT_EQ(2, oper->getSubexpressions().size());
+ isSubexprNamed(&*oper, 0, "name1", Element::fromJSON("1"));
+ isSubexprNamed(&*oper, 1, "name1", Element::fromJSON("2"));
+ }
+}
+
+// Combined abbreviated form, both at once
+
+// The abbreviated form of check
+TEST_F(LoaderTest, BothAbbrev) {
+ addNamed("name1");
+ addNamed("name2");
+ shared_ptr<LogicOperator<AllOfSpec, Log> > oper(
+ loadCheckAny<LogicOperator<AllOfSpec, Log> >("{\"name1\": 1, \"name2\": [3, 4]}"));
+ // If we don't have anything loaded, the rest would crash. It is already
+ // reported from within loadCheckAny if it isn't loaded.
+ if (oper) {
+ // The subexpressions are correct
+ ASSERT_EQ(2, oper->getSubexpressions().size());
+ // Note: this test relies on the ordering in which map returns it's
+ // elements, which is in the lexicographical order of the strings.
+ // This is not required from our interface, but is easier to write
+ // the test.
+ isSubexprNamed(&*oper, 0, "name1", Element::fromJSON("1"));
+ const LogicOperator<AnyOfSpec, Log>*
+ orOper(dynamic_cast<const LogicOperator<AnyOfSpec, Log>*>(
+ oper->getSubexpressions()[1]));
+ ASSERT_TRUE(orOper) << "Different type than AnyOf operator";
+ EXPECT_EQ(2, orOper->getSubexpressions().size());
+ isSubexprNamed(orOper, 0, "name2", Element::fromJSON("3"));
+ isSubexprNamed(orOper, 1, "name2", Element::fromJSON("4"));
+ }
+}
+
+// But this is not abbreviated form, this should be passed directly to the
+// creator
+TEST_F(LoaderTest, ListCheck) {
+ addNamed("name1", false);
+ shared_ptr<NamedCheck> check(loadCheck("{\"name1\": [1, 2]}"));
+ EXPECT_EQ("name1", check->name_);
+ EXPECT_TRUE(check->data_->equals(*Element::fromJSON("[1, 2]")));
+}
+
+// Check the action key is ignored as it should be
+TEST_F(LoaderTest, CheckNoAction) {
+ addNamed("name1");
+ shared_ptr<NamedCheck> check(loadCheck("{\"name1\": 1, \"action\": 2}"));
+ EXPECT_EQ("name1", check->name_);
+ EXPECT_TRUE(check->data_->equals(*Element::fromJSON("1")));
+}
+
+// The empty ACL can be created and run, providing the default action
+TEST_F(LoaderTest, EmptyACL) {
+ aclRun("[]", REJECT, 0);
+}
+
+// We can create a simple ACL, which will return the correct default
+// action
+TEST_F(LoaderTest, NoMatchACL) {
+ aclRun("[{\"logcheck\": [0, false], \"action\": \"ACCEPT\"}]",
+ REJECT, 1);
+}
+
+// We can created more complicated ACL, it will match at the second
+// check
+TEST_F(LoaderTest, MatchACL) {
+ aclRun("["
+ " {\"logcheck\": [0, false], \"action\": \"DROP\"},"
+ " {\"logcheck\": [1, true], \"action\": \"ACCEPT\"}"
+ "]", ACCEPT, 2);
+}
+
+// ACL without a check (matches unconditionally)
+// We add another one check after it, to make sure it is really not run
+TEST_F(LoaderTest, NoCheckACL) {
+ aclRun("["
+ " {\"action\": \"DROP\"},"
+ " {\"throwcheck\": 1, \"action\": \"ACCEPT\"}"
+ "]", DROP, 0);
+}
+
+// Malformed things are rejected
+TEST_F(LoaderTest, InvalidACLFormat) {
+ // Not a list
+ aclException("{}");
+ aclException("42");
+ aclException("true");
+ aclException("null");
+ aclException("\"hello\"");
+ // Malformed element
+ aclException("[42]");
+ aclException("[\"hello\"]");
+ aclException("[[]]");
+ aclException("[true]");
+ aclException("[null]");
+}
+
+// If there's no action keyword, it is rejected
+TEST_F(LoaderTest, NoAction) {
+ aclException("[{}]");
+ aclException("[{\"logcheck\": [0, true]}]");
+}
+
+// Exceptions from check creation is propagated
+TEST_F(LoaderTest, ACLPropagate) {
+ aclSetup();
+ EXPECT_THROW(loader_.load(
+ Element::fromJSON(
+ "[{\"action\": \"ACCEPT\", \"throw\": 1}]")),
+ TestCreatorError);
+}
+
+TEST_F(LoaderTest, nullDescription) {
+ EXPECT_THROW(loader_.load(ConstElementPtr()), isc::InvalidParameter);
+}
+
+}
diff --git a/src/lib/acl/tests/logcheck.h b/src/lib/acl/tests/logcheck.h
new file mode 100644
index 0000000..424c53d
--- /dev/null
+++ b/src/lib/acl/tests/logcheck.h
@@ -0,0 +1,94 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef LOGCHECK_H
+#define LOGCHECK_H
+
+#include <gtest/gtest.h>
+#include <acl/acl.h>
+#include <cassert>
+
+// This is not a public header, it is used only inside the tests.
+
+namespace isc {
+namespace acl {
+namespace tests {
+
+// This is arbitrary guess of size for the log. If it's too small for your
+// test, just make it bigger.
+const size_t LOG_SIZE = 10;
+
+// This will remember which checks did run already.
+struct Log {
+ // The actual log cells, if i-th check did run
+ mutable bool run[LOG_SIZE];
+ Log() {
+ // Nothing run yet
+ for (size_t i(0); i < LOG_SIZE; ++ i) {
+ run[i] = false;
+ }
+ }
+ // Checks that the first amount of checks did run and the rest didn't.
+ void checkFirst(size_t amount) const {
+ ASSERT_LE(amount, LOG_SIZE) << "Wrong test: amount bigger than size "
+ "of log";
+ {
+ SCOPED_TRACE("Checking that the first amount of checks did run");
+ for (size_t i(0); i < amount; ++ i) {
+ EXPECT_TRUE(run[i]) << "Check #" << i << " did not run.";
+ }
+ }
+
+ {
+ SCOPED_TRACE("Checking that the rest did not run");
+ for (size_t i(amount); i < LOG_SIZE; ++ i) {
+ EXPECT_FALSE(run[i]) << "Check #" << i << "did run.";
+ }
+ }
+ }
+};
+
+// This returns true or false every time, no matter what is passed to it.
+// But it logs that it did run.
+class ConstCheck : public Check<Log> {
+public:
+ ConstCheck(bool accepts, size_t logNum) :
+ logNum_(logNum),
+ accepts_(accepts)
+ {
+ assert(logNum < LOG_SIZE); // If this fails, the LOG_SIZE is too small
+ }
+ virtual bool matches(const Log& log) const {
+ /*
+ * This is abuse of the context. It is designed to carry the
+ * information to check, not to modify it. However, this is the
+ * easiest way to do the test, so we go against the design.
+ */
+ log.run[logNum_] = true;
+ return (accepts_);
+ }
+private:
+ size_t logNum_;
+ bool accepts_;
+};
+
+}
+}
+}
+
+#endif
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/acl/tests/logic_check_test.cc b/src/lib/acl/tests/logic_check_test.cc
new file mode 100644
index 0000000..1c80277
--- /dev/null
+++ b/src/lib/acl/tests/logic_check_test.cc
@@ -0,0 +1,291 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "creators.h"
+#include <acl/logic_check.h>
+#include <typeinfo>
+#include <boost/shared_ptr.hpp> // for static_pointer_cast
+
+using namespace std;
+using namespace boost;
+using namespace isc::acl;
+using namespace isc::acl::tests;
+using isc::data::Element;
+
+namespace {
+
+// Test the defs in AnyOfSpec
+TEST(LogicOperators, AnyOfSpec) {
+ EXPECT_FALSE(AnyOfSpec::start());
+ EXPECT_FALSE(AnyOfSpec::terminate(false));
+ EXPECT_TRUE(AnyOfSpec::terminate(true));
+}
+
+// Test the defs in AllOfSpec
+TEST(LogicOperators, AllOfSpec) {
+ EXPECT_TRUE(AllOfSpec::start());
+ EXPECT_TRUE(AllOfSpec::terminate(false));
+ EXPECT_FALSE(AllOfSpec::terminate(true));
+}
+
+// Generic test of one check
+template<typename Mode>
+void
+testCheck(bool emptyResult) {
+ // It can be created
+ LogicOperator<Mode, Log> oper;
+ // It is empty by default
+ EXPECT_EQ(0, oper.getSubexpressions().size());
+ // And returns true, as all 0 of the subexpressions return true
+ Log log;
+ EXPECT_EQ(emptyResult, oper.matches(log));
+ log.checkFirst(0);
+ // Fill it with some subexpressions
+ typedef shared_ptr<ConstCheck> CheckPtr;
+ oper.addSubexpression(CheckPtr(new ConstCheck(emptyResult, 0)));
+ oper.addSubexpression(CheckPtr(new ConstCheck(emptyResult, 1)));
+ // Check what happens when only the default-valued are there
+ EXPECT_EQ(2, oper.getSubexpressions().size());
+ EXPECT_EQ(emptyResult, oper.matches(log));
+ log.checkFirst(2);
+ oper.addSubexpression(CheckPtr(new ConstCheck(!emptyResult, 2)));
+ oper.addSubexpression(CheckPtr(new ConstCheck(!emptyResult, 3)));
+ // They are listed there
+ EXPECT_EQ(4, oper.getSubexpressions().size());
+ // Now, the last one kills it, but the first ones will run, the fourth
+ // won't
+ EXPECT_EQ(!emptyResult, oper.matches(log));
+ log.checkFirst(3);
+}
+
+TEST(LogicOperators, AllOf) {
+ testCheck<AllOfSpec>(true);
+}
+
+TEST(LogicOperators, AnyOf) {
+ testCheck<AnyOfSpec>(false);
+}
+
+// Fixture for the tests of the creators
+class LogicCreatorTest : public ::testing::Test {
+private:
+ typedef shared_ptr<Loader<Log>::CheckCreator> CreatorPtr;
+public:
+ // Register some creators, both tested ones and some auxiliary ones for
+ // help
+ LogicCreatorTest():
+ loader_(REJECT)
+ {
+ loader_.registerCreator(CreatorPtr(new
+ LogicCreator<AnyOfSpec, Log>("ANY")));
+ loader_.registerCreator(CreatorPtr(new
+ LogicCreator<AllOfSpec, Log>("ALL")));
+ loader_.registerCreator(CreatorPtr(new ThrowCreator));
+ loader_.registerCreator(CreatorPtr(new LogCreator));
+ loader_.registerCreator(CreatorPtr(new NotCreator<Log>("NOT")));
+ }
+ // To mark which parts of the check did run
+ Log log_;
+ // The loader
+ Loader<Log> loader_;
+ // Some convenience shortcut names
+ typedef LogicOperator<AnyOfSpec, Log> AnyOf;
+ typedef LogicOperator<AllOfSpec, Log> AllOf;
+ typedef shared_ptr<AnyOf> AnyOfPtr;
+ typedef shared_ptr<AllOf> AllOfPtr;
+ // Loads the JSON as a check and tries to convert it to the given check
+ // subclass
+ template<typename Result> shared_ptr<Result> load(const string& JSON) {
+ shared_ptr<Check<Log> > result;
+ EXPECT_NO_THROW(result = loader_.loadCheck(Element::fromJSON(JSON)));
+ /*
+ * Optimally, we would use a dynamic_pointer_cast here to both
+ * convert the pointer and to check the type is correct. However,
+ * clang++ seems to be confused by templates and creates two typeids
+ * for the same templated type (even with the same parameters),
+ * therfore considering the types different, even if they are the same.
+ * This leads to false alarm in the test. Luckily, it generates the
+ * same name for both typeids, so we use them instead (which is enough
+ * to test the correct type of Check is returned). Then we can safely
+ * cast statically, as we don't use any kind of nasty things like
+ * multiple inheritance.
+ */
+ EXPECT_STREQ(typeid(Result).name(), typeid(*result.get()).name());
+ shared_ptr<Result>
+ resultConverted(static_pointer_cast<Result>(result));
+ EXPECT_NE(shared_ptr<Result>(), resultConverted);
+ return (resultConverted);
+ }
+};
+
+// Test it can load empty ones
+TEST_F(LogicCreatorTest, empty) {
+ AnyOfPtr emptyAny(load<AnyOf>("{\"ANY\": []}"));
+ EXPECT_EQ(0, emptyAny->getSubexpressions().size());
+ AllOfPtr emptyAll(load<AllOf>("{\"ALL\": []}"));
+ EXPECT_EQ(0, emptyAll->getSubexpressions().size());
+}
+
+// Test it rejects invalid inputs (not a list as a parameter)
+TEST_F(LogicCreatorTest, invalid) {
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ANY\": null}")),
+ LoaderError);
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ANY\": {}}")),
+ LoaderError);
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ANY\": true}")),
+ LoaderError);
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ANY\": 42}")),
+ LoaderError);
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ANY\": \"hello\"}")),
+ LoaderError);
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ALL\": null}")),
+ LoaderError);
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ALL\": {}}")),
+ LoaderError);
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ALL\": true}")),
+ LoaderError);
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ALL\": 42}")),
+ LoaderError);
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ALL\": \"hello\"}")),
+ LoaderError);
+}
+
+// Exceptions from subexpression creation isn't caught
+TEST_F(LogicCreatorTest, propagate) {
+ EXPECT_THROW(loader_.loadCheck(
+ Element::fromJSON("{\"ANY\": [{\"throw\": null}]}")),
+ TestCreatorError);
+ EXPECT_THROW(loader_.loadCheck(
+ Element::fromJSON("{\"ALL\": [{\"throw\": null}]}")),
+ TestCreatorError);
+}
+
+// We can create more complex ANY check and run it correctly
+TEST_F(LogicCreatorTest, anyRun) {
+ AnyOfPtr any(load<AnyOf>("{\"ANY\": ["
+ " {\"logcheck\": [0, false]},"
+ " {\"logcheck\": [1, true]},"
+ " {\"logcheck\": [2, true]}"
+ "]}"));
+ EXPECT_EQ(3, any->getSubexpressions().size());
+ EXPECT_TRUE(any->matches(log_));
+ log_.checkFirst(2);
+}
+
+// We can create more complex ALL check and run it correctly
+TEST_F(LogicCreatorTest, allRun) {
+ AllOfPtr any(load<AllOf>("{\"ALL\": ["
+ " {\"logcheck\": [0, true]},"
+ " {\"logcheck\": [1, false]},"
+ " {\"logcheck\": [2, false]}"
+ "]}"));
+ EXPECT_EQ(3, any->getSubexpressions().size());
+ EXPECT_FALSE(any->matches(log_));
+ log_.checkFirst(2);
+}
+
+// Or is able to return false
+TEST_F(LogicCreatorTest, anyFalse) {
+ AnyOfPtr any(load<AnyOf>("{\"ANY\": ["
+ " {\"logcheck\": [0, false]},"
+ " {\"logcheck\": [1, false]},"
+ " {\"logcheck\": [2, false]}"
+ "]}"));
+ EXPECT_EQ(3, any->getSubexpressions().size());
+ EXPECT_FALSE(any->matches(log_));
+ log_.checkFirst(3);
+}
+
+// And is able to return true
+TEST_F(LogicCreatorTest, andTrue) {
+ AllOfPtr all(load<AllOf>("{\"ALL\": ["
+ " {\"logcheck\": [0, true]},"
+ " {\"logcheck\": [1, true]},"
+ " {\"logcheck\": [2, true]}"
+ "]}"));
+ EXPECT_EQ(3, all->getSubexpressions().size());
+ EXPECT_TRUE(all->matches(log_));
+ log_.checkFirst(3);
+}
+
+// We can nest them together
+TEST_F(LogicCreatorTest, nested) {
+ AllOfPtr all(load<AllOf>("{\"ALL\": ["
+ " {\"ANY\": ["
+ " {\"logcheck\": [0, true]},"
+ " {\"logcheck\": [2, true]},"
+ " ]},"
+ " {\"logcheck\": [1, false]}"
+ "]}"));
+ EXPECT_EQ(2, all->getSubexpressions().size());
+ /*
+ * This has the same problem as load function above, and we use the
+ * same solution here.
+ */
+ ASSERT_STREQ(typeid(LogicOperator<AnyOfSpec, Log>).name(),
+ typeid(*all->getSubexpressions()[0]).name());
+ const LogicOperator<AnyOfSpec, Log>*
+ any(static_cast<const LogicOperator<AnyOfSpec, Log>*>
+ (all->getSubexpressions()[0]));
+ EXPECT_EQ(2, any->getSubexpressions().size());
+ EXPECT_FALSE(all->matches(log_));
+ log_.checkFirst(2);
+}
+
+void notTest(bool value) {
+ NotOperator<Log> notOp(shared_ptr<Check<Log> >(new ConstCheck(value, 0)));
+ Log log;
+ // It returns negated value
+ EXPECT_EQ(!value, notOp.matches(log));
+ // And runs the only one thing there
+ log.checkFirst(1);
+ // Check the getSubexpressions does sane things
+ ASSERT_EQ(1, notOp.getSubexpressions().size());
+ EXPECT_EQ(value, notOp.getSubexpressions()[0]->matches(log));
+}
+
+TEST(Not, trueValue) {
+ notTest(true);
+}
+
+TEST(Not, falseValue) {
+ notTest(false);
+}
+
+TEST_F(LogicCreatorTest, notInvalid) {
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": null}")),
+ LoaderError);
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": \"hello\"}")),
+ LoaderError);
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": true}")),
+ LoaderError);
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": 42}")),
+ LoaderError);
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": []}")),
+ LoaderError);
+ EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": [{"
+ "\"logcheck\": [0, true]"
+ "}]}")),
+ LoaderError);
+}
+
+TEST_F(LogicCreatorTest, notValid) {
+ shared_ptr<NotOperator<Log> > notOp(load<NotOperator<Log> >("{\"NOT\":"
+ " {\"logcheck\":"
+ " [0, true]}}"));
+ EXPECT_FALSE(notOp->matches(log_));
+ log_.checkFirst(1);
+}
+
+}
diff --git a/src/lib/acl/tests/run_unittests.cc b/src/lib/acl/tests/run_unittests.cc
new file mode 100644
index 0000000..8dc59a2
--- /dev/null
+++ b/src/lib/acl/tests/run_unittests.cc
@@ -0,0 +1,24 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+#include <log/logger_support.h>
+#include <util/unittests/run_all.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ isc::log::initLogger();
+ return (isc::util::unittests::run_all());
+}
diff --git a/src/lib/acl/tests/sockaddr.h b/src/lib/acl/tests/sockaddr.h
new file mode 100644
index 0000000..bd30451
--- /dev/null
+++ b/src/lib/acl/tests/sockaddr.h
@@ -0,0 +1,69 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __ACL_TEST_SOCKADDR_H
+#define __ACL_TEST_SOCKADDR_H 1
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <string.h>
+
+#include <exceptions/exceptions.h>
+
+namespace isc {
+namespace acl {
+namespace tests {
+
+// This is a helper function that returns a sockaddr for the given textual
+// IP address. Note that "inline" is crucial because this function is defined
+// in a header file included in multiple .cc files. Without inline it would
+// produce an external linkage and cause troubles at link time.
+//
+// Note that this function uses a static storage for the return value.
+// So if it's called more than once in a singe context (e.g., in the same
+// EXPECT_xx()), it's unlikely to work as expected.
+inline const struct sockaddr&
+getSockAddr(const char* const addr) {
+ struct addrinfo hints, *res;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST;
+
+ if (getaddrinfo(addr, NULL, &hints, &res) == 0) {
+ static struct sockaddr_storage ss;
+ void* ss_ptr = &ss;
+ memcpy(ss_ptr, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ return (*static_cast<struct sockaddr*>(ss_ptr));
+ }
+
+ // We don't expect getaddrinfo to fail for our tests. But if that
+ // ever happens we throw an exception to make sure the corresponding test
+ // fail (either due to a failure of *_NO_THROW or the uncaught exception).
+ isc_throw(Unexpected,
+ "failed to convert textual IP address to sockaddr for " <<
+ addr);
+}
+
+} // end of namespace "tests"
+} // end of namespace "acl"
+} // end of namespace "isc"
+
+#endif // __ACL_TEST_SOCKADDR_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/asiodns/Makefile.am b/src/lib/asiodns/Makefile.am
new file mode 100644
index 0000000..2d246ef
--- /dev/null
+++ b/src/lib/asiodns/Makefile.am
@@ -0,0 +1,41 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink -I$(top_builddir)/src/lib/asiolink
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+CLEANFILES = *.gcno *.gcda asiodns_messages.h asiodns_messages.cc
+
+# Define rule to build logging source files from message file
+asiodns_messages.h asiodns_messages.cc: asiodns_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/asiodns/asiodns_messages.mes
+
+BUILT_SOURCES = asiodns_messages.h asiodns_messages.cc
+
+lib_LTLIBRARIES = libasiodns.la
+libasiodns_la_SOURCES = dns_answer.h
+libasiodns_la_SOURCES += asiodns.h
+libasiodns_la_SOURCES += dns_lookup.h
+libasiodns_la_SOURCES += dns_server.h
+libasiodns_la_SOURCES += dns_service.cc dns_service.h
+libasiodns_la_SOURCES += tcp_server.cc tcp_server.h
+libasiodns_la_SOURCES += udp_server.cc udp_server.h
+libasiodns_la_SOURCES += io_fetch.cc io_fetch.h
+
+nodist_libasiodns_la_SOURCES = asiodns_messages.cc asiodns_messages.h
+
+EXTRA_DIST = asiodns_messages.mes
+
+# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
+# B10_CXXFLAGS)
+libasiodns_la_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_CLANGPP
+# Same for clang++, but we need to turn off -Werror completely.
+libasiodns_la_CXXFLAGS += -Wno-error
+endif
+libasiodns_la_CPPFLAGS = $(AM_CPPFLAGS)
+libasiodns_la_LIBADD = $(top_builddir)/src/lib/log/liblog.la
diff --git a/src/lib/asiodns/README b/src/lib/asiodns/README
new file mode 100644
index 0000000..596d1df
--- /dev/null
+++ b/src/lib/asiodns/README
@@ -0,0 +1,157 @@
+The asiodns library is intended to provide an abstraction layer between
+BIND10 modules and asiolink library.
+
+These DNS server and client routines are written using the "stackless
+coroutine" pattern invented by Chris Kohlhoff and described at
+http://blog.think-async.com/2010/03/potted-guide-to-stackless-coroutines.html.
+This is intended to simplify development a bit, since it allows the
+routines to be written in a straightfowrard step-step-step fashion rather
+than as a complex chain of separate handler functions.
+
+Coroutine objects (i.e., UDPServer, TCPServer and IOFetch) are objects
+with reenterable operator() members. When an instance of one of these
+classes is called as a function, it resumes at the position where it left
+off. Thus, a UDPServer can issue an asynchronous I/O call and specify
+itself as the handler object; when the call completes, the UDPServer
+carries on at the same position. As a result, the code can look as
+if it were using synchronous, not asynchronous, I/O, providing some of
+the benefit of threading but with minimal switching overhead.
+
+So, in simplified form, the behavior of a DNS Server is:
+
+ REENTER:
+ while true:
+ YIELD packet = read_packet
+ FORK
+ if not parent:
+ break
+
+ # This callback informs the caller that a packet has arrived, and
+ # gives it a chance to update configuration, etc
+ SimpleCallback(packet)
+ YIELD answer = DNSLookup(packet, this)
+ response = DNSAnswer(answer)
+ YIELD send(response)
+
+At each "YIELD" point, the coroutine initiates an asynchronous operation,
+then pauses and turns over control to some other task on the ASIO service
+queue. When the operation completes, the coroutine resumes.
+
+DNSLookup, DNSAnswer and SimpleCallback define callback methods
+used by a DNS Server to communicate with the module that called it.
+They are abstract-only classes whose concrete implementations
+are supplied by the calling module.
+
+The DNSLookup callback always runs asynchronously. Concrete
+implementations must be sure to call the server's "resume" method when
+it is finished.
+
+In an authoritative server, the DNSLookup implementation would examine
+the query, look up the answer, then call "resume". (See the diagram
+in doc/auth_process.jpg.)
+
+In a recursive server, the DNSLookup impelemtation would initiate a
+DNSQuery, which in turn would be responsible for calling the server's
+"resume" method. (See the diagram in doc/recursive_process.jpg.)
+
+A DNSQuery object is intended to handle resolution of a query over
+the network when the local authoritative data sources or cache are not
+sufficient. The plan is that it will make use of subsidiary DNSFetch
+calls to get data from particular authoritative servers, and when it has
+gotten a complete answer, it calls "resume".
+
+In current form, however, DNSQuery is much simpler; it forwards queries
+to a single upstream resolver and passes the answers back to the client.
+It is constructed with the address of the forward server. Queries are
+initiated with the question to ask the forward server, a buffer into
+which to write the answer, and a pointer to the coroutine to be resumed
+when the answer has arrived. In simplified form, the DNSQuery routine is:
+
+ REENTER:
+ render the question into a wire-format query packet
+ YIELD send(query)
+ YIELD response = read_packet
+ server->resume
+
+Currently, DNSQuery is only implemented for UDP queries. In future work
+it will be necessary to write code to fall back to TCP when circumstances
+require it.
+
+
+Upstream Fetches
+================
+Upstream fetches (queries by the resolver on behalf of a client) are made
+using a slightly-modified version of the pattern described above.
+
+Sockets
+-------
+First, it will be useful to understand the class hierarchy used in the
+fetch logic:
+
+ IOSocket
+ |
+ IOAsioSocket
+ |
+ +-----+-----+
+ | |
+UDPSocket TCPSocket
+
+IOSocket is a wrapper class for a socket and is used by the authoritative
+server code. It is an abstract base class, providing little more that the ability to hold the socket and to return the protocol in use.
+
+Built on this is IOAsioSocket, which adds the open, close, asyncSend and
+asyncReceive methods. This is a template class, which takes as template
+argument the class of the object that will be used as the callback when the
+asynchronous operation completes. This object can be of any type, but must
+include an operator() method with the signature:
+
+ operator()(asio::error_code ec, size_t length)
+
+... the two arguments being the status of the completed I/O operation and
+the number of bytes transferred. (In the case of the open method, the second
+argument will be zero.)
+
+Finally, the TCPSocket and UDPSocket classes provide the body of the
+asynchronous operations.
+
+Fetch Sequence
+--------------
+The fetch is implemented by the IOFetch class, which takes as argument the
+protocol to use. The sequence is:
+
+ REENTER:
+ render the question into a wire-format query packet
+ open() // Open socket and optionally connect
+ if (! synchronous) {
+ YIELD;
+ }
+ YIELD asyncSend(query) // Send query
+ do {
+ YIELD asyncReceive(response) // Read response
+ } while (! complete(response))
+ close() // Drop connection and close socket
+ server->resume
+
+The open() method opens a socket for use. On TCP, it also makes a
+connection to the remote end. So under UDP the operation will complete
+immediately, but under TCP it could take a long time. One solution would be
+for the open operation to post an event to the I/O queue; then both cases
+could be regarded as being equivalent, with the completion being signalled
+by the posting of the completion event. However UDP is the most common case
+and that would involve extra overhead. So the open() returns a status
+indicating whether the operation completed asynchronously. If it did, the
+code yields back to the coroutine; if not the yield is bypassed.
+
+The asynchronous send is straightforward, invoking the underlying ASIO
+function. (Note that the address/port is supplied to both the open() and
+asyncSend() methods - it is used by the TCPSocket in open() and by the
+UDPSocket in asyncSend().)
+
+The asyncReceive() method issues an asynchronous read and waits for completion.
+The fetch object keeps track of the amount of data received so far and when
+the receive completes it calls a method on the socket to determine if the
+entire message has been received. (This will always be the case for UDP. On
+TCP though, the message is preceded by a count field as several reads may be
+required to read all the data.) The fetch loops until all the data is read.
+
+Finally, the socket is closed and the server called to resume operation.
diff --git a/src/lib/asiodns/asiodns.h b/src/lib/asiodns/asiodns.h
new file mode 100644
index 0000000..8791a72
--- /dev/null
+++ b/src/lib/asiodns/asiodns.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __ASIODNS_H
+#define __ASIODNS_H 1
+
+#include <asiodns/dns_service.h>
+#include <asiodns/dns_server.h>
+#include <asiodns/dns_lookup.h>
+#include <asiodns/dns_answer.h>
+
+#endif // __ASIODNS_H
diff --git a/src/lib/asiodns/asiodns_messages.mes b/src/lib/asiodns/asiodns_messages.mes
new file mode 100644
index 0000000..3e11ede
--- /dev/null
+++ b/src/lib/asiodns/asiodns_messages.mes
@@ -0,0 +1,56 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+$NAMESPACE isc::asiodns
+
+% ASIODNS_FETCH_COMPLETED upstream fetch to %1(%2) has now completed
+A debug message, this records that the upstream fetch (a query made by the
+resolver on behalf of its client) to the specified address has completed.
+
+% ASIODNS_FETCH_STOPPED upstream fetch to %1(%2) has been stopped
+An external component has requested the halting of an upstream fetch. This
+is an allowed operation, and the message should only appear if debug is
+enabled.
+
+% ASIODNS_OPEN_SOCKET error %1 opening %2 socket to %3(%4)
+The asynchronous I/O code encountered an error when trying to open a socket
+of the specified protocol in order to send a message to the target address.
+The number of the system error that cause the problem is given in the
+message.
+
+% ASIODNS_READ_DATA error %1 reading %2 data from %3(%4)
+The asynchronous I/O code encountered an error when trying to read data from
+the specified address on the given protocol. The number of the system
+error that cause the problem is given in the message.
+
+% ASIODNS_READ_TIMEOUT receive timeout while waiting for data from %1(%2)
+An upstream fetch from the specified address timed out. This may happen for
+any number of reasons and is most probably a problem at the remote server
+or a problem on the network. The message will only appear if debug is
+enabled.
+
+% ASIODNS_SEND_DATA error %1 sending data using %2 to %3(%4)
+The asynchronous I/O code encountered an error when trying send data to
+the specified address on the given protocol. The the number of the system
+error that cause the problem is given in the message.
+
+% ASIODNS_UNKNOWN_ORIGIN unknown origin for ASIO error code %1 (protocol: %2, address %3)
+An internal consistency check on the origin of a message from the
+asynchronous I/O module failed. This may indicate an internal error;
+please submit a bug report.
+
+% ASIODNS_UNKNOWN_RESULT unknown result (%1) when IOFetch::stop() was executed for I/O to %2(%3)
+An internal error indicating that the termination method of the resolver's
+upstream fetch class was called with an unknown result code (which is
+given in the message). Please submit a bug report.
diff --git a/src/lib/asiodns/dns_answer.h b/src/lib/asiodns/dns_answer.h
new file mode 100644
index 0000000..3654369
--- /dev/null
+++ b/src/lib/asiodns/dns_answer.h
@@ -0,0 +1,77 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __ASIOLINK_DNS_ANSWER_H
+#define __ASIOLINK_DNS_ANSWER_H 1
+
+#include <asiolink/io_message.h>
+#include <util/buffer.h>
+#include <dns/message.h>
+
+namespace isc {
+namespace asiodns {
+
+/// \brief The \c DNSAnswer class is an abstract base class for a DNS
+/// Answer provider function.
+///
+/// Specific derived class implementations are hidden within the
+/// implementation. Instances of the derived classes can be called
+/// as functions via the operator() interface. Pointers to these
+/// instances can then be provided to the \c IOService class
+/// via its constructor.
+///
+/// A DNS Answer provider function takes answer data that has been obtained
+/// from a DNS Lookup provider functon and readies it to be sent to the
+/// client. After it has run, the OutputBuffer object passed to it should
+/// contain the answer to the query rendered into wire format.
+class DNSAnswer {
+ ///
+ /// \name Constructors and Destructor
+ ///
+ /// Note: The copy constructor and the assignment operator are
+ /// intentionally defined as private, making this class non-copyable.
+ //@{
+private:
+ DNSAnswer(const DNSAnswer& source);
+ DNSAnswer& operator=(const DNSAnswer& source);
+protected:
+ /// \brief The default constructor.
+ ///
+ /// This is intentionally defined as \c protected as this base class
+ /// should never be instantiated (except as part of a derived class).
+ DNSAnswer() {}
+public:
+ /// \brief The destructor
+ virtual ~DNSAnswer() {}
+ //@}
+ /// \brief The function operator
+ ///
+ /// This makes its call indirectly via the "self" pointer, ensuring
+ /// that the function ultimately invoked will be the one in the derived
+ /// class.
+ ///
+ /// \param io_message The event message to handle
+ /// \param query_message The DNS MessagePtr of the original query
+ /// \param answer_message The DNS MessagePtr of the answer we are
+ /// building
+ /// \param buffer Intermediate data results are put here
+ virtual void operator()(const asiolink::IOMessage& io_message,
+ isc::dns::MessagePtr query_message,
+ isc::dns::MessagePtr answer_message,
+ isc::util::OutputBufferPtr buffer) const = 0;
+};
+
+} // namespace asiodns
+} // namespace isc
+#endif // __ASIOLINK_DNS_ANSWER_H
diff --git a/src/lib/asiodns/dns_lookup.h b/src/lib/asiodns/dns_lookup.h
new file mode 100644
index 0000000..40290e4
--- /dev/null
+++ b/src/lib/asiodns/dns_lookup.h
@@ -0,0 +1,85 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __ASIOLINK_DNS_LOOKUP_H
+#define __ASIOLINK_DNS_LOOKUP_H 1
+
+#include <asiolink/io_message.h>
+#include <asiodns/dns_server.h>
+#include <dns/message.h>
+#include <util/buffer.h>
+
+namespace isc {
+namespace asiodns {
+
+/// \brief The \c DNSLookup class is an abstract base class for a DNS
+/// Lookup provider function.
+///
+/// Specific derived class implementations are hidden within the
+/// implementation. Instances of the derived classes can be called
+/// as functions via the operator() interface. Pointers to these
+/// instances can then be provided to the \c IOService class
+/// via its constructor.
+///
+/// A DNS Lookup provider function obtains the data needed to answer
+/// a DNS query (e.g., from authoritative data source, cache, or upstream
+/// query). After it has run, the OutputBuffer object passed to it
+/// should contain the answer to the query, in an internal representation.
+class DNSLookup {
+ ///
+ /// \name Constructors and Destructor
+ ///
+ /// Note: The copy constructor and the assignment operator are
+ /// intentionally defined as private, making this class non-copyable.
+ //@{
+private:
+ DNSLookup(const DNSLookup& source);
+ DNSLookup& operator=(const DNSLookup& source);
+protected:
+ /// \brief The default constructor.
+ ///
+ /// This is intentionally defined as \c protected as this base class
+ /// should never be instantiated (except as part of a derived class).
+ DNSLookup() : self_(this) {}
+public:
+ /// \brief The destructor
+ virtual ~DNSLookup() {}
+ //@}
+ /// \brief The function operator
+ ///
+ /// This makes its call indirectly via the "self" pointer, ensuring
+ /// that the function ultimately invoked will be the one in the derived
+ /// class.
+ ///
+ /// \param io_message The event message to handle
+ /// \param message The DNS MessagePtr that needs handling
+ /// \param answer_message The final answer will be constructed in
+ /// this MessagePtr
+ /// \param buffer The final answer is put here
+ /// \param server DNSServer object to use
+ virtual void operator()(const asiolink::IOMessage& io_message,
+ isc::dns::MessagePtr message,
+ isc::dns::MessagePtr answer_message,
+ isc::util::OutputBufferPtr buffer,
+ DNSServer* server) const
+ {
+ (*self_)(io_message, message, answer_message, buffer, server);
+ }
+private:
+ DNSLookup* self_;
+};
+
+} // namespace asiodns
+} // namespace isc
+#endif // __ASIOLINK_DNS_LOOKUP_H
diff --git a/src/lib/asiodns/dns_server.h b/src/lib/asiodns/dns_server.h
new file mode 100644
index 0000000..f235860
--- /dev/null
+++ b/src/lib/asiodns/dns_server.h
@@ -0,0 +1,157 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __ASIOLINK_DNS_SERVER_H
+#define __ASIOLINK_DNS_SERVER_H 1
+
+#include <asiolink/io_message.h>
+
+namespace isc {
+namespace asiodns {
+
+/// \brief The \c DNSServer class is a wrapper (and base class) for
+/// classes which provide DNS server functionality.
+///
+/// The classes derived from this one, \c TCPServer and \c UDPServer,
+/// act as the interface layer between clients sending queries, and
+/// functions defined elsewhere that provide answers to those queries.
+/// Those functions are described in more detail below under
+/// \c SimpleCallback, \c DNSLookup, and \c DNSAnswer.
+///
+/// Notes to developers:
+/// When constructed, this class (and its derived classes) will have its
+/// "self_" member set to point to "this". Objects of this class (as
+/// instantiated through a base class) are sometimes passed by
+/// reference (as this superclass); calls to methods in the base
+/// class are then rerouted via this pointer to methods in the derived
+/// class. This allows code from outside asiodns, with no specific
+/// knowledge of \c TCPServer or \c UDPServer, to access their methods.
+///
+/// This class is both assignable and copy-constructable. Its subclasses
+/// use the "stackless coroutine" pattern, meaning that it will copy itself
+/// when "forking", and that instances will be posted as ASIO handler
+/// objects, which are always copied.
+///
+/// Because these objects are frequently copied, it is recommended
+/// that derived classes be kept small to reduce copy overhead.
+class DNSServer {
+protected:
+ ///
+ /// \name Constructors and destructors
+ ///
+ /// This is intentionally defined as \c protected, as this base class
+ /// should never be instantiated except as part of a derived class.
+ //@{
+ DNSServer() : self_(this) {}
+public:
+ /// \brief The destructor
+ virtual ~DNSServer() {}
+ //@}
+
+ ///
+ /// \name Class methods
+ ///
+ /// These methods all make their calls indirectly via the "self_"
+ /// pointer, ensuring that the functions ultimately invoked will be
+ /// the ones in the derived class. This makes it possible to pass
+ /// instances of derived classes as references to this base class
+ /// without losing access to derived class data.
+ ///
+ //@{
+ /// \brief The funtion operator
+ virtual void operator()(asio::error_code ec = asio::error_code(),
+ size_t length = 0)
+ {
+ (*self_)(ec, length);
+ }
+
+ /// \brief Stop current running server
+ virtual void stop() { self_->stop();}
+
+ /// \brief Resume processing of the server coroutine after an
+ /// asynchronous call (e.g., to the DNS Lookup provider) has completed.
+ ///
+ /// \param done If true, this signals the system there is an answer
+ /// to return.
+ virtual void resume(const bool done) { self_->resume(done); }
+
+ /// \brief Indicate whether the server is able to send an answer
+ /// to a query.
+ ///
+ /// This is presently used only for testing purposes.
+ virtual bool hasAnswer() { return (self_->hasAnswer()); }
+
+ /// \brief Returns the current value of the 'coroutine' object
+ ///
+ /// This is a temporary method, intended to be used for debugging
+ /// purposes during development and removed later. It allows
+ /// callers from outside the coroutine object to retrieve information
+ /// about its current state.
+ ///
+ /// \return The value of the 'coroutine' object
+ virtual int value() { return (self_->value()); }
+
+ /// \brief Returns a pointer to a clone of this DNSServer object.
+ ///
+ /// When a \c DNSServer object is copied or assigned, the result will
+ /// normally be another \c DNSServer object containing a copy
+ /// of the original "self_" pointer. Calling clone() guarantees
+ /// that the underlying object is also correctly copied.
+ ///
+ /// \return A deep copy of this DNSServer object
+ virtual DNSServer* clone() { return (self_->clone()); }
+ //@}
+
+protected:
+ /// \brief Lookup handler object.
+ ///
+ /// This is a protected class; it can only be instantiated
+ /// from within a derived class of \c DNSServer.
+ ///
+ /// A server object that has received a query creates an instance
+ /// of this class and scheudles it on the ASIO service queue
+ /// using asio::io_service::post(). When the handler executes, it
+ /// calls the asyncLookup() method in the server object to start a
+ /// DNS lookup. When the lookup is complete, the server object is
+ /// scheduled to resume, again using io_service::post().
+ ///
+ /// Note that the calling object is copied into the handler object,
+ /// not referenced. This is because, once the calling object yields
+ /// control to the handler, it falls out of scope and may disappear
+ template <typename T>
+ class AsyncLookup {
+ public:
+ AsyncLookup(T& caller) : caller_(caller) {}
+ void operator()() { caller_.asyncLookup(); }
+ private:
+ T caller_;
+ };
+
+ /// \brief Carries out a DNS lookup.
+ ///
+ /// This function calls the \c DNSLookup object specified by the
+ /// DNS server when the \c IOService was created, passing along
+ /// the details of the query and a pointer back to the current
+ /// server object. It is called asynchronously via the AsyncLookup
+ /// handler class.
+ virtual void asyncLookup() { self_->asyncLookup(); }
+
+private:
+ DNSServer* self_;
+};
+
+
+} // namespace asiodns
+} // namespace isc
+#endif // __ASIOLINK_DNS_SERVER_H
diff --git a/src/lib/asiodns/dns_service.cc b/src/lib/asiodns/dns_service.cc
new file mode 100644
index 0000000..94510fe
--- /dev/null
+++ b/src/lib/asiodns/dns_service.cc
@@ -0,0 +1,201 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h> // for some IPC/network system calls
+
+#include <boost/lexical_cast.hpp>
+
+#include <config.h>
+
+#include <log/dummylog.h>
+
+#include <asio.hpp>
+#include <dns_service.h>
+#include <asiolink/io_service.h>
+#include <asiolink/io_service.h>
+#include <tcp_server.h>
+#include <udp_server.h>
+
+#include <log/dummylog.h>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/foreach.hpp>
+
+using isc::log::dlog;
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace asiodns {
+
+class DNSLookup;
+class DNSAnswer;
+
+namespace {
+
+asio::ip::address
+convertAddr(const std::string& address) {
+ asio::error_code err;
+ asio::ip::address addr = asio::ip::address::from_string(address, err);
+ if (err) {
+ isc_throw(IOError, "Invalid IP address '" << &address << "': "
+ << err.message());
+ }
+ return (addr);
+}
+
+}
+
+
+class DNSServiceImpl {
+public:
+ DNSServiceImpl(IOService& io_service, const char& port,
+ const asio::ip::address* v4addr,
+ const asio::ip::address* v6addr,
+ SimpleCallback* checkin, DNSLookup* lookup,
+ DNSAnswer* answer);
+
+ IOService& io_service_;
+
+ typedef boost::shared_ptr<UDPServer> UDPServerPtr;
+ typedef boost::shared_ptr<TCPServer> TCPServerPtr;
+ typedef boost::shared_ptr<DNSServer> DNSServerPtr;
+ std::vector<DNSServerPtr> servers_;
+ SimpleCallback *checkin_;
+ DNSLookup *lookup_;
+ DNSAnswer *answer_;
+
+ void addServer(uint16_t port, const asio::ip::address& address) {
+ try {
+ dlog(std::string("Initialize TCP server at ") + address.to_string() + ":" + boost::lexical_cast<std::string>(port));
+ TCPServerPtr tcpServer(new TCPServer(io_service_.get_io_service(),
+ address, port, checkin_, lookup_, answer_));
+ (*tcpServer)();
+ servers_.push_back(tcpServer);
+ dlog(std::string("Initialize UDP server at ") + address.to_string() + ":" + boost::lexical_cast<std::string>(port));
+ UDPServerPtr udpServer(new UDPServer(io_service_.get_io_service(),
+ address, port, checkin_, lookup_, answer_));
+ (*udpServer)();
+ servers_.push_back(udpServer);
+ }
+ catch (const asio::system_error& err) {
+ // We need to catch and convert any ASIO level exceptions.
+ // This can happen for unavailable address, binding a privilege port
+ // without the privilege, etc.
+ isc_throw(IOError, "Failed to initialize network servers: " <<
+ err.what());
+ }
+ }
+ void addServer(const char& port, const asio::ip::address& address) {
+ uint16_t portnum;
+ try {
+ // XXX: SunStudio with stlport4 doesn't reject some invalid
+ // representation such as "-1" by lexical_cast<uint16_t>, so
+ // we convert it into a signed integer of a larger size and perform
+ // range check ourselves.
+ const int32_t portnum32 = boost::lexical_cast<int32_t>(&port);
+ if (portnum32 < 0 || portnum32 > 65535) {
+ isc_throw(IOError, "Invalid port number '" << &port);
+ }
+ portnum = portnum32;
+ } catch (const boost::bad_lexical_cast& ex) {
+ isc_throw(IOError, "Invalid port number '" << &port << "': " <<
+ ex.what());
+ }
+ addServer(portnum, address);
+ }
+};
+
+DNSServiceImpl::DNSServiceImpl(IOService& io_service,
+ const char& port,
+ const asio::ip::address* const v4addr,
+ const asio::ip::address* const v6addr,
+ SimpleCallback* checkin,
+ DNSLookup* lookup,
+ DNSAnswer* answer) :
+ io_service_(io_service),
+ checkin_(checkin),
+ lookup_(lookup),
+ answer_(answer)
+{
+
+ if (v4addr) {
+ addServer(port, *v4addr);
+ }
+ if (v6addr) {
+ addServer(port, *v6addr);
+ }
+}
+
+DNSService::DNSService(IOService& io_service,
+ const char& port, const char& address,
+ SimpleCallback* checkin,
+ DNSLookup* lookup,
+ DNSAnswer* answer) :
+ impl_(new DNSServiceImpl(io_service, port, NULL, NULL, checkin, lookup,
+ answer)), io_service_(io_service)
+{
+ addServer(port, &address);
+}
+
+DNSService::DNSService(IOService& io_service,
+ const char& port,
+ const bool use_ipv4, const bool use_ipv6,
+ SimpleCallback* checkin,
+ DNSLookup* lookup,
+ DNSAnswer* answer) :
+ impl_(NULL), io_service_(io_service)
+{
+ const asio::ip::address v4addr_any =
+ asio::ip::address(asio::ip::address_v4::any());
+ const asio::ip::address* const v4addrp = use_ipv4 ? &v4addr_any : NULL;
+ const asio::ip::address v6addr_any =
+ asio::ip::address(asio::ip::address_v6::any());
+ const asio::ip::address* const v6addrp = use_ipv6 ? &v6addr_any : NULL;
+ impl_ = new DNSServiceImpl(io_service, port, v4addrp, v6addrp, checkin, lookup, answer);
+}
+
+DNSService::DNSService(IOService& io_service, SimpleCallback* checkin,
+ DNSLookup* lookup, DNSAnswer *answer) :
+ impl_(new DNSServiceImpl(io_service, *"0", NULL, NULL, checkin, lookup,
+ answer)), io_service_(io_service)
+{
+}
+
+DNSService::~DNSService() {
+ delete impl_;
+}
+
+void
+DNSService::addServer(const char& port, const std::string& address) {
+ impl_->addServer(port, convertAddr(address));
+}
+
+void
+DNSService::addServer(uint16_t port, const std::string& address) {
+ impl_->addServer(port, convertAddr(address));
+}
+
+void
+DNSService::clearServers() {
+ BOOST_FOREACH(const DNSServiceImpl::DNSServerPtr& s, impl_->servers_) {
+ s->stop();
+ }
+ impl_->servers_.clear();
+}
+
+} // namespace asiodns
+} // namespace isc
diff --git a/src/lib/asiodns/dns_service.h b/src/lib/asiodns/dns_service.h
new file mode 100644
index 0000000..6b6a6c0
--- /dev/null
+++ b/src/lib/asiodns/dns_service.h
@@ -0,0 +1,114 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __ASIOLINK_DNS_SERVICE_H
+#define __ASIOLINK_DNS_SERVICE_H 1
+
+#include <resolve/resolver_interface.h>
+
+#include <asiolink/io_service.h>
+#include <asiolink/simple_callback.h>
+
+namespace isc {
+namespace asiodns {
+
+class DNSLookup;
+class DNSAnswer;
+class DNSServiceImpl;
+
+/// \brief Handle DNS Queries
+///
+/// DNSService is the service that handles DNS queries and answers with
+/// a given IOService. This class is mainly intended to hold all the
+/// logic that is shared between the authoritative and the recursive
+/// server implementations. As such, it handles asio, including config
+/// updates (through the 'Checkinprovider'), and listening sockets.
+class DNSService {
+ ///
+ /// \name Constructors and Destructor
+ ///
+ /// Note: The copy constructor and the assignment operator are
+ /// intentionally defined as private, making this class non-copyable.
+ //@{
+private:
+ DNSService(const DNSService& source);
+ DNSService& operator=(const DNSService& source);
+
+public:
+ /// \brief The constructor with a specific IP address and port on which
+ /// the services listen on.
+ ///
+ /// \param io_service The IOService to work with
+ /// \param port the port to listen on
+ /// \param address the IP address to listen on
+ /// \param checkin Provider for cc-channel events (see \c SimpleCallback)
+ /// \param lookup The lookup provider (see \c DNSLookup)
+ /// \param answer The answer provider (see \c DNSAnswer)
+ DNSService(asiolink::IOService& io_service, const char& port,
+ const char& address, isc::asiolink::SimpleCallback* checkin,
+ DNSLookup* lookup, DNSAnswer* answer);
+ /// \brief The constructor with a specific port on which the services
+ /// listen on.
+ ///
+ /// It effectively listens on "any" IPv4 and/or IPv6 addresses.
+ /// IPv4/IPv6 services will be available if and only if \c use_ipv4
+ /// or \c use_ipv6 is \c true, respectively.
+ ///
+ /// \param io_service The IOService to work with
+ /// \param port the port to listen on
+ /// \param use_ipv4 If true, listen on ipv4 'any'
+ /// \param use_ipv6 If true, listen on ipv6 'any'
+ /// \param checkin Provider for cc-channel events (see \c SimpleCallback)
+ /// \param lookup The lookup provider (see \c DNSLookup)
+ /// \param answer The answer provider (see \c DNSAnswer)
+ DNSService(asiolink::IOService& io_service, const char& port,
+ const bool use_ipv4, const bool use_ipv6,
+ isc::asiolink::SimpleCallback* checkin, DNSLookup* lookup,
+ DNSAnswer* answer);
+ /// \brief The constructor without any servers.
+ ///
+ /// Use addServer() to add some servers.
+ DNSService(asiolink::IOService& io_service, isc::asiolink::SimpleCallback* checkin,
+ DNSLookup* lookup, DNSAnswer* answer);
+ /// \brief The destructor.
+ ~DNSService();
+ //@}
+
+ /// \brief Add another server to the service
+ void addServer(uint16_t port, const std::string &address);
+ void addServer(const char &port, const std::string &address);
+ /// \brief Remove all servers from the service
+ void clearServers();
+
+ /// \brief Return the native \c io_service object used in this wrapper.
+ ///
+ /// This is a short term work around to support other BIND 10 modules
+ /// that share the same \c io_service with the authoritative server.
+ /// It will eventually be removed once the wrapper interface is
+ /// generalized.
+ asio::io_service& get_io_service() { return io_service_.get_io_service(); }
+
+ /// \brief Return the IO Service Object
+ ///
+ /// \return IOService object for this DNS service.
+ asiolink::IOService& getIOService() { return (io_service_);}
+
+private:
+ DNSServiceImpl* impl_;
+ asiolink::IOService& io_service_;
+};
+
+} // namespace asiodns
+} // namespace isc
+#endif // __ASIOLINK_DNS_SERVICE_H
diff --git a/src/lib/asiodns/io_fetch.cc b/src/lib/asiodns/io_fetch.cc
new file mode 100644
index 0000000..31b5f50
--- /dev/null
+++ b/src/lib/asiodns/io_fetch.cc
@@ -0,0 +1,429 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <netinet/in.h>
+#include <stdint.h>
+#include <sys/socket.h>
+#include <unistd.h> // for some IPC/network system calls
+
+#include <boost/bind.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+#include <asio.hpp>
+#include <asio/deadline_timer.hpp>
+
+#include <asiolink/io_address.h>
+#include <asiolink/io_asio_socket.h>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_service.h>
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/tcp_socket.h>
+#include <asiolink/udp_endpoint.h>
+#include <asiolink/udp_socket.h>
+
+#include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+#include <log/logger.h>
+#include <log/macros.h>
+
+#include <asiodns/asiodns_messages.h>
+#include <asiodns/io_fetch.h>
+
+#include <util/buffer.h>
+#include <util/random/qid_gen.h>
+
+
+using namespace asio;
+using namespace isc::asiolink;
+using namespace isc::dns;
+using namespace isc::util;
+using namespace isc::util::random;
+using namespace isc::log;
+using namespace std;
+
+namespace isc {
+namespace asiodns {
+
+/// Use the ASIO logger
+
+namespace {
+
+isc::log::Logger logger("asiolink");
+// Log debug verbosity
+enum {
+ DBG_IMPORTANT = 1,
+ DBG_COMMON = 20,
+ DBG_ALL = 50
+};
+
+}
+
+/// \brief IOFetch Data
+///
+/// The data for IOFetch is held in a separate struct pointed to by a shared_ptr
+/// object. This is because the IOFetch object will be copied often (it is used
+/// as a coroutine and passed as callback to many async_*() functions) and we
+/// want keep the same data). Organising the data in this way keeps copying to
+/// a minimum.
+struct IOFetchData {
+
+ // The first two members are shared pointers to a base class because what is
+ // actually instantiated depends on whether the fetch is over UDP or TCP,
+ // which is not known until construction of the IOFetch. Use of a shared
+ // pointer here is merely to ensure deletion when the data object is deleted.
+ boost::scoped_ptr<IOAsioSocket<IOFetch> > socket;
+ ///< Socket to use for I/O
+ boost::scoped_ptr<IOEndpoint> remote_snd;///< Where the fetch is sent
+ boost::scoped_ptr<IOEndpoint> remote_rcv;///< Where the response came from
+ OutputBufferPtr msgbuf; ///< Wire buffer for question
+ OutputBufferPtr received; ///< Received data put here
+ IOFetch::Callback* callback; ///< Called on I/O Completion
+ asio::deadline_timer timer; ///< Timer to measure timeouts
+ IOFetch::Protocol protocol; ///< Protocol being used
+ size_t cumulative; ///< Cumulative received amount
+ size_t expected; ///< Expected amount of data
+ size_t offset; ///< Offset to receive data
+ bool stopped; ///< Have we stopped running?
+ int timeout; ///< Timeout in ms
+ bool packet; ///< true if packet was supplied
+
+ // In case we need to log an error, the origin of the last asynchronous
+ // I/O is recorded. To save time and simplify the code, this is recorded
+ // as the ID of the error message that would be generated if the I/O failed.
+ // This means that we must make sure that all possible "origins" take the
+ // same arguments in their message in the same order.
+ isc::log::MessageID origin; ///< Origin of last asynchronous I/O
+ uint8_t staging[IOFetch::STAGING_LENGTH];
+ ///< Temporary array for received data
+ isc::dns::qid_t qid; ///< The QID set in the query
+
+ /// \brief Constructor
+ ///
+ /// Just fills in the data members of the IOFetchData structure
+ ///
+ /// \param proto Either IOFetch::TCP or IOFetch::UDP.
+ /// \param service I/O Service object to handle the asynchronous
+ /// operations.
+ /// \param address IP address of upstream server
+ /// \param port Port to use for the query
+ /// \param buff Output buffer into which the response (in wire format)
+ /// is written (if a response is received).
+ /// \param cb Callback object containing the callback to be called
+ /// when we terminate. The caller is responsible for managing this
+ /// object and deleting it if necessary.
+ /// \param wait Timeout for the fetch (in ms).
+ ///
+ /// TODO: May need to alter constructor (see comment 4 in Trac ticket #554)
+ IOFetchData(IOFetch::Protocol proto, IOService& service,
+ const IOAddress& address, uint16_t port, OutputBufferPtr& buff,
+ IOFetch::Callback* cb, int wait)
+ :
+ socket((proto == IOFetch::UDP) ?
+ static_cast<IOAsioSocket<IOFetch>*>(
+ new UDPSocket<IOFetch>(service)) :
+ static_cast<IOAsioSocket<IOFetch>*>(
+ new TCPSocket<IOFetch>(service))
+ ),
+ remote_snd((proto == IOFetch::UDP) ?
+ static_cast<IOEndpoint*>(new UDPEndpoint(address, port)) :
+ static_cast<IOEndpoint*>(new TCPEndpoint(address, port))
+ ),
+ remote_rcv((proto == IOFetch::UDP) ?
+ static_cast<IOEndpoint*>(new UDPEndpoint(address, port)) :
+ static_cast<IOEndpoint*>(new TCPEndpoint(address, port))
+ ),
+ msgbuf(new OutputBuffer(512)),
+ received(buff),
+ callback(cb),
+ timer(service.get_io_service()),
+ protocol(proto),
+ cumulative(0),
+ expected(0),
+ offset(0),
+ stopped(false),
+ timeout(wait),
+ packet(false),
+ origin(ASIODNS_UNKNOWN_ORIGIN),
+ staging(),
+ qid(QidGenerator::getInstance().generateQid())
+ {}
+
+ // Checks if the response we received was ok;
+ // - data contains the buffer we read, as well as the address
+ // we sent to and the address we received from.
+ // length is provided by the operator() in IOFetch.
+ // Addresses must match, number of octets read must be at least
+ // 2, and the first two octets must match the qid of the message
+ // we sent.
+ bool responseOK() {
+ return (*remote_snd == *remote_rcv && cumulative >= 2 &&
+ readUint16(received->getData()) == qid);
+ }
+};
+
+/// IOFetch Constructor - just initialize the private data
+
+IOFetch::IOFetch(Protocol protocol, IOService& service,
+ const isc::dns::Question& question, const IOAddress& address, uint16_t port,
+ OutputBufferPtr& buff, Callback* cb, int wait)
+{
+ MessagePtr query_msg(new Message(Message::RENDER));
+ initIOFetch(query_msg, protocol, service, question, address, port, buff,
+ cb, wait);
+}
+
+IOFetch::IOFetch(Protocol protocol, IOService& service,
+ OutputBufferPtr& outpkt, const IOAddress& address, uint16_t port,
+ OutputBufferPtr& buff, Callback* cb, int wait)
+ :
+ data_(new IOFetchData(protocol, service,
+ address, port, buff, cb, wait))
+{
+ data_->msgbuf = outpkt;
+ data_->packet = true;
+}
+
+IOFetch::IOFetch(Protocol protocol, IOService& service,
+ ConstMessagePtr query_message, const IOAddress& address, uint16_t port,
+ OutputBufferPtr& buff, Callback* cb, int wait)
+{
+ MessagePtr msg(new Message(Message::RENDER));
+
+ msg->setHeaderFlag(Message::HEADERFLAG_RD,
+ query_message->getHeaderFlag(Message::HEADERFLAG_RD));
+ msg->setHeaderFlag(Message::HEADERFLAG_CD,
+ query_message->getHeaderFlag(Message::HEADERFLAG_CD));
+
+ initIOFetch(msg, protocol, service,
+ **(query_message->beginQuestion()),
+ address, port, buff, cb, wait);
+}
+
+void
+IOFetch::initIOFetch(MessagePtr& query_msg, Protocol protocol, IOService& service,
+ const isc::dns::Question& question,
+ const IOAddress& address, uint16_t port,
+ OutputBufferPtr& buff, Callback* cb, int wait)
+{
+ data_ = boost::shared_ptr<IOFetchData>(new IOFetchData(
+ protocol, service, address, port, buff, cb, wait));
+
+ query_msg->setQid(data_->qid);
+ query_msg->setOpcode(Opcode::QUERY());
+ query_msg->setRcode(Rcode::NOERROR());
+ query_msg->setHeaderFlag(Message::HEADERFLAG_RD);
+ query_msg->addQuestion(question);
+ EDNSPtr edns_query(new EDNS());
+ edns_query->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
+ query_msg->setEDNS(edns_query);
+ MessageRenderer renderer(*data_->msgbuf);
+ query_msg->toWire(renderer);
+}
+
+// Return protocol in use.
+
+IOFetch::Protocol
+IOFetch::getProtocol() const {
+ return (data_->protocol);
+}
+
+/// The function operator is implemented with the "stackless coroutine"
+/// pattern; see internal/coroutine.h for details.
+
+void
+IOFetch::operator()(asio::error_code ec, size_t length) {
+
+ if (data_->stopped) {
+ return;
+ } else if (ec) {
+ logIOFailure(ec);
+ return;
+ }
+
+ CORO_REENTER (this) {
+
+ /// Generate the upstream query and render it to wire format
+ /// This is done in a different scope to allow inline variable
+ /// declarations.
+ {
+ if (data_->packet) {
+ // A packet was given, overwrite the QID (which is in the
+ // first two bytes of the packet).
+ data_->msgbuf->writeUint16At(data_->qid, 0);
+
+ }
+ }
+
+ // If we timeout, we stop, which will can cancel outstanding I/Os and
+ // shutdown everything.
+ if (data_->timeout != -1) {
+ data_->timer.expires_from_now(boost::posix_time::milliseconds(
+ data_->timeout));
+ data_->timer.async_wait(boost::bind(&IOFetch::stop, *this,
+ TIME_OUT));
+ }
+
+ // Open a connection to the target system. For speed, if the operation
+ // is synchronous (i.e. UDP operation) we bypass the yield.
+ data_->origin = ASIODNS_OPEN_SOCKET;
+ if (data_->socket->isOpenSynchronous()) {
+ data_->socket->open(data_->remote_snd.get(), *this);
+ } else {
+ CORO_YIELD data_->socket->open(data_->remote_snd.get(), *this);
+ }
+
+ do {
+ // Begin an asynchronous send, and then yield. When the send completes,
+ // we will resume immediately after this point.
+ data_->origin = ASIODNS_SEND_DATA;
+ CORO_YIELD data_->socket->asyncSend(data_->msgbuf->getData(),
+ data_->msgbuf->getLength(), data_->remote_snd.get(), *this);
+
+ // Now receive the response. Since TCP may not receive the entire
+ // message in one operation, we need to loop until we have received
+ // it. (This can't be done within the asyncReceive() method because
+ // each I/O operation will be done asynchronously and between each one
+ // we need to yield ... and we *really* don't want to set up another
+ // coroutine within that method.) So after each receive (and yield),
+ // we check if the operation is complete and if not, loop to read again.
+ //
+ // Another concession to TCP is that the amount of is contained in the
+ // first two bytes. This leads to two problems:
+ //
+ // a) We don't want those bytes in the return buffer.
+ // b) They may not both arrive in the first I/O.
+ //
+ // So... we need to loop until we have at least two bytes, then store
+ // the expected amount of data. Then we need to loop until we have
+ // received all the data before copying it back to the user's buffer.
+ // And we want to minimise the amount of copying...
+
+ data_->origin = ASIODNS_READ_DATA;
+ data_->cumulative = 0; // No data yet received
+ data_->offset = 0; // First data into start of buffer
+ data_->received->clear(); // Clear the receive buffer
+ do {
+ CORO_YIELD data_->socket->asyncReceive(data_->staging,
+ static_cast<size_t>(STAGING_LENGTH),
+ data_->offset,
+ data_->remote_rcv.get(), *this);
+ } while (!data_->socket->processReceivedData(data_->staging, length,
+ data_->cumulative, data_->offset,
+ data_->expected, data_->received));
+ } while (!data_->responseOK());
+
+ // Finished with this socket, so close it. This will not generate an
+ // I/O error, but reset the origin to unknown in case we change this.
+ data_->origin = ASIODNS_UNKNOWN_ORIGIN;
+ data_->socket->close();
+
+ /// We are done
+ stop(SUCCESS);
+ }
+}
+
+// Function that stops the coroutine sequence. It is called either when the
+// query finishes or when the timer times out. Either way, it sets the
+// "stopped_" flag and cancels anything that is in progress.
+//
+// As the function may be entered multiple times as things wind down, it checks
+// if the stopped_ flag is already set. If it is, the call is a no-op.
+
+void
+IOFetch::stop(Result result) {
+
+ if (!data_->stopped) {
+
+ // Mark the fetch as stopped to prevent other completion callbacks
+ // (invoked because of the calls to cancel()) from executing the
+ // cancel calls again.
+ //
+ // In a single threaded environment, the callbacks won't be invoked
+ // until this one completes. In a multi-threaded environment, they may
+ // well be, in which case the testing (and setting) of the stopped_
+ // variable should be done inside a mutex (and the stopped_ variable
+ // declared as "volatile").
+ //
+ // The numeric arguments indicate the debug level, with the lower
+ // numbers indicating the most important information. The relative
+ // values are somewhat arbitrary.
+ //
+ // TODO: Update testing of stopped_ if threads are used.
+ data_->stopped = true;
+ switch (result) {
+ case TIME_OUT:
+ LOG_DEBUG(logger, DBG_COMMON, ASIODNS_READ_TIMEOUT).
+ arg(data_->remote_snd->getAddress().toText()).
+ arg(data_->remote_snd->getPort());
+ break;
+
+ case SUCCESS:
+ LOG_DEBUG(logger, DBG_ALL, ASIODNS_FETCH_COMPLETED).
+ arg(data_->remote_rcv->getAddress().toText()).
+ arg(data_->remote_rcv->getPort());
+ break;
+
+ case STOPPED:
+ // Fetch has been stopped for some other reason. This is
+ // allowed but as it is unusual it is logged, but with a lower
+ // debug level than a timeout (which is totally normal).
+ LOG_DEBUG(logger, DBG_IMPORTANT, ASIODNS_FETCH_STOPPED).
+ arg(data_->remote_snd->getAddress().toText()).
+ arg(data_->remote_snd->getPort());
+ break;
+
+ default:
+ LOG_ERROR(logger, ASIODNS_UNKNOWN_RESULT).
+ arg(data_->remote_snd->getAddress().toText()).
+ arg(data_->remote_snd->getPort());
+ }
+
+ // Stop requested, cancel and I/O's on the socket and shut it down,
+ // and cancel the timer.
+ data_->socket->cancel();
+ data_->socket->close();
+
+ data_->timer.cancel();
+
+ // Execute the I/O completion callback (if present).
+ if (data_->callback) {
+ (*(data_->callback))(result);
+ }
+ }
+}
+
+// Log an error - called on I/O failure
+
+void IOFetch::logIOFailure(asio::error_code ec) {
+
+ // Should only get here with a known error code.
+ assert((data_->origin == ASIODNS_OPEN_SOCKET) ||
+ (data_->origin == ASIODNS_SEND_DATA) ||
+ (data_->origin == ASIODNS_READ_DATA) ||
+ (data_->origin == ASIODNS_UNKNOWN_ORIGIN));
+
+ static const char* PROTOCOL[2] = {"TCP", "UDP"};
+ LOG_ERROR(logger, data_->origin).arg(ec.value()).
+ arg((data_->remote_snd->getProtocol() == IPPROTO_TCP) ?
+ PROTOCOL[0] : PROTOCOL[1]).
+ arg(data_->remote_snd->getAddress().toText()).
+ arg(data_->remote_snd->getPort());
+}
+
+} // namespace asiodns
+} // namespace isc {
diff --git a/src/lib/asiodns/io_fetch.h b/src/lib/asiodns/io_fetch.h
new file mode 100644
index 0000000..9626ffe
--- /dev/null
+++ b/src/lib/asiodns/io_fetch.h
@@ -0,0 +1,228 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __IO_FETCH_H
+#define __IO_FETCH_H 1
+
+#include <config.h>
+
+#include <boost/shared_array.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+#include <coroutine.h>
+
+#include <asio/error_code.hpp>
+#include <asiolink/io_address.h>
+#include <asiolink/io_service.h>
+
+#include <util/buffer.h>
+#include <dns/question.h>
+#include <dns/message.h>
+
+namespace isc {
+namespace asiodns {
+
+// Forward declarations
+class IOFetchData;
+
+/// \brief Upstream Fetch Processing
+///
+/// IOFetch is the class used to send upstream fetches and to handle responses.
+///
+/// \param E Endpoint type to use.
+
+class IOFetch : public coroutine {
+public:
+ /// \brief Protocol to use on the fetch
+ enum Protocol {
+ UDP = 0,
+ TCP = 1
+ };
+
+ /// \brief Origin of Asynchronous I/O Call
+ ///
+ /// Indicates what initiated an asynchronous I/O call and used in deciding
+ /// what error message to output if the I/O fails.
+ enum Origin {
+ NONE = 0, ///< No asynchronous call outstanding
+ OPEN = 1,
+ SEND = 2,
+ RECEIVE = 3,
+ CLOSE = 4
+ };
+
+ /// \brief Result of Upstream Fetch
+ ///
+ /// Note that this applies to the status of I/Os in the fetch - a fetch
+ /// that resulted in a packet being received from the server is a SUCCESS,
+ /// even if the contents of the packet indicate that some error occurred.
+ enum Result {
+ SUCCESS = 0, ///< Success, fetch completed
+ TIME_OUT = 1, ///< Failure, fetch timed out
+ STOPPED = 2, ///< Control code, fetch has been stopped
+ NOTSET = 3 ///< For testing, indicates value not set
+ };
+
+ // The next enum is a "trick" to allow constants to be defined in a class
+ // declaration.
+
+ /// \brief Integer Constants
+ enum {
+ STAGING_LENGTH = 8192 ///< Size of staging buffer
+ };
+
+ /// \brief I/O Fetch Callback
+ ///
+ /// Class of callback object for when the fetch itself has completed - an
+ /// object of this class is passed to the IOFetch constructor and its
+ /// operator() method called when the fetch completes.
+ ///
+ /// Note the difference between the two operator() methods:
+ /// - IOFetch::operator() callback is called when an asynchronous I/O has
+ /// completed.
+ /// - IOFetch::Callback::operator() is called when an upstream fetch - which
+ /// may have involved several asynchronous I/O operations - has completed.
+ ///
+ /// This is an abstract class.
+ class Callback {
+ public:
+ /// \brief Default Constructor
+ Callback()
+ {}
+
+ /// \brief Virtual Destructor
+ virtual ~Callback()
+ {}
+
+ /// \brief Callback method
+ ///
+ /// This is the method called when the fetch completes.
+ ///
+ /// \param result Result of the fetch
+ virtual void operator()(Result result) = 0;
+ };
+
+ /// \brief Constructor.
+ ///
+ /// Creates the object that will handle the upstream fetch.
+ ///
+ /// \param protocol Fetch protocol, either IOFetch::TCP or IOFetch::UDP
+ /// \param service I/O Service object to handle the asynchronous
+ /// operations.
+ /// \param question DNS question to send to the upstream server.
+ /// \param address IP address of upstream server
+ /// \param port Port to which to connect on the upstream server
+ /// \param buff Output buffer into which the response (in wire format)
+ /// is written (if a response is received).
+ /// \param cb Callback object containing the callback to be called when we
+ /// terminate. The caller is responsible for managing this object
+ /// and deleting it if necessary.
+ /// \param wait Timeout for the fetch (in ms). The default value of
+ /// -1 indicates no timeout.
+ IOFetch(Protocol protocol, isc::asiolink::IOService& service,
+ const isc::dns::Question& question,
+ const isc::asiolink::IOAddress& address,
+ uint16_t port, isc::util::OutputBufferPtr& buff, Callback* cb,
+ int wait = -1);
+
+ /// \brief Constructor
+ /// This constructor has one parameter "query_message", which
+ /// is the shared_ptr to a full query message. It's different
+ /// with above contructor which has only question section. All
+ /// other parameters are same.
+ ///
+ /// \param query_message the shared_ptr to a full query message
+ /// got from a query client.
+ IOFetch(Protocol protocol, isc::asiolink::IOService& service,
+ isc::dns::ConstMessagePtr query_message,
+ const isc::asiolink::IOAddress& address,
+ uint16_t port, isc::util::OutputBufferPtr& buff, Callback* cb,
+ int wait = -1);
+
+ /// \brief Constructor.
+ ///
+ /// Creates the object that will handle the upstream fetch.
+ ///
+ /// \param protocol Fetch protocol, either IOFetch::TCP or IOFetch::UDP
+ /// \param service I/O Service object to handle the asynchronous
+ /// operations.
+ /// \param outpkt Packet to send to upstream server. Note that the
+ /// QID (first two bytes of the packet) may be altered in the sending.
+ /// \param buff Output buffer into which the response (in wire format)
+ /// is written (if a response is received).
+ /// \param cb Callback object containing the callback to be called
+ /// when we terminate. The caller is responsible for managing this
+ /// object and deleting it if necessary.
+ /// \param address IP address of upstream server
+ /// \param port Port to which to connect on the upstream server
+ /// (default = 53)
+ /// \param wait Timeout for the fetch (in ms). The default value of
+ /// -1 indicates no timeout.
+ IOFetch(Protocol protocol, isc::asiolink::IOService& service,
+ isc::util::OutputBufferPtr& outpkt,
+ const isc::asiolink::IOAddress& address,
+ uint16_t port, isc::util::OutputBufferPtr& buff, Callback* cb,
+ int wait = -1);
+
+ /// \brief Return Current Protocol
+ ///
+ /// \return Protocol associated with this IOFetch object.
+ Protocol getProtocol() const;
+
+ /// \brief Coroutine entry point
+ ///
+ /// The operator() method is the method in which the coroutine code enters
+ /// this object when an operation has been completed.
+ ///
+ /// \param ec Error code, the result of the last asynchronous I/O operation.
+ /// \param length Amount of data received on the last asynchronous read
+ void operator()(asio::error_code ec = asio::error_code(), size_t length = 0);
+
+ /// \brief Terminate query
+ ///
+ /// This method can be called at any point. It terminates the current
+ /// query with the specified reason.
+ ///
+ /// \param reason Reason for terminating the query
+ void stop(Result reason = STOPPED);
+
+private:
+ /// \brief IOFetch Initialization Function.
+ /// All the parameters are same with the constructor, except
+ /// parameter "query_message"
+ /// \param query_message the message to be sent out.
+ void initIOFetch(isc::dns::MessagePtr& query_message, Protocol protocol,
+ isc::asiolink::IOService& service, const isc::dns::Question& question,
+ const isc::asiolink::IOAddress& address, uint16_t port,
+ isc::util::OutputBufferPtr& buff, Callback* cb, int wait);
+
+ /// \brief Log I/O Failure
+ ///
+ /// Records an I/O failure to the log file
+ ///
+ /// \param ec ASIO error code
+ void logIOFailure(asio::error_code ec);
+
+ // Member variables. All data is in a structure pointed to by a shared
+ // pointer. The IOFetch object is copied a number of times during its
+ // life, and only requiring a pointer to be copied reduces overhead.
+ boost::shared_ptr<IOFetchData> data_; ///< Private data
+
+};
+
+} // namespace asiodns
+} // namespace isc
+
+#endif // __IO_FETCH_H
diff --git a/src/lib/asiodns/tcp_server.cc b/src/lib/asiodns/tcp_server.cc
new file mode 100644
index 0000000..f91eb32
--- /dev/null
+++ b/src/lib/asiodns/tcp_server.cc
@@ -0,0 +1,244 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h> // for some IPC/network system calls
+#include <errno.h>
+
+#include <boost/shared_array.hpp>
+
+#include <log/dummylog.h>
+
+#include <util/buffer.h>
+
+#include <asio.hpp>
+#include <asiolink/dummy_io_cb.h>
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/tcp_socket.h>
+#include <tcp_server.h>
+
+
+using namespace asio;
+using asio::ip::udp;
+using asio::ip::tcp;
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::util;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace asiodns {
+
+/// The following functions implement the \c TCPServer class.
+///
+/// The constructor
+TCPServer::TCPServer(io_service& io_service,
+ const ip::address& addr, const uint16_t port,
+ const SimpleCallback* checkin,
+ const DNSLookup* lookup,
+ const DNSAnswer* answer) :
+ io_(io_service), done_(false),
+ checkin_callback_(checkin), lookup_callback_(lookup),
+ answer_callback_(answer)
+{
+ tcp::endpoint endpoint(addr, port);
+ acceptor_.reset(new tcp::acceptor(io_service));
+ acceptor_->open(endpoint.protocol());
+ // Set v6-only (we use a separate instantiation for v4,
+ // otherwise asio will bind to both v4 and v6
+ if (addr.is_v6()) {
+ acceptor_->set_option(ip::v6_only(true));
+ }
+ acceptor_->set_option(tcp::acceptor::reuse_address(true));
+ acceptor_->bind(endpoint);
+ acceptor_->listen();
+}
+
+void
+TCPServer::operator()(error_code ec, size_t length) {
+ /// Because the coroutine reentry block is implemented as
+ /// a switch statement, inline variable declarations are not
+ /// permitted. Certain variables used below can be declared here.
+
+ boost::array<const_buffer,2> bufs;
+ OutputBuffer lenbuf(TCP_MESSAGE_LENGTHSIZE);
+
+ CORO_REENTER (this) {
+ do {
+ /// Create a socket to listen for connections
+ socket_.reset(new tcp::socket(acceptor_->get_io_service()));
+
+ /// Wait for new connections. In the event of non-fatal error,
+ /// try again
+ do {
+ CORO_YIELD acceptor_->async_accept(*socket_, *this);
+
+ // Abort on fatal errors
+ // TODO: Log error?
+ if (ec) {
+ using namespace asio::error;
+ if (ec.value() != would_block && ec.value() != try_again &&
+ ec.value() != connection_aborted &&
+ ec.value() != interrupted) {
+ return;
+ }
+ }
+ } while (ec);
+
+ /// Fork the coroutine by creating a copy of this one and
+ /// scheduling it on the ASIO service queue. The parent
+ /// will continue listening for DNS connections while the
+ /// handles the one that has just arrived.
+ CORO_FORK io_.post(TCPServer(*this));
+ } while (is_parent());
+
+ /// Instantiate the data buffer that will be used by the
+ /// asynchronous read call.
+ data_.reset(new char[MAX_LENGTH]);
+
+ /// Read the message, in two parts. First, the message length:
+ CORO_YIELD async_read(*socket_, asio::buffer(data_.get(),
+ TCP_MESSAGE_LENGTHSIZE), *this);
+ if (ec) {
+ socket_->close();
+ CORO_YIELD return;
+ }
+
+ /// Now read the message itself. (This is done in a different scope
+ /// to allow inline variable declarations.)
+ CORO_YIELD {
+ InputBuffer dnsbuffer(data_.get(), length);
+ uint16_t msglen = dnsbuffer.readUint16();
+ async_read(*socket_, asio::buffer(data_.get(), msglen), *this);
+ }
+
+ if (ec) {
+ socket_->close();
+ CORO_YIELD return;
+ }
+
+
+ // Create an \c IOMessage object to store the query.
+ //
+ // (XXX: It would be good to write a factory function
+ // that would quickly generate an IOMessage object without
+ // all these calls to "new".)
+ peer_.reset(new TCPEndpoint(socket_->remote_endpoint()));
+
+ // The TCP socket class has been extended with asynchronous functions
+ // and takes as a template parameter a completion callback class. As
+ // TCPServer does not use these extended functions (only those defined
+ // in the IOSocket base class) - but needs a TCPSocket to get hold of
+ // the underlying Boost TCP socket - DummyIOCallback is used. This
+ // provides the appropriate operator() but is otherwise functionless.
+ iosock_.reset(new TCPSocket<DummyIOCallback>(*socket_));
+ io_message_.reset(new IOMessage(data_.get(), length, *iosock_, *peer_));
+ bytes_ = length;
+
+ // Perform any necessary operations prior to processing the incoming
+ // packet (e.g., checking for queued configuration messages).
+ //
+ // (XXX: it may be a performance issue to have this called for
+ // every single incoming packet; we may wish to throttle it somehow
+ // in the future.)
+ if (checkin_callback_ != NULL) {
+ (*checkin_callback_)(*io_message_);
+ }
+
+ // If we don't have a DNS Lookup provider, there's no point in
+ // continuing; we exit the coroutine permanently.
+ if (lookup_callback_ == NULL) {
+ socket_->close();
+ CORO_YIELD return;
+ }
+
+ // Reset or instantiate objects that will be needed by the
+ // DNS lookup and the write call.
+ respbuf_.reset(new OutputBuffer(0));
+ query_message_.reset(new Message(Message::PARSE));
+ answer_message_.reset(new Message(Message::RENDER));
+
+ // Schedule a DNS lookup, and yield. When the lookup is
+ // finished, the coroutine will resume immediately after
+ // this point.
+ CORO_YIELD io_.post(AsyncLookup<TCPServer>(*this));
+
+ // The 'done_' flag indicates whether we have an answer
+ // to send back. If not, exit the coroutine permanently.
+ if (!done_) {
+ // TODO: should we keep the connection open for a short time
+ // to see if new requests come in?
+ socket_->close();
+ CORO_YIELD return;
+ }
+
+ if (ec) {
+ CORO_YIELD return;
+ }
+ // Call the DNS answer provider to render the answer into
+ // wire format
+ (*answer_callback_)(*io_message_, query_message_,
+ answer_message_, respbuf_);
+
+ // Set up the response, beginning with two length bytes.
+ lenbuf.writeUint16(respbuf_->getLength());
+ bufs[0] = buffer(lenbuf.getData(), lenbuf.getLength());
+ bufs[1] = buffer(respbuf_->getData(), respbuf_->getLength());
+
+ // Begin an asynchronous send, and then yield. When the
+ // send completes, we will resume immediately after this point
+ // (though we have nothing further to do, so the coroutine
+ // will simply exit at that time).
+ CORO_YIELD async_write(*socket_, bufs, *this);
+
+ // TODO: should we keep the connection open for a short time
+ // to see if new requests come in?
+ socket_->close();
+ }
+}
+
+/// Call the DNS lookup provider. (Expected to be called by the
+/// AsyncLookup<TCPServer> handler.)
+void
+TCPServer::asyncLookup() {
+ (*lookup_callback_)(*io_message_, query_message_,
+ answer_message_, respbuf_, this);
+}
+
+void TCPServer::stop() {
+ /// we use close instead of cancel, with the same reason
+ /// with udp server stop, refer to the udp server code
+
+ acceptor_->close();
+ // User may stop the server even when it hasn't started to
+ // run, in that that socket_ is empty
+ if (socket_) {
+ socket_->close();
+ }
+}
+/// Post this coroutine on the ASIO service queue so that it will
+/// resume processing where it left off. The 'done' parameter indicates
+/// whether there is an answer to return to the client.
+void
+TCPServer::resume(const bool done) {
+ done_ = done;
+ io_.post(*this);
+}
+
+} // namespace asiodns
+} // namespace isc
diff --git a/src/lib/asiodns/tcp_server.h b/src/lib/asiodns/tcp_server.h
new file mode 100644
index 0000000..22a2f69
--- /dev/null
+++ b/src/lib/asiodns/tcp_server.h
@@ -0,0 +1,124 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __TCP_SERVER_H
+#define __TCP_SERVER_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <boost/shared_array.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <asiolink/asiolink.h>
+#include <coroutine.h>
+#include "dns_server.h"
+#include "dns_lookup.h"
+#include "dns_answer.h"
+
+namespace isc {
+namespace asiodns {
+
+/// \brief A TCP-specific \c DNSServer object.
+///
+/// This class inherits from both \c DNSServer and from \c coroutine,
+/// defined in coroutine.h.
+class TCPServer : public virtual DNSServer, public virtual coroutine {
+public:
+ explicit TCPServer(asio::io_service& io_service,
+ const asio::ip::address& addr, const uint16_t port,
+ const isc::asiolink::SimpleCallback* checkin = NULL,
+ const DNSLookup* lookup = NULL,
+ const DNSAnswer* answer = NULL);
+
+ void operator()(asio::error_code ec = asio::error_code(),
+ size_t length = 0);
+ void asyncLookup();
+ void stop();
+ void resume(const bool done);
+ bool hasAnswer() { return (done_); }
+ int value() { return (get_value()); }
+
+ DNSServer* clone() {
+ TCPServer* s = new TCPServer(*this);
+ return (s);
+ }
+
+private:
+ enum { MAX_LENGTH = 65535 };
+ static const size_t TCP_MESSAGE_LENGTHSIZE = 2;
+
+ // The ASIO service object
+ asio::io_service& io_;
+
+ // Class member variables which are dynamic, and changes to which
+ // need to accessible from both sides of a coroutine fork or from
+ // outside of the coroutine (i.e., from an asynchronous I/O call),
+ // should be declared here as pointers and allocated in the
+ // constructor or in the coroutine. This allows state information
+ // to persist when an individual copy of the coroutine falls out
+ // scope while waiting for an event, *so long as* there is another
+ // object that is referencing the same data. As a side-benefit, using
+ // pointers also reduces copy overhead for coroutine objects.
+ //
+ // Note: Currently these objects are allocated by "new" in the
+ // constructor, or in the function operator while processing a query.
+ // Repeated allocations from the heap for every incoming query is
+ // clearly a performance issue; this must be optimized in the future.
+ // The plan is to have a structure pre-allocate several "server state"
+ // objects which can be pulled off a free list and placed on an in-use
+ // list whenever a query comes in. This will serve the dual purpose
+ // of improving performance and guaranteeing that state information
+ // will *not* be destroyed when any one instance of the coroutine
+ // falls out of scope while waiting for an event.
+ //
+ // An ASIO acceptor object to handle new connections. Created in
+ // the constructor.
+ boost::shared_ptr<asio::ip::tcp::acceptor> acceptor_;
+
+ // Socket used to for listen for queries. Created in the
+ // constructor and stored in a shared_ptr because socket objects
+ // are not copyable.
+ boost::shared_ptr<asio::ip::tcp::socket> socket_;
+
+ // The buffer into which the response is written
+ boost::shared_ptr<isc::util::OutputBuffer> respbuf_;
+
+ // \c IOMessage and \c Message objects to be passed to the
+ // DNS lookup and answer providers
+ boost::shared_ptr<isc::asiolink::IOMessage> io_message_;
+ isc::dns::MessagePtr query_message_;
+ isc::dns::MessagePtr answer_message_;
+
+ // The buffer into which the query packet is written
+ boost::shared_array<char>data_;
+
+ // State information that is entirely internal to a given instance
+ // of the coroutine can be declared here.
+ size_t bytes_;
+ bool done_;
+
+ // Callback functions provided by the caller
+ const isc::asiolink::SimpleCallback* checkin_callback_;
+ const DNSLookup* lookup_callback_;
+ const DNSAnswer* answer_callback_;
+
+ boost::shared_ptr<isc::asiolink::IOEndpoint> peer_;
+ boost::shared_ptr<isc::asiolink::IOSocket> iosock_;
+};
+
+} // namespace asiodns
+} // namespace isc
+#endif // __TCP_SERVER_H
diff --git a/src/lib/asiodns/tests/Makefile.am b/src/lib/asiodns/tests/Makefile.am
new file mode 100644
index 0000000..f49d485
--- /dev/null
+++ b/src/lib/asiodns/tests/Makefile.am
@@ -0,0 +1,50 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc -I$(top_builddir)/src/lib/util
+AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\"
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
+run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
+run_unittests_SOURCES += io_service_unittest.cc
+run_unittests_SOURCES += dns_server_unittest.cc
+run_unittests_SOURCES += io_fetch_unittest.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
+
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
+# B10_CXXFLAGS)
+run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_GXX
+run_unittests_CXXFLAGS += -Wno-unused-parameter
+endif
+if USE_CLANGPP
+# Same for clang++, but we need to turn off -Werror completely.
+run_unittests_CXXFLAGS += -Wno-error
+endif
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/asiodns/tests/dns_server_unittest.cc b/src/lib/asiodns/tests/dns_server_unittest.cc
new file mode 100644
index 0000000..1ef3192
--- /dev/null
+++ b/src/lib/asiodns/tests/dns_server_unittest.cc
@@ -0,0 +1,503 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <gtest/gtest.h>
+
+#include <asio.hpp>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_error.h>
+#include <asiodns/udp_server.h>
+#include <asiodns/tcp_server.h>
+#include <asiodns/dns_answer.h>
+#include <asiodns/dns_lookup.h>
+#include <string>
+#include <csignal>
+#include <unistd.h> //for alarm
+
+#include <boost/shared_ptr.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
+
+/// The following tests focus on stop interface for udp and
+/// tcp server, there are lots of things can be shared to test
+/// both tcp and udp server, so they are in the same unittest
+
+/// The general work flow for dns server, is that wait for user
+/// query, once get one query, we will check the data is valid or
+/// not, if it passed, we will try to loop up the question, then
+/// compose the answer and finally send it back to user. The server
+/// may be stopped at any point during this porcess, so the test strategy
+/// is that we define 5 stop point and stop the server at these
+/// 5 points, to check whether stop is successful
+/// The 5 test points are :
+/// Before the server start to run
+/// After we get the query and check whether it's valid
+/// After we lookup the query
+/// After we compoisite the answer
+/// After user get the final result.
+
+/// The standard about whether we stop the server successfully or not
+/// is based on the fact that if the server is still running, the io
+/// service won't quit since it will wait for some asynchronized event for
+/// server. So if the io service block function run returns we assume
+/// that the server is stopped. To avoid stop interface failure which
+/// will block followed tests, using alarm signal to stop the blocking
+/// io service
+///
+/// The whole test context including one server and one client, and
+/// five stop checkpoints, we call them ServerStopper exclude the first
+/// stop point. Once the unittest fired, the client will send message
+/// to server, and the stopper may stop the server at the checkpoint, then
+/// we check the client get feedback or not. Since there is no DNS logic
+/// involved so the message sending between client and server is plain text
+/// And the valid checker, question lookup and answer composition are dummy.
+
+using namespace isc::asiolink;
+using namespace isc::asiodns;
+using namespace asio;
+
+namespace {
+static const std::string server_ip = "127.0.0.1";
+const int server_port = 5553;
+//message client send to udp server, which isn't dns package
+//just for simple testing
+static const std::string query_message("BIND10 is awesome");
+
+// \brief provide capacity to derived class the ability
+// to stop DNSServer at certern point
+class ServerStopper {
+ public:
+ ServerStopper() : server_to_stop_(NULL) {}
+ virtual ~ServerStopper(){}
+
+ void setServerToStop(DNSServer* server) {
+ server_to_stop_ = server;
+ }
+
+ void stopServer() const {
+ if (server_to_stop_) {
+ server_to_stop_->stop();
+ }
+ }
+
+ private:
+ DNSServer* server_to_stop_;
+};
+
+// \brief no check logic at all,just provide a checkpoint to stop the server
+class DummyChecker : public SimpleCallback, public ServerStopper {
+ public:
+ virtual void operator()(const IOMessage&) const {
+ stopServer();
+ }
+};
+
+// \brief no lookup logic at all,just provide a checkpoint to stop the server
+class DummyLookup : public DNSLookup, public ServerStopper {
+ public:
+ void operator()(const IOMessage& io_message,
+ isc::dns::MessagePtr message,
+ isc::dns::MessagePtr answer_message,
+ isc::util::OutputBufferPtr buffer,
+ DNSServer* server) const {
+ stopServer();
+ server->resume(true);
+ }
+};
+
+// \brief copy the data received from user to the answer part
+// provide checkpoint to stop server
+class SimpleAnswer : public DNSAnswer, public ServerStopper {
+ public:
+ void operator()(const IOMessage& message,
+ isc::dns::MessagePtr query_message,
+ isc::dns::MessagePtr answer_message,
+ isc::util::OutputBufferPtr buffer) const
+ {
+ //copy what we get from user
+ buffer->writeData(message.getData(), message.getDataSize());
+ stopServer();
+ }
+
+};
+
+// \brief simple client, send one string to server and wait for response
+// in case, server stopped and client cann't get response, there is a timer wait
+// for specified seconds (the value is just a estimate since server process logic is quite
+// simple, and all the intercommunication is local) then cancel the waiting.
+class SimpleClient : public ServerStopper {
+ public:
+ static const size_t MAX_DATA_LEN = 256;
+ SimpleClient(asio::io_service& service,
+ unsigned int wait_server_time_out)
+ {
+ wait_for_response_timer_.reset(new deadline_timer(service));
+ received_data_ = new char[MAX_DATA_LEN];
+ received_data_len_ = 0;
+ wait_server_time_out_ = wait_server_time_out;
+ }
+
+ virtual ~SimpleClient() {
+ delete [] received_data_;
+ }
+
+ void setGetFeedbackCallback(boost::function<void()>& func) {
+ get_response_call_back_ = func;
+ }
+
+ virtual void sendDataThenWaitForFeedback(const std::string& data) = 0;
+ virtual std::string getReceivedData() const = 0;
+
+ void startTimer() {
+ wait_for_response_timer_->cancel();
+ wait_for_response_timer_->
+ expires_from_now(boost::posix_time::
+ seconds(wait_server_time_out_));
+ wait_for_response_timer_->
+ async_wait(boost::bind(&SimpleClient::stopWaitingforResponse,
+ this));
+ }
+
+ void cancelTimer() { wait_for_response_timer_->cancel(); }
+
+ void getResponseCallBack(const asio::error_code& error, size_t
+ received_bytes)
+ {
+ cancelTimer();
+ if (!error)
+ received_data_len_ = received_bytes;
+ if (!get_response_call_back_.empty()) {
+ get_response_call_back_();
+ }
+ stopServer();
+ }
+
+
+ protected:
+ virtual void stopWaitingforResponse() = 0;
+
+ boost::shared_ptr<deadline_timer> wait_for_response_timer_;
+ char* received_data_;
+ size_t received_data_len_;
+ boost::function<void()> get_response_call_back_;
+ unsigned int wait_server_time_out_;
+};
+
+
+
+class UDPClient : public SimpleClient {
+ public:
+ //After 1 seconds without feedback client will stop wait
+ static const unsigned int server_time_out = 1;
+
+ UDPClient(asio::io_service& service, const ip::udp::endpoint& server) :
+ SimpleClient(service, server_time_out)
+ {
+ server_ = server;
+ socket_.reset(new ip::udp::socket(service));
+ socket_->open(ip::udp::v4());
+ }
+
+
+ void sendDataThenWaitForFeedback(const std::string& data) {
+ received_data_len_ = 0;
+ socket_->send_to(buffer(data.c_str(), data.size() + 1), server_);
+ socket_->async_receive_from(buffer(received_data_, MAX_DATA_LEN),
+ received_from_,
+ boost::bind(&SimpleClient::
+ getResponseCallBack, this, _1,
+ _2));
+ startTimer();
+ }
+
+ virtual std::string getReceivedData() const {
+ return (received_data_len_ == 0 ? std::string("") :
+ std::string(received_data_));
+ }
+
+ private:
+ void stopWaitingforResponse() {
+ socket_->close();
+ }
+
+ boost::shared_ptr<ip::udp::socket> socket_;
+ ip::udp::endpoint server_;
+ ip::udp::endpoint received_from_;
+};
+
+
+class TCPClient : public SimpleClient {
+ public:
+ // after 2 seconds without feedback client will stop wait,
+ // this includes connect, send message and recevice message
+ static const unsigned int server_time_out = 2;
+ TCPClient(asio::io_service& service, const ip::tcp::endpoint& server)
+ : SimpleClient(service, server_time_out)
+ {
+ server_ = server;
+ socket_.reset(new ip::tcp::socket(service));
+ socket_->open(ip::tcp::v4());
+ }
+
+
+ virtual void sendDataThenWaitForFeedback(const std::string &data) {
+ received_data_len_ = 0;
+ data_to_send_ = data;
+ data_to_send_len_ = data.size() + 1;
+ socket_->async_connect(server_, boost::bind(&TCPClient::connectHandler,
+ this, _1));
+ startTimer();
+ }
+
+ virtual std::string getReceivedData() const {
+ return (received_data_len_ == 0 ? std::string("") :
+ std::string(received_data_ + 2));
+ }
+
+ private:
+ void stopWaitingforResponse() {
+ socket_->close();
+ }
+
+ void connectHandler(const asio::error_code& error) {
+ if (!error) {
+ data_to_send_len_ = htons(data_to_send_len_);
+ socket_->async_send(buffer(&data_to_send_len_, 2),
+ boost::bind(&TCPClient::sendMessageBodyHandler,
+ this, _1, _2));
+ }
+ }
+
+ void sendMessageBodyHandler(const asio::error_code& error,
+ size_t send_bytes)
+ {
+ if (!error && send_bytes == 2) {
+ socket_->async_send(buffer(data_to_send_.c_str(),
+ data_to_send_.size() + 1),
+ boost::bind(&TCPClient::finishSendHandler, this, _1, _2));
+ }
+ }
+
+ void finishSendHandler(const asio::error_code& error, size_t send_bytes) {
+ if (!error && send_bytes == data_to_send_.size() + 1) {
+ socket_->async_receive(buffer(received_data_, MAX_DATA_LEN),
+ boost::bind(&SimpleClient::getResponseCallBack, this, _1,
+ _2));
+ }
+ }
+
+ boost::shared_ptr<ip::tcp::socket> socket_;
+ ip::tcp::endpoint server_;
+ std::string data_to_send_;
+ uint16_t data_to_send_len_;
+};
+
+
+
+// \brief provide the context which including two client and
+// two server, udp client will only communicate with udp server, same for tcp client
+class DNSServerTest : public::testing::Test {
+ protected:
+ void SetUp() {
+ ip::address server_address = ip::address::from_string(server_ip);
+ checker_ = new DummyChecker();
+ lookup_ = new DummyLookup();
+ answer_ = new SimpleAnswer();
+ udp_server_ = new UDPServer(service, server_address, server_port,
+ checker_, lookup_, answer_);
+ udp_client_ = new UDPClient(service,
+ ip::udp::endpoint(server_address,
+ server_port));
+ tcp_server_ = new TCPServer(service, server_address, server_port,
+ checker_, lookup_, answer_);
+ tcp_client_ = new TCPClient(service,
+ ip::tcp::endpoint(server_address,
+ server_port));
+ }
+
+
+ void TearDown() {
+ udp_server_->stop();
+ tcp_server_->stop();
+ delete checker_;
+ delete lookup_;
+ delete answer_;
+ delete udp_server_;
+ delete udp_client_;
+ delete tcp_server_;
+ delete tcp_client_;
+ }
+
+
+ void testStopServerByStopper(DNSServer* server, SimpleClient* client,
+ ServerStopper* stopper)
+ {
+ static const unsigned int io_service_time_out = 5;
+ io_service_is_time_out = false;
+ stopper->setServerToStop(server);
+ (*server)();
+ client->sendDataThenWaitForFeedback(query_message);
+ // Since thread hasn't been introduced into the tool box, using signal
+ // to make sure run function will eventually return even server stop
+ // failed
+ void (*prev_handler)(int) = std::signal(SIGALRM, DNSServerTest::stopIOService);
+ alarm(io_service_time_out);
+ service.run();
+ service.reset();
+ //cancel scheduled alarm
+ alarm(0);
+ std::signal(SIGALRM, prev_handler);
+ }
+
+
+ static void stopIOService(int _no_use_parameter) {
+ io_service_is_time_out = true;
+ service.stop();
+ }
+
+ bool serverStopSucceed() const {
+ return (!io_service_is_time_out);
+ }
+
+ DummyChecker* checker_;
+ DummyLookup* lookup_;
+ SimpleAnswer* answer_;
+ UDPServer* udp_server_;
+ UDPClient* udp_client_;
+ TCPClient* tcp_client_;
+ TCPServer* tcp_server_;
+
+ // To access them in signal handle function, the following
+ // variables have to be static.
+ static asio::io_service service;
+ static bool io_service_is_time_out;
+};
+
+bool DNSServerTest::io_service_is_time_out = false;
+asio::io_service DNSServerTest::service;
+
+// Test whether server stopped successfully after client get response
+// client will send query and start to wait for response, once client
+// get response, udp server will be stopped, the io service won't quit
+// if udp server doesn't stop successfully.
+TEST_F(DNSServerTest, stopUDPServerAfterOneQuery) {
+ testStopServerByStopper(udp_server_, udp_client_, udp_client_);
+ EXPECT_EQ(query_message, udp_client_->getReceivedData());
+ EXPECT_TRUE(serverStopSucceed());
+}
+
+// Test whether udp server stopped successfully before server start to serve
+TEST_F(DNSServerTest, stopUDPServerBeforeItStartServing) {
+ udp_server_->stop();
+ testStopServerByStopper(udp_server_, udp_client_, udp_client_);
+ EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
+ EXPECT_TRUE(serverStopSucceed());
+}
+
+
+// Test whether udp server stopped successfully during message check
+TEST_F(DNSServerTest, stopUDPServerDuringMessageCheck) {
+ testStopServerByStopper(udp_server_, udp_client_, checker_);
+ EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
+ EXPECT_TRUE(serverStopSucceed());
+}
+
+// Test whether udp server stopped successfully during query lookup
+TEST_F(DNSServerTest, stopUDPServerDuringQueryLookup) {
+ testStopServerByStopper(udp_server_, udp_client_, lookup_);
+ EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
+ EXPECT_TRUE(serverStopSucceed());
+}
+
+// Test whether udp server stopped successfully during composing answer
+TEST_F(DNSServerTest, stopUDPServerDuringPrepareAnswer) {
+ testStopServerByStopper(udp_server_, udp_client_, answer_);
+ EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
+ EXPECT_TRUE(serverStopSucceed());
+}
+
+static void stopServerManyTimes(DNSServer *server, unsigned int times) {
+ for (int i = 0; i < times; ++i) {
+ server->stop();
+ }
+}
+
+// Test whether udp server stop interface can be invoked several times without
+// throw any exception
+TEST_F(DNSServerTest, stopUDPServeMoreThanOnce) {
+ ASSERT_NO_THROW({
+ boost::function<void()> stop_server_3_times
+ = boost::bind(stopServerManyTimes, udp_server_, 3);
+ udp_client_->setGetFeedbackCallback(stop_server_3_times);
+ testStopServerByStopper(udp_server_, udp_client_, udp_client_);
+ EXPECT_EQ(query_message, udp_client_->getReceivedData());
+ });
+ EXPECT_TRUE(serverStopSucceed());
+}
+
+
+TEST_F(DNSServerTest, stopTCPServerAfterOneQuery) {
+ testStopServerByStopper(tcp_server_, tcp_client_, tcp_client_);
+ EXPECT_EQ(query_message, tcp_client_->getReceivedData());
+ EXPECT_TRUE(serverStopSucceed());
+}
+
+
+// Test whether tcp server stopped successfully before server start to serve
+TEST_F(DNSServerTest, stopTCPServerBeforeItStartServing) {
+ tcp_server_->stop();
+ testStopServerByStopper(tcp_server_, tcp_client_, tcp_client_);
+ EXPECT_EQ(std::string(""), tcp_client_->getReceivedData());
+ EXPECT_TRUE(serverStopSucceed());
+}
+
+
+// Test whether tcp server stopped successfully during message check
+TEST_F(DNSServerTest, stopTCPServerDuringMessageCheck) {
+ testStopServerByStopper(tcp_server_, tcp_client_, checker_);
+ EXPECT_EQ(std::string(""), tcp_client_->getReceivedData());
+ EXPECT_TRUE(serverStopSucceed());
+}
+
+// Test whether tcp server stopped successfully during query lookup
+TEST_F(DNSServerTest, stopTCPServerDuringQueryLookup) {
+ testStopServerByStopper(tcp_server_, tcp_client_, lookup_);
+ EXPECT_EQ(std::string(""), tcp_client_->getReceivedData());
+ EXPECT_TRUE(serverStopSucceed());
+}
+
+// Test whether tcp server stopped successfully during composing answer
+TEST_F(DNSServerTest, stopTCPServerDuringPrepareAnswer) {
+ testStopServerByStopper(tcp_server_, tcp_client_, answer_);
+ EXPECT_EQ(std::string(""), tcp_client_->getReceivedData());
+ EXPECT_TRUE(serverStopSucceed());
+}
+
+
+// Test whether tcp server stop interface can be invoked several times without
+// throw any exception
+TEST_F(DNSServerTest, stopTCPServeMoreThanOnce) {
+ ASSERT_NO_THROW({
+ boost::function<void()> stop_server_3_times
+ = boost::bind(stopServerManyTimes, tcp_server_, 3);
+ tcp_client_->setGetFeedbackCallback(stop_server_3_times);
+ testStopServerByStopper(tcp_server_, tcp_client_, tcp_client_);
+ EXPECT_EQ(query_message, tcp_client_->getReceivedData());
+ });
+ EXPECT_TRUE(serverStopSucceed());
+}
+
+}
diff --git a/src/lib/asiodns/tests/io_fetch_unittest.cc b/src/lib/asiodns/tests/io_fetch_unittest.cc
new file mode 100644
index 0000000..52a51a1
--- /dev/null
+++ b/src/lib/asiodns/tests/io_fetch_unittest.cc
@@ -0,0 +1,731 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <algorithm>
+#include <cstdlib>
+#include <string>
+#include <iostream>
+#include <iomanip>
+#include <iterator>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <boost/bind.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+#include <asio.hpp>
+
+#include <util/buffer.h>
+#include <util/io_utilities.h>
+
+#include <dns/question.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/name.h>
+#include <dns/rcode.h>
+
+#include <asiolink/io_address.h>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_service.h>
+#include <asiodns/io_fetch.h>
+
+using namespace asio;
+using namespace isc::dns;
+using namespace isc::util;
+using namespace asio::ip;
+using namespace std;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace asiodns {
+
+const asio::ip::address TEST_HOST(asio::ip::address::from_string("127.0.0.1"));
+const uint16_t TEST_PORT(5301);
+const int SEND_INTERVAL = 250; // Interval in ms between TCP sends
+const size_t MAX_SIZE = 64 * 1024; // Should be able to take 64kB
+
+// The tests are complex, so debug output has been left in (although disabled).
+// Set this to true to enable it.
+const bool DEBUG = false;
+
+/// \brief Test fixture for the asiolink::IOFetch.
+class IOFetchTest : public virtual ::testing::Test, public virtual IOFetch::Callback
+{
+public:
+ IOService service_; ///< Service to run the query
+ IOFetch::Result expected_; ///< Expected result of the callback
+ bool run_; ///< Did the callback run already?
+ Question question_; ///< What to ask
+ OutputBufferPtr result_buff_; ///< Buffer to hold result of fetch
+ OutputBufferPtr msgbuf_; ///< Buffer corresponding to known question
+ IOFetch udp_fetch_; ///< For UDP query test
+ IOFetch tcp_fetch_; ///< For TCP query test
+ IOFetch::Protocol protocol_; ///< Protocol being tested
+ size_t cumulative_; ///< Cumulative data received by "server".
+ deadline_timer timer_; ///< Timer to measure timeouts
+
+ // The next member is the buffer in which the "server" (implemented by the
+ // response handler methods in this class) receives the question sent by the
+ // fetch object.
+ uint8_t receive_buffer_[MAX_SIZE]; ///< Server receive buffer
+ OutputBufferPtr expected_buffer_; ///< Data we expect to receive
+ vector<uint8_t> send_buffer_; ///< Server send buffer
+ uint16_t send_cumulative_; ///< Data sent so far
+
+ // Other data.
+ string return_data_; ///< Data returned by server
+ string test_data_; ///< Large string - here for convenience
+ bool debug_; ///< true to enable debug output
+ size_t tcp_send_size_; ///< Max size of TCP send
+ uint8_t qid_0; ///< First octet of qid
+ uint8_t qid_1; ///< Second octet of qid
+
+ bool tcp_short_send_; ///< If set to true, we do not send
+ /// all data in the tcp response
+
+ /// \brief Constructor
+ IOFetchTest() :
+ service_(),
+ expected_(IOFetch::NOTSET),
+ run_(false),
+ question_(Name("example.net"), RRClass::IN(), RRType::A()),
+ result_buff_(new OutputBuffer(512)),
+ msgbuf_(new OutputBuffer(512)),
+ udp_fetch_(IOFetch::UDP, service_, question_, IOAddress(TEST_HOST),
+ TEST_PORT, result_buff_, this, 100),
+ tcp_fetch_(IOFetch::TCP, service_, question_, IOAddress(TEST_HOST),
+ TEST_PORT, result_buff_, this, (16 * SEND_INTERVAL)),
+ // Timeout interval chosen to ensure no timeout
+ protocol_(IOFetch::TCP), // for initialization - will be changed
+ cumulative_(0),
+ timer_(service_.get_io_service()),
+ receive_buffer_(),
+ expected_buffer_(new OutputBuffer(512)),
+ send_buffer_(),
+ send_cumulative_(0),
+ return_data_(""),
+ test_data_(""),
+ debug_(DEBUG),
+ tcp_send_size_(0),
+ qid_0(0),
+ qid_1(0),
+ tcp_short_send_(false)
+ {
+ // Construct the data buffer for question we expect to receive.
+ Message msg(Message::RENDER);
+ msg.setQid(0);
+ msg.setOpcode(Opcode::QUERY());
+ msg.setRcode(Rcode::NOERROR());
+ msg.setHeaderFlag(Message::HEADERFLAG_RD);
+ msg.addQuestion(question_);
+ EDNSPtr msg_edns(new EDNS());
+ msg_edns->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
+ msg.setEDNS(msg_edns);
+ MessageRenderer renderer(*msgbuf_);
+ msg.toWire(renderer);
+ MessageRenderer renderer2(*expected_buffer_);
+ msg.toWire(renderer2);
+
+ // Initialize the test data to be returned: tests will return a
+ // substring of this data. (It's convenient to have this as a member of
+ // the class.)
+ //
+ // We could initialize the data with a single character, but as an added
+ // check we'll make ssre that it has some structure.
+
+ test_data_.clear();
+ test_data_.reserve(MAX_SIZE);
+ while (test_data_.size() < MAX_SIZE) {
+ test_data_ += "A message to be returned to the client that has "
+ "some sort of structure.";
+ }
+ }
+
+ /// \brief UDP Response handler (the "remote UDP DNS server")
+ ///
+ /// When IOFetch is sending data, this response handler emulates the remote
+ /// DNS server. It checks that the data sent by the IOFetch object is what
+ /// was expected to have been sent, then sends back a known buffer of data.
+ ///
+ /// \param remote Endpoint to which to send the answer
+ /// \param socket Socket to use to send the answer
+ /// \param ec ASIO error code, completion code of asynchronous I/O issued
+ /// by the "server" to receive data.
+ /// \param bad_qid If set to true, the QID in the response will be mangled
+ /// \param second_send If set to true, (and bad_qid is too), after the
+ /// mangled qid response has been sent, a second packet will be
+ /// sent with the correct QID.
+ /// \param length Amount of data received.
+ void udpReceiveHandler(udp::endpoint* remote, udp::socket* socket,
+ error_code ec = error_code(), size_t length = 0,
+ bool bad_qid = false, bool second_send = false) {
+ if (debug_) {
+ cout << "udpReceiveHandler(): error = " << ec.value() <<
+ ", length = " << length << endl;
+ }
+
+ // The QID in the incoming data is random so set it to 0 for the
+ // data comparison check. (It is set to 0 in the buffer containing
+ // the expected data.)
+ qid_0 = receive_buffer_[0];
+ qid_1 = receive_buffer_[1];
+ receive_buffer_[0] = receive_buffer_[1] = 0;
+
+ // Check that length of the received data and the expected data are
+ // identical, then check that the data is identical as well.
+ EXPECT_EQ(msgbuf_->getLength(), length);
+ EXPECT_TRUE(equal(receive_buffer_, (receive_buffer_ + length - 1),
+ static_cast<const uint8_t*>(msgbuf_->getData())));
+
+ // Return a message back to the IOFetch object.
+ if (!bad_qid) {
+ expected_buffer_->writeUint8At(qid_0, 0);
+ expected_buffer_->writeUint8At(qid_1, 1);
+ } else {
+ expected_buffer_->writeUint8At(qid_0 + 1, 0);
+ expected_buffer_->writeUint8At(qid_1 + 1, 1);
+ }
+ socket->send_to(asio::buffer(expected_buffer_->getData(), length), *remote);
+
+ if (bad_qid && second_send) {
+ expected_buffer_->writeUint8At(qid_0, 0);
+ expected_buffer_->writeUint8At(qid_1, 1);
+ socket->send_to(asio::buffer(expected_buffer_->getData(),
+ expected_buffer_->getLength()), *remote);
+ }
+ if (debug_) {
+ cout << "udpReceiveHandler(): returned " << expected_buffer_->getLength() <<
+ " bytes to the client" << endl;
+ }
+ }
+
+ /// \brief Completion Handler for accepting TCP data
+ ///
+ /// Called when the remote system connects to the "server". It issues
+ /// an asynchronous read on the socket to read data.
+ ///
+ /// \param socket Socket on which data will be received
+ /// \param ec Boost error code, value should be zero.
+ void tcpAcceptHandler(tcp::socket* socket, error_code ec = error_code())
+ {
+ if (debug_) {
+ cout << "tcpAcceptHandler(): error = " << ec.value() << endl;
+ }
+
+ // Expect that the accept completed without a problem.
+ EXPECT_EQ(0, ec.value());
+
+ // Work out the maximum size of data we can send over it when we
+ // respond, then subtract 1kB or so for safety.
+ tcp::socket::send_buffer_size send_size;
+ socket->get_option(send_size);
+ if (send_size.value() < (2 * 1024)) {
+ FAIL() << "TCP send size is less than 2kB";
+ } else {
+ tcp_send_size_ = send_size.value() - 1024;
+ if (debug_) {
+ cout << "tcpacceptHandler(): will use send size = " << tcp_send_size_ << endl;
+ }
+ }
+
+ // Initiate a read on the socket.
+ cumulative_ = 0;
+ socket->async_receive(asio::buffer(receive_buffer_, sizeof(receive_buffer_)),
+ boost::bind(&IOFetchTest::tcpReceiveHandler, this, socket, _1, _2));
+ }
+
+ /// \brief Completion handler for receiving TCP data
+ ///
+ /// When IOFetch is sending data, this response handler emulates the remote
+ /// DNS server. It that all the data sent by the IOFetch object has been
+ /// received, issuing another read if not. If the data is complete, it is
+ /// compared to what is expected and a reply sent back to the IOFetch.
+ ///
+ /// \param socket Socket to use to send the answer
+ /// \param ec ASIO error code, completion code of asynchronous I/O issued
+ /// by the "server" to receive data.
+ /// \param length Amount of data received.
+ void tcpReceiveHandler(tcp::socket* socket, error_code ec = error_code(),
+ size_t length = 0)
+ {
+ if (debug_) {
+ cout << "tcpReceiveHandler(): error = " << ec.value() <<
+ ", length = " << length << endl;
+ }
+ // Expect that the receive completed without a problem.
+ EXPECT_EQ(0, ec.value());
+
+ // If we haven't received all the data, issue another read.
+ cumulative_ += length;
+ bool complete = false;
+ if (cumulative_ > 2) {
+ uint16_t dns_length = readUint16(receive_buffer_);
+ complete = ((dns_length + 2) == cumulative_);
+ }
+
+ if (!complete) {
+ socket->async_receive(asio::buffer((receive_buffer_ + cumulative_),
+ (sizeof(receive_buffer_) - cumulative_)),
+ boost::bind(&IOFetchTest::tcpReceiveHandler, this, socket, _1, _2));
+ return;
+ }
+
+ // Check that length of the DNS message received is that expected, then
+ // compare buffers, zeroing the QID in the received buffer to match
+ // that set in our expected question. Note that due to the length
+ // field the QID in the received buffer is in the third and fourth
+ // bytes.
+ EXPECT_EQ(msgbuf_->getLength() + 2, cumulative_);
+ qid_0 = receive_buffer_[2];
+ qid_1 = receive_buffer_[3];
+
+ receive_buffer_[2] = receive_buffer_[3] = 0;
+ EXPECT_TRUE(equal((receive_buffer_ + 2), (receive_buffer_ + cumulative_ - 2),
+ static_cast<const uint8_t*>(msgbuf_->getData())));
+
+ // ... and return a message back. This has to be preceded by a two-byte
+ // count field.
+
+ send_buffer_.clear();
+ send_buffer_.push_back(0);
+ send_buffer_.push_back(0);
+ writeUint16(return_data_.size(), &send_buffer_[0]);
+ copy(return_data_.begin(), return_data_.end(), back_inserter(send_buffer_));
+ if (return_data_.size() >= 2) {
+ send_buffer_[2] = qid_0;
+ send_buffer_[3] = qid_1;
+ }
+ // Send the data. This is done in multiple writes with a delay between
+ // each to check that the reassembly of TCP packets from fragments works.
+ send_cumulative_ = 0;
+ tcpSendData(socket);
+ }
+
+ /// \brief Sent Data Over TCP
+ ///
+ /// Send the TCP data back to the IOFetch object. The data is sent in
+ /// three chunks - two of 16 bytes and the remainder, with a 250ms gap
+ /// between each. (Amounts of data smaller than one 32 bytes are sent in
+ /// one or two packets.)
+ ///
+ /// \param socket Socket over which send should take place
+ void tcpSendData(tcp::socket* socket) {
+ if (debug_) {
+ cout << "tcpSendData()" << endl;
+ }
+
+ // Decide what to send based on the cumulative count. At most we'll do
+ // two chunks of 16 bytes (with a 250ms gap between) and then the
+ // remainder.
+ uint8_t* send_ptr = &send_buffer_[send_cumulative_];
+ // Pointer to data to send
+ size_t amount = 16; // Amount of data to send
+ if (send_cumulative_ < (2 * amount)) {
+
+ // First or second time through, send at most 16 bytes
+ amount = min(amount, (send_buffer_.size() - send_cumulative_));
+
+ } else {
+
+ // For all subsequent times, send the remainder, maximised to
+ // whatever we have chosen for the maximum send size.
+ amount = min(tcp_send_size_,
+ (send_buffer_.size() - send_cumulative_));
+ }
+
+ // This is for the short send test; reduce the actual amount of
+ // data we send
+ if (tcp_short_send_) {
+ if (debug_) {
+ cout << "tcpSendData(): sending incomplete data (" <<
+ (amount - 1) << " of " << amount << " bytes)" <<
+ endl;
+ }
+ --amount;
+ } else {
+ if (debug_) {
+ cout << "tcpSendData(): sending " << amount << " bytes" << endl;
+ }
+ }
+
+ // ... and send it. The amount sent is also passed as the first
+ // argument of the send callback, as a check.
+ socket->async_send(asio::buffer(send_ptr, amount),
+ boost::bind(&IOFetchTest::tcpSendHandler, this,
+ amount, socket, _1, _2));
+ }
+
+ /// \brief Completion Handler for Sending TCP data
+ ///
+ /// Called when the asynchronous send of data back to the IOFetch object
+ /// by the TCP "server" in this class has completed. (This send has to
+ /// be asynchronous because control needs to return to the caller in order
+ /// for the IOService "run()" method to be called to run the handlers.)
+ ///
+ /// If not all the data has been sent, a short delay is instigated (during
+ /// which control returns to the IOService). This should force the queued
+ /// data to actually be sent and the IOFetch receive handler to be triggered.
+ /// In this way, the ability of IOFetch to handle fragmented TCP packets
+ /// should be checked.
+ ///
+ /// \param expected Number of bytes that were expected to have been sent.
+ /// \param socket Socket over which the send took place. Only used to
+ /// pass back to the send method.
+ /// \param ec Boost error code, value should be zero.
+ /// \param length Number of bytes sent.
+ void tcpSendHandler(size_t expected, tcp::socket* socket,
+ error_code ec = error_code(), size_t length = 0)
+ {
+ if (debug_) {
+ cout << "tcpSendHandler(): error = " << ec.value() <<
+ ", length = " << length << endl;
+ }
+
+ EXPECT_EQ(0, ec.value()); // Expect no error
+ EXPECT_EQ(expected, length); // And that amount sent is as expected
+
+ // Do we need to send more?
+ send_cumulative_ += length;
+ if (send_cumulative_ < send_buffer_.size()) {
+
+ // Yes - set up a timer: the callback handler for the timer is
+ // tcpSendData, which will then send the next chunk. We pass the
+ // socket over which data should be sent as an argument to that
+ // function.
+ timer_.expires_from_now(boost::posix_time::milliseconds(SEND_INTERVAL));
+ timer_.async_wait(boost::bind(&IOFetchTest::tcpSendData, this,
+ socket));
+ }
+ }
+
+ /// \brief Fetch completion callback
+ ///
+ /// This is the callback's operator() method which is called when the fetch
+ /// is complete. It checks that the data received is the wire format of the
+ /// data sent back by the server.
+ ///
+ /// \param result Result indicated by the callback
+ void operator()(IOFetch::Result result) {
+ if (debug_) {
+ cout << "operator()(): result = " << result << endl;
+ }
+
+ EXPECT_EQ(expected_, result); // Check correct result returned
+ EXPECT_FALSE(run_); // Check it is run only once
+ run_ = true; // Note success
+
+ // If the expected result for SUCCESS, then this should have been called
+ // when one of the "servers" in this class has sent back return_data_.
+ // Check the data is as expected/
+ if (expected_ == IOFetch::SUCCESS) {
+ // In the case of UDP, we actually send back a real looking packet
+ // in the case of TCP, we send back a 'random' string
+ if (protocol_ == IOFetch::UDP) {
+ EXPECT_EQ(expected_buffer_->getLength(), result_buff_->getLength());
+ EXPECT_EQ(0, memcmp(expected_buffer_->getData(), result_buff_->getData(),
+ expected_buffer_->getLength()));
+ } else {
+ EXPECT_EQ(return_data_.size(), result_buff_->getLength());
+ // Overwrite the random qid with our own data for the
+ // comparison to succeed
+ if (result_buff_->getLength() >= 2) {
+ result_buff_->writeUint8At(return_data_[0], 0);
+ result_buff_->writeUint8At(return_data_[1], 1);
+ }
+ const uint8_t* start = static_cast<const uint8_t*>(result_buff_->getData());
+ EXPECT_TRUE(equal(return_data_.begin(), return_data_.end(), start));
+ }
+ }
+
+ // ... and cause the run loop to exit.
+ service_.stop();
+ }
+
+ // The next set of methods are the tests themselves. A number of the TCP
+ // and UDP tests are very similar.
+
+ /// \brief Check for stop()
+ ///
+ /// Test that when we run the query and stop it after it was run, it returns
+ /// "stopped" correctly. (That is why stop() is posted to the service_ as
+ /// well instead of calling it.)
+ ///
+ /// \param protocol Test protocol
+ /// \param fetch Fetch object being tested
+ void stopTest(IOFetch::Protocol protocol, IOFetch& fetch) {
+ protocol_ = protocol;
+ expected_ = IOFetch::STOPPED;
+
+ // Post the query
+ service_.get_io_service().post(fetch);
+
+ // Post query_.stop() (yes, the boost::bind thing is just
+ // query_.stop()).
+ service_.get_io_service().post(
+ boost::bind(&IOFetch::stop, fetch, IOFetch::STOPPED));
+
+ // Run both of them. run() returns when everything in the I/O service
+ // queue has completed.
+ service_.run();
+ EXPECT_TRUE(run_);
+ }
+
+ /// \brief Premature stop test
+ ///
+ /// Test that when we queue the query to service_ and call stop() before it
+ /// gets executed, it acts sanely as well (eg. has the same result as
+ /// running stop() after - calls the callback).
+ ///
+ /// \param protocol Test protocol
+ /// \param fetch Fetch object being tested
+ void prematureStopTest(IOFetch::Protocol protocol, IOFetch& fetch) {
+ protocol_ = protocol;
+ expected_ = IOFetch::STOPPED;
+
+ // Stop before it is started
+ fetch.stop();
+ service_.get_io_service().post(fetch);
+
+ service_.run();
+ EXPECT_TRUE(run_);
+ }
+
+ /// \brief Timeout test
+ ///
+ /// Test that fetch times out when no answer arrives.
+ ///
+ /// \param protocol Test protocol
+ /// \param fetch Fetch object being tested
+ void timeoutTest(IOFetch::Protocol protocol, IOFetch& fetch) {
+ protocol_ = protocol;
+ expected_ = IOFetch::TIME_OUT;
+
+ service_.get_io_service().post(fetch);
+ service_.run();
+ EXPECT_TRUE(run_);
+ }
+
+ /// \brief Send/Receive Test
+ ///
+ /// Send a query to the server then receives a response.
+ ///
+ /// \param Test data to return to client
+ /// \param short_send If true, do not send all data
+ /// (should result in timeout)
+ void tcpSendReturnTest(const std::string& return_data, bool short_send = false) {
+ if (debug_) {
+ cout << "tcpSendReturnTest(): data size = " << return_data.size() << endl;
+ }
+ return_data_ = return_data;
+ protocol_ = IOFetch::TCP;
+ if (short_send) {
+ tcp_short_send_ = true;
+ expected_ = IOFetch::TIME_OUT;
+ } else {
+ expected_ = IOFetch::SUCCESS;
+ }
+
+ // Socket into which the connection will be accepted.
+ tcp::socket socket(service_.get_io_service());
+
+ // Acceptor object - called when the connection is made, the handler
+ // will initiate a read on the socket.
+ tcp::acceptor acceptor(service_.get_io_service(),
+ tcp::endpoint(tcp::v4(), TEST_PORT));
+ acceptor.async_accept(socket,
+ boost::bind(&IOFetchTest::tcpAcceptHandler, this, &socket, _1));
+
+ // Post the TCP fetch object to send the query and receive the response.
+ service_.get_io_service().post(tcp_fetch_);
+
+ // ... and execute all the callbacks. This exits when the fetch
+ // completes.
+ service_.run();
+ EXPECT_TRUE(run_); // Make sure the callback did execute
+
+ // Tidy up
+ socket.close();
+ }
+
+ /// Perform a send/receive test over UDP
+ ///
+ /// \param bad_qid If true, do the test where the QID is mangled
+ /// in the response
+ /// \param second_send If true, do the test where the QID is
+ /// mangled in the response, but a second
+ /// (correct) packet is used
+ void udpSendReturnTest(bool bad_qid, bool second_send) {
+ protocol_ = IOFetch::UDP;
+
+ // Set up the server.
+ udp::socket socket(service_.get_io_service(), udp::v4());
+ socket.set_option(socket_base::reuse_address(true));
+ socket.bind(udp::endpoint(TEST_HOST, TEST_PORT));
+ return_data_ = "Message returned to the client";
+
+ udp::endpoint remote;
+ socket.async_receive_from(asio::buffer(receive_buffer_, sizeof(receive_buffer_)),
+ remote,
+ boost::bind(&IOFetchTest::udpReceiveHandler, this, &remote, &socket,
+ _1, _2, bad_qid, second_send));
+ service_.get_io_service().post(udp_fetch_);
+ if (debug_) {
+ cout << "udpSendReceive: async_receive_from posted, waiting for callback" <<
+ endl;
+ }
+ service_.run();
+
+ socket.close();
+
+ EXPECT_TRUE(run_);;
+ }
+};
+
+// Check the protocol
+TEST_F(IOFetchTest, Protocol) {
+ EXPECT_EQ(IOFetch::UDP, udp_fetch_.getProtocol());
+ EXPECT_EQ(IOFetch::TCP, tcp_fetch_.getProtocol());
+}
+
+// UDP Stop test - see IOFetchTest::stopTest() header.
+TEST_F(IOFetchTest, UdpStop) {
+ stopTest(IOFetch::UDP, udp_fetch_);
+}
+
+// UDP premature stop test - see IOFetchTest::prematureStopTest() header.
+TEST_F(IOFetchTest, UdpPrematureStop) {
+ prematureStopTest(IOFetch::UDP, udp_fetch_);
+}
+
+// UDP premature stop test - see IOFetchTest::timeoutTest() header.
+TEST_F(IOFetchTest, UdpTimeout) {
+ timeoutTest(IOFetch::UDP, udp_fetch_);
+}
+
+// UDP SendReceive test. Set up a UDP server then ports a UDP fetch object.
+// This will send question_ to the server and receive the answer back from it.
+TEST_F(IOFetchTest, UdpSendReceive) {
+ expected_ = IOFetch::SUCCESS;
+
+ udpSendReturnTest(false, false);
+
+ EXPECT_TRUE(run_);;
+}
+
+TEST_F(IOFetchTest, UdpSendReceiveBadQid) {
+ expected_ = IOFetch::TIME_OUT;
+
+ udpSendReturnTest(true, false);
+
+ EXPECT_TRUE(run_);;
+}
+
+TEST_F(IOFetchTest, UdpSendReceiveBadQidResend) {
+ expected_ = IOFetch::SUCCESS;
+
+ udpSendReturnTest(true, true);
+
+ EXPECT_TRUE(run_);;
+}
+
+// Do the same tests for TCP transport
+
+TEST_F(IOFetchTest, TcpStop) {
+ stopTest(IOFetch::TCP, tcp_fetch_);
+}
+
+TEST_F(IOFetchTest, TcpPrematureStop) {
+ prematureStopTest(IOFetch::TCP, tcp_fetch_);
+}
+
+TEST_F(IOFetchTest, TcpTimeout) {
+ timeoutTest(IOFetch::TCP, tcp_fetch_);
+}
+
+// Test with values at or near 2, then at or near the chunk size (16 and 32
+// bytes, the sizes of the first two packets) then up to 65535. These are done
+// in separate tests because in practice a new IOFetch is created for each
+// query/response exchange and we don't want to confuse matters in the test
+// by running the test with an IOFetch that has already done one exchange.
+//
+// Don't do 0 or 1; the server would not accept the packet
+// (since the length is too short to check the qid)
+TEST_F(IOFetchTest, TcpSendReceive2) {
+ tcpSendReturnTest(test_data_.substr(0, 2));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive3) {
+ tcpSendReturnTest(test_data_.substr(0, 3));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive15) {
+ tcpSendReturnTest(test_data_.substr(0, 15));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive16) {
+ tcpSendReturnTest(test_data_.substr(0, 16));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive17) {
+ tcpSendReturnTest(test_data_.substr(0, 17));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive31) {
+ tcpSendReturnTest(test_data_.substr(0, 31));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive32) {
+ tcpSendReturnTest(test_data_.substr(0, 32));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive33) {
+ tcpSendReturnTest(test_data_.substr(0, 33));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive4096) {
+ tcpSendReturnTest(test_data_.substr(0, 4096));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive8192) {
+ tcpSendReturnTest(test_data_.substr(0, 8192));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive16384) {
+ tcpSendReturnTest(test_data_.substr(0, 16384));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive32768) {
+ tcpSendReturnTest(test_data_.substr(0, 32768));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive65535) {
+ tcpSendReturnTest(test_data_.substr(0, 65535));
+}
+
+TEST_F(IOFetchTest, TcpSendReceive2ShortSend) {
+ tcpSendReturnTest(test_data_.substr(0, 2), true);
+}
+
+TEST_F(IOFetchTest, TcpSendReceive15ShortSend) {
+ tcpSendReturnTest(test_data_.substr(0, 15), true);
+}
+
+TEST_F(IOFetchTest, TcpSendReceive8192ShortSend) {
+ tcpSendReturnTest(test_data_.substr(0, 8192), true);
+}
+
+
+} // namespace asiodns
+} // namespace isc
diff --git a/src/lib/asiodns/tests/io_service_unittest.cc b/src/lib/asiodns/tests/io_service_unittest.cc
new file mode 100644
index 0000000..cc64022
--- /dev/null
+++ b/src/lib/asiodns/tests/io_service_unittest.cc
@@ -0,0 +1,118 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <gtest/gtest.h>
+
+#include <asio.hpp>
+#include <asiolink/asiolink.h>
+#include <asiodns/asiodns.h>
+
+using namespace isc::asiolink;
+using namespace isc::asiodns;
+
+const char* const TEST_SERVER_PORT = "53535";
+const char* const TEST_CLIENT_PORT = "53536";
+const char* const TEST_IPV6_ADDR = "::1";
+const char* const TEST_IPV4_ADDR = "127.0.0.1";
+
+TEST(IOServiceTest, badPort) {
+ IOService io_service;
+ EXPECT_THROW(DNSService(io_service, *"65536", true, false, NULL, NULL, NULL), IOError);
+ EXPECT_THROW(DNSService(io_service, *"53210.0", true, false, NULL, NULL, NULL), IOError);
+ EXPECT_THROW(DNSService(io_service, *"-1", true, false, NULL, NULL, NULL), IOError);
+ EXPECT_THROW(DNSService(io_service, *"domain", true, false, NULL, NULL, NULL), IOError);
+}
+
+TEST(IOServiceTest, badAddress) {
+ IOService io_service;
+ EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.1.1", NULL, NULL, NULL), IOError);
+ EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"2001:db8:::1", NULL, NULL, NULL), IOError);
+ EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"localhost", NULL, NULL, NULL), IOError);
+}
+
+TEST(IOServiceTest, unavailableAddress) {
+ IOService io_service;
+ // These addresses should generally be unavailable as a valid local
+ // address, although there's no guarantee in theory.
+ EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.0", NULL, NULL, NULL), IOError);
+
+ // Some OSes would simply reject binding attempt for an AF_INET6 socket
+ // to an IPv4-mapped IPv6 address. Even if those that allow it, since
+ // the corresponding IPv4 address is the same as the one used in the
+ // AF_INET socket case above, it should at least show the same result
+ // as the previous one.
+ EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:192.0.2.0", NULL, NULL, NULL), IOError);
+}
+
+TEST(IOServiceTest, duplicateBind_v6) {
+ // In each sub test case, second attempt should fail due to duplicate bind
+ IOService io_service;
+
+ // IPv6, "any" address
+ DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL);
+ EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL), IOError);
+ delete dns_service;
+
+}
+
+TEST(IOServiceTest, duplicateBind_v6_address) {
+ // In each sub test case, second attempt should fail due to duplicate bind
+ IOService io_service;
+
+ // IPv6, specific address
+ DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL);
+ EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL), IOError);
+ delete dns_service;
+
+}
+
+TEST(IOServiceTest, duplicateBind_v4) {
+ // In each sub test case, second attempt should fail due to duplicate bind
+ IOService io_service;
+
+ // IPv4, "any" address
+ DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL);
+ EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL), IOError);
+ delete dns_service;
+
+}
+
+TEST(IOServiceTest, duplicateBind_v4_address) {
+ // In each sub test case, second attempt should fail due to duplicate bind
+ IOService io_service;
+
+ // IPv4, specific address
+ DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL);
+ EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL), IOError);
+ delete dns_service;
+}
+
+// Disabled because IPv4-mapped addresses don't seem to be working with
+// the IOService constructor
+TEST(IOServiceTest, DISABLED_IPv4MappedDuplicateBind) {
+ IOService io_service;
+ // Duplicate bind on IPv4-mapped IPv6 address
+ DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL);
+ EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL), IOError);
+ delete dns_service;
+
+ // XXX:
+ // Currently, this throws an "invalid argument" exception. I have
+ // not been able to get IPv4-mapped addresses to work.
+ dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL);
+ EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL), IOError);
+ delete dns_service;
+}
+
diff --git a/src/lib/asiodns/tests/run_unittests.cc b/src/lib/asiodns/tests/run_unittests.cc
new file mode 100644
index 0000000..df77368
--- /dev/null
+++ b/src/lib/asiodns/tests/run_unittests.cc
@@ -0,0 +1,29 @@
+// Copyright (C) 2009 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 <util/unittests/run_all.h>
+
+#include <log/logger_manager.h>
+#include <dns/tests/unittest_util.h>
+
+int
+main(int argc, char* argv[])
+{
+ ::testing::InitGoogleTest(&argc, argv); // Initialize Google test
+ isc::log::LoggerManager::init("unittest"); // Set a root logger name
+ isc::UnitTestUtil::addDataPath(TEST_DATA_DIR); // Add location of test data
+
+ return (isc::util::unittests::run_all());
+}
diff --git a/src/lib/asiodns/udp_server.cc b/src/lib/asiodns/udp_server.cc
new file mode 100644
index 0000000..f103e5a
--- /dev/null
+++ b/src/lib/asiodns/udp_server.cc
@@ -0,0 +1,325 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h> // for some IPC/network system calls
+#include <errno.h>
+
+#include <boost/shared_array.hpp>
+
+#include <config.h>
+
+#include <log/dummylog.h>
+
+#include <asio.hpp>
+#include <asio/error.hpp>
+#include <asiolink/dummy_io_cb.h>
+#include <asiolink/udp_endpoint.h>
+#include <asiolink/udp_socket.h>
+#include "udp_server.h"
+
+#include <dns/opcode.h>
+
+using namespace asio;
+using asio::ip::udp;
+using isc::log::dlog;
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::util;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace asiodns {
+
+/*
+ * Some of the member variables here are shared_ptrs and some are
+ * auto_ptrs. There will be one instance of Data for the lifetime
+ * of packet. The variables that are state only for a single packet
+ * use auto_ptr, as it is more lightweight. In the case of shared
+ * configuration (eg. the callbacks, socket), we use shared_ptrs.
+ */
+struct UDPServer::Data {
+ /*
+ * Constructor from parameters passed to UDPServer constructor.
+ * This instance will not be used to retrieve and answer the actual
+ * query, it will only hold parameters until we wait for the
+ * first packet. But we do initialize the socket in here.
+ */
+ Data(io_service& io_service, const ip::address& addr, const uint16_t port,
+ SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) :
+ io_(io_service), done_(false),
+ checkin_callback_(checkin),lookup_callback_(lookup),
+ answer_callback_(answer)
+ {
+ // We must use different instantiations for v4 and v6;
+ // otherwise ASIO will bind to both
+ udp proto = addr.is_v4() ? udp::v4() : udp::v6();
+ socket_.reset(new udp::socket(io_service, proto));
+ socket_->set_option(socket_base::reuse_address(true));
+ if (addr.is_v6()) {
+ socket_->set_option(asio::ip::v6_only(true));
+ }
+ socket_->bind(udp::endpoint(addr, port));
+ }
+
+ /*
+ * Copy constructor. Default one would probably do, but it is unnecessary
+ * to copy many of the member variables every time we fork to handle
+ * another packet.
+ *
+ * We also allocate data for receiving the packet here.
+ */
+ Data(const Data& other) :
+ io_(other.io_), socket_(other.socket_), done_(false),
+ checkin_callback_(other.checkin_callback_),
+ lookup_callback_(other.lookup_callback_),
+ answer_callback_(other.answer_callback_)
+ {
+ // Instantiate the data buffer and endpoint that will
+ // be used by the asynchronous receive call.
+ data_.reset(new char[MAX_LENGTH]);
+ sender_.reset(new udp::endpoint());
+ }
+
+ // The ASIO service object
+ asio::io_service& io_;
+
+ // Class member variables which are dynamic, and changes to which
+ // need to accessible from both sides of a coroutine fork or from
+ // outside of the coroutine (i.e., from an asynchronous I/O call),
+ // should be declared here as pointers and allocated in the
+ // constructor or in the coroutine. This allows state information
+ // to persist when an individual copy of the coroutine falls out
+ // scope while waiting for an event, *so long as* there is another
+ // object that is referencing the same data. As a side-benefit, using
+ // pointers also reduces copy overhead for coroutine objects.
+ //
+ // Note: Currently these objects are allocated by "new" in the
+ // constructor, or in the function operator while processing a query.
+ // Repeated allocations from the heap for every incoming query is
+ // clearly a performance issue; this must be optimized in the future.
+ // The plan is to have a structure pre-allocate several "Data"
+ // objects which can be pulled off a free list and placed on an in-use
+ // list whenever a query comes in. This will serve the dual purpose
+ // of improving performance and guaranteeing that state information
+ // will *not* be destroyed when any one instance of the coroutine
+ // falls out of scope while waiting for an event.
+ //
+ // Socket used to for listen for queries. Created in the
+ // constructor and stored in a shared_ptr because socket objects
+ // are not copyable.
+ boost::shared_ptr<asio::ip::udp::socket> socket_;
+
+ // The ASIO-internal endpoint object representing the client
+ std::auto_ptr<asio::ip::udp::endpoint> sender_;
+
+ // \c IOMessage and \c Message objects to be passed to the
+ // DNS lookup and answer providers
+ std::auto_ptr<asiolink::IOMessage> io_message_;
+
+ // The original query as sent by the client
+ isc::dns::MessagePtr query_message_;
+
+ // The response message we are building
+ isc::dns::MessagePtr answer_message_;
+
+ // The buffer into which the response is written
+ isc::util::OutputBufferPtr respbuf_;
+
+ // The buffer into which the query packet is written
+ boost::shared_array<char> data_;
+
+ // State information that is entirely internal to a given instance
+ // of the coroutine can be declared here.
+ size_t bytes_;
+ bool done_;
+
+
+ // Callback functions provided by the caller
+ const SimpleCallback* checkin_callback_;
+ const DNSLookup* lookup_callback_;
+ const DNSAnswer* answer_callback_;
+
+ std::auto_ptr<IOEndpoint> peer_;
+ std::auto_ptr<IOSocket> iosock_;
+};
+
+/// The following functions implement the \c UDPServer class.
+///
+/// The constructor. It just creates new internal state object
+/// and lets it handle the initialization.
+UDPServer::UDPServer(io_service& io_service, const ip::address& addr,
+ const uint16_t port, SimpleCallback* checkin, DNSLookup* lookup,
+ DNSAnswer* answer) :
+ data_(new Data(io_service, addr, port, checkin, lookup, answer))
+{ }
+
+/// The function operator is implemented with the "stackless coroutine"
+/// pattern; see internal/coroutine.h for details.
+void
+UDPServer::operator()(error_code ec, size_t length) {
+ /// Because the coroutine reentry block is implemented as
+ /// a switch statement, inline variable declarations are not
+ /// permitted. Certain variables used below can be declared here.
+
+ CORO_REENTER (this) {
+ do {
+ /*
+ * This is preparation for receiving a packet. We get a new
+ * state object for the lifetime of the next packet to come.
+ * It allocates the buffers to receive data into.
+ */
+ data_.reset(new Data(*data_));
+
+ do {
+ // Begin an asynchronous receive, then yield.
+ // When the receive event is posted, the coroutine
+ // will resume immediately after this point.
+ CORO_YIELD data_->socket_->async_receive_from(
+ buffer(data_->data_.get(), MAX_LENGTH), *data_->sender_,
+ *this);
+
+ // Abort on fatal errors
+ // TODO: add log
+ if (ec) {
+ using namespace asio::error;
+ if (ec.value() != would_block && ec.value() != try_again &&
+ ec.value() != interrupted) {
+ return;
+ }
+ }
+
+ } while (ec || length == 0);
+
+ data_->bytes_ = length;
+
+ /*
+ * We fork the coroutine now. One (the child) will keep
+ * the current state and handle the packet, then die and
+ * drop ownership of the state. The other (parent) will just
+ * go into the loop again and replace the current state with
+ * a new one for a new object.
+ *
+ * Actually, both of the coroutines will be a copy of this
+ * one, but that's just internal implementation detail.
+ */
+ CORO_FORK data_->io_.post(UDPServer(*this));
+ } while (is_parent());
+
+ // Create an \c IOMessage object to store the query.
+ //
+ // (XXX: It would be good to write a factory function
+ // that would quickly generate an IOMessage object without
+ // all these calls to "new".)
+ data_->peer_.reset(new UDPEndpoint(*data_->sender_));
+
+ // The UDP socket class has been extended with asynchronous functions
+ // and takes as a template parameter a completion callback class. As
+ // UDPServer does not use these extended functions (only those defined
+ // in the IOSocket base class) - but needs a UDPSocket to get hold of
+ // the underlying Boost UDP socket - DummyIOCallback is used. This
+ // provides the appropriate operator() but is otherwise functionless.
+ data_->iosock_.reset(
+ new UDPSocket<DummyIOCallback>(*data_->socket_));
+
+ data_->io_message_.reset(new IOMessage(data_->data_.get(),
+ data_->bytes_, *data_->iosock_, *data_->peer_));
+
+ // Perform any necessary operations prior to processing an incoming
+ // query (e.g., checking for queued configuration messages).
+ //
+ // (XXX: it may be a performance issue to check in for every single
+ // incoming query; we may wish to throttle this in the future.)
+ if (data_->checkin_callback_ != NULL) {
+ (*data_->checkin_callback_)(*data_->io_message_);
+ }
+
+ // If we don't have a DNS Lookup provider, there's no point in
+ // continuing; we exit the coroutine permanently.
+ if (data_->lookup_callback_ == NULL) {
+ CORO_YIELD return;
+ }
+
+ // Instantiate objects that will be needed by the
+ // asynchronous DNS lookup and/or by the send call.
+ data_->respbuf_.reset(new OutputBuffer(0));
+ data_->query_message_.reset(new Message(Message::PARSE));
+ data_->answer_message_.reset(new Message(Message::RENDER));
+
+ // Schedule a DNS lookup, and yield. When the lookup is
+ // finished, the coroutine will resume immediately after
+ // this point.
+ CORO_YIELD data_->io_.post(AsyncLookup<UDPServer>(*this));
+
+ // The 'done_' flag indicates whether we have an answer
+ // to send back. If not, exit the coroutine permanently.
+ if (!data_->done_) {
+ CORO_YIELD return;
+ }
+
+ // Call the DNS answer provider to render the answer into
+ // wire format
+ (*data_->answer_callback_)(*data_->io_message_, data_->query_message_,
+ data_->answer_message_, data_->respbuf_);
+
+ // Begin an asynchronous send, and then yield. When the
+ // send completes, we will resume immediately after this point
+ // (though we have nothing further to do, so the coroutine
+ // will simply exit at that time).
+ CORO_YIELD data_->socket_->async_send_to(
+ buffer(data_->respbuf_->getData(), data_->respbuf_->getLength()),
+ *data_->sender_, *this);
+ }
+}
+
+/// Call the DNS lookup provider. (Expected to be called by the
+/// AsyncLookup<UDPServer> handler.)
+void
+UDPServer::asyncLookup() {
+ (*data_->lookup_callback_)(*data_->io_message_,
+ data_->query_message_, data_->answer_message_, data_->respbuf_, this);
+}
+
+/// Stop the UDPServer
+void
+UDPServer::stop() {
+ /// Using close instead of cancel, because cancel
+ /// will only cancel the asynchornized event already submitted
+ /// to io service, the events post to io service after
+ /// cancel still can be scheduled by io service, if
+ /// the socket is cloesed, all the asynchronized event
+ /// for it won't be scheduled by io service not matter it is
+ /// submit to io serice before or after close call. And we will
+ //. get bad_descriptor error
+ data_->socket_->close();
+}
+
+/// Post this coroutine on the ASIO service queue so that it will
+/// resume processing where it left off. The 'done' parameter indicates
+/// whether there is an answer to return to the client.
+void
+UDPServer::resume(const bool done) {
+ data_->done_ = done;
+ data_->io_.post(*this);
+}
+
+bool
+UDPServer::hasAnswer() {
+ return (data_->done_);
+}
+
+} // namespace asiodns
+} // namespace isc
diff --git a/src/lib/asiodns/udp_server.h b/src/lib/asiodns/udp_server.h
new file mode 100644
index 0000000..4c19544
--- /dev/null
+++ b/src/lib/asiodns/udp_server.h
@@ -0,0 +1,108 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __UDP_SERVER_H
+#define __UDP_SERVER_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <asiolink/simple_callback.h>
+#include <asiodns/dns_answer.h>
+#include <asiodns/dns_lookup.h>
+#include <asiodns/dns_server.h>
+
+#include <coroutine.h>
+
+namespace isc {
+namespace asiodns {
+
+//
+// Asynchronous UDP server coroutine
+//
+///
+/// \brief This class implements the coroutine to handle UDP
+/// DNS query event. As such, it is both a \c DNSServer and
+/// a \c coroutine
+///
+class UDPServer : public virtual DNSServer, public virtual coroutine {
+public:
+ /// \brief Constructor
+ /// \param io_service the asio::io_service to work with
+ /// \param addr the IP address to listen for queries on
+ /// \param port the port to listen for queries on
+ /// \param checkin the callbackprovider for non-DNS events
+ /// \param lookup the callbackprovider for DNS lookup events
+ /// \param answer the callbackprovider for DNS answer events
+ explicit UDPServer(asio::io_service& io_service,
+ const asio::ip::address& addr, const uint16_t port,
+ isc::asiolink::SimpleCallback* checkin = NULL,
+ DNSLookup* lookup = NULL,
+ DNSAnswer* answer = NULL);
+
+ /// \brief The function operator
+ void operator()(asio::error_code ec = asio::error_code(),
+ size_t length = 0);
+
+ /// \brief Calls the lookup callback
+ void asyncLookup();
+
+ /// \brief Stop the running server
+ /// \note once the server stopped, it can't restart
+ void stop();
+
+ /// \brief Resume operation
+ ///
+ /// \param done Set this to true if the lookup action is done and
+ /// we have an answer
+ void resume(const bool done);
+
+ /// \brief Check if we have an answer
+ ///
+ /// \return true if we have an answer
+ bool hasAnswer();
+
+ /// \brief Returns the coroutine state value
+ ///
+ /// \return the coroutine state value
+ int value() { return (get_value()); }
+
+ /// \brief Clones the object
+ ///
+ /// \return a newly allocated copy of this object
+ DNSServer* clone() {
+ UDPServer* s = new UDPServer(*this);
+ return (s);
+ }
+
+private:
+ enum { MAX_LENGTH = 4096 };
+
+ /**
+ * \brief Internal state and data.
+ *
+ * We use the pimple design pattern, but not because we need to hide
+ * internal data. This class and whole header is for private use anyway.
+ * It turned out that UDPServer is copied a lot, because it is a coroutine.
+ * This way the overhead of copying is lower, we copy only one shared
+ * pointer instead of about 10 of them.
+ */
+ class Data;
+ boost::shared_ptr<Data> data_;
+};
+
+} // namespace asiodns
+} // namespace isc
+#endif // __UDP_SERVER_H
diff --git a/src/lib/asiolink/Makefile.am b/src/lib/asiolink/Makefile.am
index 2fda728..22b3a8e 100644
--- a/src/lib/asiolink/Makefile.am
+++ b/src/lib/asiolink/Makefile.am
@@ -2,7 +2,6 @@ SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -13,33 +12,21 @@ CLEANFILES = *.gcno *.gcda
# which would make the build fail with -Werror (our default setting).
lib_LTLIBRARIES = libasiolink.la
libasiolink_la_SOURCES = asiolink.h
-libasiolink_la_SOURCES += asiolink_utilities.h
-libasiolink_la_SOURCES += asiodef.cc asiodef.h
-libasiolink_la_SOURCES += dns_answer.h
-libasiolink_la_SOURCES += dns_lookup.h
-libasiolink_la_SOURCES += dns_server.h
-libasiolink_la_SOURCES += dns_service.cc dns_service.h
libasiolink_la_SOURCES += dummy_io_cb.h
libasiolink_la_SOURCES += interval_timer.cc interval_timer.h
libasiolink_la_SOURCES += io_address.cc io_address.h
libasiolink_la_SOURCES += io_asio_socket.h
libasiolink_la_SOURCES += io_endpoint.cc io_endpoint.h
libasiolink_la_SOURCES += io_error.h
-libasiolink_la_SOURCES += io_fetch.cc io_fetch.h
libasiolink_la_SOURCES += io_message.h
-libasiolink_la_SOURCES += qid_gen.cc qid_gen.h
libasiolink_la_SOURCES += io_service.h io_service.cc
libasiolink_la_SOURCES += io_socket.h io_socket.cc
libasiolink_la_SOURCES += simple_callback.h
libasiolink_la_SOURCES += tcp_endpoint.h
-libasiolink_la_SOURCES += tcp_server.cc tcp_server.h
libasiolink_la_SOURCES += tcp_socket.h
libasiolink_la_SOURCES += udp_endpoint.h
-libasiolink_la_SOURCES += udp_server.cc udp_server.h
libasiolink_la_SOURCES += udp_socket.h
-EXTRA_DIST = asiodef.msg
-
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
libasiolink_la_CXXFLAGS = $(AM_CXXFLAGS)
diff --git a/src/lib/asiolink/README b/src/lib/asiolink/README
index 6bd1a73..b9e38f9 100644
--- a/src/lib/asiolink/README
+++ b/src/lib/asiolink/README
@@ -16,167 +16,14 @@ including:
them in only one place allows us to relax strictness here, while
leaving it in place elsewhere.
-Currently, the asiolink library only supports DNS servers (i.e., b10-auth
-and b10-resolver). The plan is to make it more generic and allow it to
-support other modules as well.
-
Some of the classes defined here--for example, IOSocket, IOEndpoint,
and IOAddress--are to be used by BIND 10 modules as wrappers around
ASIO-specific classes.
-Other classes implement the DNS protocol on behalf of BIND 10 modules.
-
-These DNS server and client routines are written using the "stackless
-coroutine" pattern invented by Chris Kohlhoff and described at
-http://blog.think-async.com/2010/03/potted-guide-to-stackless-coroutines.html.
-This is intended to simplify development a bit, since it allows the
-routines to be written in a straightfowrard step-step-step fashion rather
-than as a complex chain of separate handler functions.
-
-Coroutine objects (i.e., UDPServer, TCPServer and IOFetch) are objects
-with reenterable operator() members. When an instance of one of these
-classes is called as a function, it resumes at the position where it left
-off. Thus, a UDPServer can issue an asynchronous I/O call and specify
-itself as the handler object; when the call completes, the UDPServer
-carries on at the same position. As a result, the code can look as
-if it were using synchronous, not asynchronous, I/O, providing some of
-the benefit of threading but with minimal switching overhead.
-
-So, in simplified form, the behavior of a DNS Server is:
-
- REENTER:
- while true:
- YIELD packet = read_packet
- FORK
- if not parent:
- break
-
- # This callback informs the caller that a packet has arrived, and
- # gives it a chance to update configuration, etc
- SimpleCallback(packet)
- YIELD answer = DNSLookup(packet, this)
- response = DNSAnswer(answer)
- YIELD send(response)
-
-At each "YIELD" point, the coroutine initiates an asynchronous operation,
-then pauses and turns over control to some other task on the ASIO service
-queue. When the operation completes, the coroutine resumes.
-
-DNSLookup, DNSAnswer and SimpleCallback define callback methods
-used by a DNS Server to communicate with the module that called it.
-They are abstract-only classes whose concrete implementations
-are supplied by the calling module.
-
-The DNSLookup callback always runs asynchronously. Concrete
-implementations must be sure to call the server's "resume" method when
-it is finished.
-
-In an authoritative server, the DNSLookup implementation would examine
-the query, look up the answer, then call "resume". (See the diagram
-in doc/auth_process.jpg.)
-
-In a recursive server, the DNSLookup impelemtation would initiate a
-DNSQuery, which in turn would be responsible for calling the server's
-"resume" method. (See the diagram in doc/recursive_process.jpg.)
-
-A DNSQuery object is intended to handle resolution of a query over
-the network when the local authoritative data sources or cache are not
-sufficient. The plan is that it will make use of subsidiary DNSFetch
-calls to get data from particular authoritative servers, and when it has
-gotten a complete answer, it calls "resume".
-
-In current form, however, DNSQuery is much simpler; it forwards queries
-to a single upstream resolver and passes the answers back to the client.
-It is constructed with the address of the forward server. Queries are
-initiated with the question to ask the forward server, a buffer into
-which to write the answer, and a pointer to the coroutine to be resumed
-when the answer has arrived. In simplified form, the DNSQuery routine is:
-
- REENTER:
- render the question into a wire-format query packet
- YIELD send(query)
- YIELD response = read_packet
- server->resume
-Currently, DNSQuery is only implemented for UDP queries. In future work
-it will be necessary to write code to fall back to TCP when circumstances
-require it.
-
-
-Upstream Fetches
-================
-Upstream fetches (queries by the resolver on behalf of a client) are made
-using a slightly-modified version of the pattern described above.
-
-Sockets
+Logging
-------
-First, it will be useful to understand the class hierarchy used in the
-fetch logic:
-
- IOSocket
- |
- IOAsioSocket
- |
- +-----+-----+
- | |
-UDPSocket TCPSocket
-
-IOSocket is a wrapper class for a socket and is used by the authoritative
-server code. It is an abstract base class, providing little more that the ability to hold the socket and to return the protocol in use.
-
-Built on this is IOAsioSocket, which adds the open, close, asyncSend and
-asyncReceive methods. This is a template class, which takes as template
-argument the class of the object that will be used as the callback when the
-asynchronous operation completes. This object can be of any type, but must
-include an operator() method with the signature:
-
- operator()(asio::error_code ec, size_t length)
-
-... the two arguments being the status of the completed I/O operation and
-the number of bytes transferred. (In the case of the open method, the second
-argument will be zero.)
-
-Finally, the TCPSocket and UDPSocket classes provide the body of the
-asynchronous operations.
-
-Fetch Sequence
---------------
-The fetch is implemented by the IOFetch class, which takes as argument the
-protocol to use. The sequence is:
-
- REENTER:
- render the question into a wire-format query packet
- open() // Open socket and optionally connect
- if (! synchronous) {
- YIELD;
- }
- YIELD asyncSend(query) // Send query
- do {
- YIELD asyncReceive(response) // Read response
- } while (! complete(response))
- close() // Drop connection and close socket
- server->resume
-
-The open() method opens a socket for use. On TCP, it also makes a
-connection to the remote end. So under UDP the operation will complete
-immediately, but under TCP it could take a long time. One solution would be
-for the open operation to post an event to the I/O queue; then both cases
-could be regarded as being equivalent, with the completion being signalled
-by the posting of the completion event. However UDP is the most common case
-and that would involve extra overhead. So the open() returns a status
-indicating whether the operation completed asynchronously. If it did, the
-code yields back to the coroutine; if not the yield is bypassed.
-
-The asynchronous send is straightforward, invoking the underlying ASIO
-function. (Note that the address/port is supplied to both the open() and
-asyncSend() methods - it is used by the TCPSocket in open() and by the
-UDPSocket in asyncSend().)
-
-The asyncReceive() method issues an asynchronous read and waits for completion.
-The fetch object keeps track of the amount of data received so far and when
-the receive completes it calls a method on the socket to determine if the
-entire message has been received. (This will always be the case for UDP. On
-TCP though, the message is preceded by a count field as several reads may be
-required to read all the data.) The fetch loops until all the data is read.
-Finally, the socket is closed and the server called to resume operation.
+At this point, nothing is logged by this low-level library. We may
+revisit that in the future, if we find suitable messages to log, but
+right now there are also no loggers initialized or called.
diff --git a/src/lib/asiolink/asiodef.cc b/src/lib/asiolink/asiodef.cc
deleted file mode 100644
index 94c71b5..0000000
--- a/src/lib/asiolink/asiodef.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// File created from asiodef.msg on Mon Feb 28 17:15:30 2011
-
-#include <cstddef>
-#include <log/message_types.h>
-#include <log/message_initializer.h>
-
-namespace asiolink {
-
-extern const isc::log::MessageID ASIO_FETCHCOMP = "FETCHCOMP";
-extern const isc::log::MessageID ASIO_FETCHSTOP = "FETCHSTOP";
-extern const isc::log::MessageID ASIO_OPENSOCK = "OPENSOCK";
-extern const isc::log::MessageID ASIO_RECVSOCK = "RECVSOCK";
-extern const isc::log::MessageID ASIO_RECVTMO = "RECVTMO";
-extern const isc::log::MessageID ASIO_SENDSOCK = "SENDSOCK";
-extern const isc::log::MessageID ASIO_UNKORIGIN = "UNKORIGIN";
-extern const isc::log::MessageID ASIO_UNKRESULT = "UNKRESULT";
-
-} // namespace asiolink
-
-namespace {
-
-const char* values[] = {
- "FETCHCOMP", "upstream fetch to %s(%d) has now completed",
- "FETCHSTOP", "upstream fetch to %s(%d) has been stopped",
- "OPENSOCK", "error %d opening %s socket to %s(%d)",
- "RECVSOCK", "error %d reading %s data from %s(%d)",
- "RECVTMO", "receive timeout while waiting for data from %s(%d)",
- "SENDSOCK", "error %d sending data using %s to %s(%d)",
- "UNKORIGIN", "unknown origin for ASIO error code %d (protocol: %s, address %s)",
- "UNKRESULT", "unknown result (%d) when IOFetch::stop() was executed for I/O to %s(%d)",
- NULL
-};
-
-const isc::log::MessageInitializer initializer(values);
-
-} // Anonymous namespace
-
diff --git a/src/lib/asiolink/asiodef.h b/src/lib/asiolink/asiodef.h
deleted file mode 100644
index ba77817..0000000
--- a/src/lib/asiolink/asiodef.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// File created from asiodef.msg on Mon Feb 28 17:15:30 2011
-
-#ifndef __ASIODEF_H
-#define __ASIODEF_H
-
-#include <log/message_types.h>
-
-namespace asiolink {
-
-extern const isc::log::MessageID ASIO_FETCHCOMP;
-extern const isc::log::MessageID ASIO_FETCHSTOP;
-extern const isc::log::MessageID ASIO_OPENSOCK;
-extern const isc::log::MessageID ASIO_RECVSOCK;
-extern const isc::log::MessageID ASIO_RECVTMO;
-extern const isc::log::MessageID ASIO_SENDSOCK;
-extern const isc::log::MessageID ASIO_UNKORIGIN;
-extern const isc::log::MessageID ASIO_UNKRESULT;
-
-} // namespace asiolink
-
-#endif // __ASIODEF_H
diff --git a/src/lib/asiolink/asiodef.msg b/src/lib/asiolink/asiodef.msg
deleted file mode 100644
index 2fcadd1..0000000
--- a/src/lib/asiolink/asiodef.msg
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
-
-$PREFIX ASIO_
-$NAMESPACE asiolink
-
-FETCHCOMP upstream fetch to %s(%d) has now completed
-+ A debug message, this records the the upstream fetch (a query made by the
-+ resolver on behalf of its client) to the specified address has completed.
-
-FETCHSTOP upstream fetch to %s(%d) has been stopped
-+ An external component has requested the halting of an upstream fetch. This
-+ is an allowed operation, and the message should only appear if debug is
-+ enabled.
-
-OPENSOCK error %d opening %s socket to %s(%d)
-+ The asynchronous I/O code encountered an error when trying to open a socket
-+ of the specified protocol in order to send a message to the target address.
-+ The the number of the system error that cause the problem is given in the
-+ message.
-
-RECVSOCK error %d reading %s data from %s(%d)
-+ The asynchronous I/O code encountered an error when trying read data from
-+ the specified address on the given protocol. The the number of the system
-+ error that cause the problem is given in the message.
-
-SENDSOCK error %d sending data using %s to %s(%d)
-+ The asynchronous I/O code encountered an error when trying send data to
-+ the specified address on the given protocol. The the number of the system
-+ error that cause the problem is given in the message.
-
-RECVTMO receive timeout while waiting for data from %s(%d)
-+ An upstream fetch from the specified address timed out. This may happen for
-+ any number of reasons and is most probably a problem at the remote server
-+ or a problem on the network. The message will only appear if debug is
-+ enabled.
-
-UNKORIGIN unknown origin for ASIO error code %d (protocol: %s, address %s)
-+ This message should not appear and indicates an internal error if it does.
-+ Please enter a bug report.
-
-UNKRESULT unknown result (%d) when IOFetch::stop() was executed for I/O to %s(%d)
-+ The termination method of the resolver's upstream fetch class was called with
-+ an unknown result code (which is given in the message). This message should
-+ not appear and may indicate an internal error. Please enter a bug report.
diff --git a/src/lib/asiolink/asiolink.h b/src/lib/asiolink/asiolink.h
index 6e8fe84..51d3a14 100644
--- a/src/lib/asiolink/asiolink.h
+++ b/src/lib/asiolink/asiolink.h
@@ -20,10 +20,6 @@
// See the description of the namespace below.
#include <asiolink/io_service.h>
-#include <asiolink/dns_service.h>
-#include <asiolink/dns_server.h>
-#include <asiolink/dns_lookup.h>
-#include <asiolink/dns_answer.h>
#include <asiolink/simple_callback.h>
#include <asiolink/interval_timer.h>
@@ -62,11 +58,6 @@
/// this module. The resulting interfaces are thus straightforward mapping
/// to the ASIO counterparts.
///
-/// Notes to developers:
-/// Currently the wrapper interface is fairly specific to use by a
-/// DNS server, i.e., b10-auth or b10-resolver. But the plan is to
-/// generalize it and have other modules use it as well.
-///
/// One obvious drawback of this approach is performance overhead
/// due to the additional layer. We should eventually evaluate the cost
/// of the wrapper abstraction in benchmark tests. Another drawback is
diff --git a/src/lib/asiolink/asiolink_utilities.h b/src/lib/asiolink/asiolink_utilities.h
deleted file mode 100644
index 659e6a0..0000000
--- a/src/lib/asiolink/asiolink_utilities.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __ASIOLINK_UTILITIES_H
-#define __ASIOLINK_UTILITIES_H
-
-#include <cstddef>
-
-namespace asiolink {
-
-/// \brief Read Unsigned 16-Bit Integer from Buffer
-///
-/// This is essentially a copy of the isc::dns::InputBuffer::readUint16. It
-/// should really be moved into a separate library.
-///
-/// \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.
-///
-/// \return Value of 16-bit integer
-inline uint16_t
-readUint16(const void* buffer) {
- const uint8_t* byte_buffer = static_cast<const uint8_t*>(buffer);
-
- uint16_t result = (static_cast<uint16_t>(byte_buffer[0])) << 8;
- result |= static_cast<uint16_t>(byte_buffer[1]);
-
- return (result);
-}
-
-/// \brief Write Unisgned 16-Bit Integer to Buffer
-///
-/// This is essentially a copy of isc::dns::OutputBuffer::writeUint16. It
-/// should really be moved into a separate library.
-///
-/// \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.
-
-inline void
-writeUint16(uint16_t value, void* buffer) {
- uint8_t* byte_buffer = static_cast<uint8_t*>(buffer);
-
- byte_buffer[0] = static_cast<uint8_t>((value & 0xff00U) >> 8);
- byte_buffer[1] = static_cast<uint8_t>(value & 0x00ffU);
-}
-
-} // namespace asiolink
-
-#endif // __ASIOLINK_UTILITIES_H
diff --git a/src/lib/asiolink/dns_answer.h b/src/lib/asiolink/dns_answer.h
deleted file mode 100644
index 84e1f6f..0000000
--- a/src/lib/asiolink/dns_answer.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __ASIOLINK_DNS_ANSWER_H
-#define __ASIOLINK_DNS_ANSWER_H 1
-
-#include <asiolink/io_message.h>
-
-namespace asiolink {
-
-/// \brief The \c DNSAnswer class is an abstract base class for a DNS
-/// Answer provider function.
-///
-/// Specific derived class implementations are hidden within the
-/// implementation. Instances of the derived classes can be called
-/// as functions via the operator() interface. Pointers to these
-/// instances can then be provided to the \c IOService class
-/// via its constructor.
-///
-/// A DNS Answer provider function takes answer data that has been obtained
-/// from a DNS Lookup provider functon and readies it to be sent to the
-/// client. After it has run, the OutputBuffer object passed to it should
-/// contain the answer to the query rendered into wire format.
-class DNSAnswer {
- ///
- /// \name Constructors and Destructor
- ///
- /// Note: The copy constructor and the assignment operator are
- /// intentionally defined as private, making this class non-copyable.
- //@{
-private:
- DNSAnswer(const DNSAnswer& source);
- DNSAnswer& operator=(const DNSAnswer& source);
-protected:
- /// \brief The default constructor.
- ///
- /// This is intentionally defined as \c protected as this base class
- /// should never be instantiated (except as part of a derived class).
- DNSAnswer() {}
-public:
- /// \brief The destructor
- virtual ~DNSAnswer() {}
- //@}
- /// \brief The function operator
- ///
- /// This makes its call indirectly via the "self" pointer, ensuring
- /// that the function ultimately invoked will be the one in the derived
- /// class.
- ///
- /// \param io_message The event message to handle
- /// \param query_message The DNS MessagePtr of the original query
- /// \param answer_message The DNS MessagePtr of the answer we are
- /// building
- /// \param buffer Intermediate data results are put here
- virtual void operator()(const IOMessage& io_message,
- isc::dns::MessagePtr query_message,
- isc::dns::MessagePtr answer_message,
- isc::dns::OutputBufferPtr buffer) const = 0;
-};
-
-} // namespace asiolink
-#endif // __ASIOLINK_DNS_ANSWER_H
diff --git a/src/lib/asiolink/dns_lookup.h b/src/lib/asiolink/dns_lookup.h
deleted file mode 100644
index a79976f..0000000
--- a/src/lib/asiolink/dns_lookup.h
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __ASIOLINK_DNS_LOOKUP_H
-#define __ASIOLINK_DNS_LOOKUP_H 1
-
-#include <asiolink/io_message.h>
-#include <asiolink/dns_server.h>
-#include <dns/buffer.h>
-#include <dns/message.h>
-
-namespace asiolink {
-
-/// \brief The \c DNSLookup class is an abstract base class for a DNS
-/// Lookup provider function.
-///
-/// Specific derived class implementations are hidden within the
-/// implementation. Instances of the derived classes can be called
-/// as functions via the operator() interface. Pointers to these
-/// instances can then be provided to the \c IOService class
-/// via its constructor.
-///
-/// A DNS Lookup provider function obtains the data needed to answer
-/// a DNS query (e.g., from authoritative data source, cache, or upstream
-/// query). After it has run, the OutputBuffer object passed to it
-/// should contain the answer to the query, in an internal representation.
-class DNSLookup {
- ///
- /// \name Constructors and Destructor
- ///
- /// Note: The copy constructor and the assignment operator are
- /// intentionally defined as private, making this class non-copyable.
- //@{
-private:
- DNSLookup(const DNSLookup& source);
- DNSLookup& operator=(const DNSLookup& source);
-protected:
- /// \brief The default constructor.
- ///
- /// This is intentionally defined as \c protected as this base class
- /// should never be instantiated (except as part of a derived class).
- DNSLookup() : self_(this) {}
-public:
- /// \brief The destructor
- virtual ~DNSLookup() {}
- //@}
- /// \brief The function operator
- ///
- /// This makes its call indirectly via the "self" pointer, ensuring
- /// that the function ultimately invoked will be the one in the derived
- /// class.
- ///
- /// \param io_message The event message to handle
- /// \param message The DNS MessagePtr that needs handling
- /// \param answer_message The final answer will be constructed in
- /// this MessagePtr
- /// \param buffer The final answer is put here
- /// \param server DNSServer object to use
- virtual void operator()(const IOMessage& io_message,
- isc::dns::MessagePtr message,
- isc::dns::MessagePtr answer_message,
- isc::dns::OutputBufferPtr buffer,
- DNSServer* server) const
- {
- (*self_)(io_message, message, answer_message, buffer, server);
- }
-private:
- DNSLookup* self_;
-};
-
-} // namespace asiolink
-#endif // __ASIOLINK_DNS_LOOKUP_H
diff --git a/src/lib/asiolink/dns_server.h b/src/lib/asiolink/dns_server.h
deleted file mode 100644
index f15f808..0000000
--- a/src/lib/asiolink/dns_server.h
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __ASIOLINK_DNS_SERVER_H
-#define __ASIOLINK_DNS_SERVER_H 1
-
-#include <asiolink/io_message.h>
-
-namespace asiolink {
-
-/// \brief The \c DNSServer class is a wrapper (and base class) for
-/// classes which provide DNS server functionality.
-///
-/// The classes derived from this one, \c TCPServer and \c UDPServer,
-/// act as the interface layer between clients sending queries, and
-/// functions defined elsewhere that provide answers to those queries.
-/// Those functions are described in more detail below under
-/// \c SimpleCallback, \c DNSLookup, and \c DNSAnswer.
-///
-/// Notes to developers:
-/// When constructed, this class (and its derived classes) will have its
-/// "self_" member set to point to "this". Objects of this class (as
-/// instantiated through a base class) are sometimes passed by
-/// reference (as this superclass); calls to methods in the base
-/// class are then rerouted via this pointer to methods in the derived
-/// class. This allows code from outside asiolink, with no specific
-/// knowledge of \c TCPServer or \c UDPServer, to access their methods.
-///
-/// This class is both assignable and copy-constructable. Its subclasses
-/// use the "stackless coroutine" pattern, meaning that it will copy itself
-/// when "forking", and that instances will be posted as ASIO handler
-/// objects, which are always copied.
-///
-/// Because these objects are frequently copied, it is recommended
-/// that derived classes be kept small to reduce copy overhead.
-class DNSServer {
-protected:
- ///
- /// \name Constructors and destructors
- ///
- /// This is intentionally defined as \c protected, as this base class
- /// should never be instantiated except as part of a derived class.
- //@{
- DNSServer() : self_(this) {}
-public:
- /// \brief The destructor
- virtual ~DNSServer() {}
- //@}
-
- ///
- /// \name Class methods
- ///
- /// These methods all make their calls indirectly via the "self_"
- /// pointer, ensuring that the functions ultimately invoked will be
- /// the ones in the derived class. This makes it possible to pass
- /// instances of derived classes as references to this base class
- /// without losing access to derived class data.
- ///
- //@{
- /// \brief The funtion operator
- virtual void operator()(asio::error_code ec = asio::error_code(),
- size_t length = 0)
- {
- (*self_)(ec, length);
- }
-
- /// \brief Stop current running server
- virtual void stop() { self_->stop();}
-
- /// \brief Resume processing of the server coroutine after an
- /// asynchronous call (e.g., to the DNS Lookup provider) has completed.
- ///
- /// \param done If true, this signals the system there is an answer
- /// to return.
- virtual void resume(const bool done) { self_->resume(done); }
-
- /// \brief Indicate whether the server is able to send an answer
- /// to a query.
- ///
- /// This is presently used only for testing purposes.
- virtual bool hasAnswer() { return (self_->hasAnswer()); }
-
- /// \brief Returns the current value of the 'coroutine' object
- ///
- /// This is a temporary method, intended to be used for debugging
- /// purposes during development and removed later. It allows
- /// callers from outside the coroutine object to retrieve information
- /// about its current state.
- ///
- /// \return The value of the 'coroutine' object
- virtual int value() { return (self_->value()); }
-
- /// \brief Returns a pointer to a clone of this DNSServer object.
- ///
- /// When a \c DNSServer object is copied or assigned, the result will
- /// normally be another \c DNSServer object containing a copy
- /// of the original "self_" pointer. Calling clone() guarantees
- /// that the underlying object is also correctly copied.
- ///
- /// \return A deep copy of this DNSServer object
- virtual DNSServer* clone() { return (self_->clone()); }
- //@}
-
-protected:
- /// \brief Lookup handler object.
- ///
- /// This is a protected class; it can only be instantiated
- /// from within a derived class of \c DNSServer.
- ///
- /// A server object that has received a query creates an instance
- /// of this class and scheudles it on the ASIO service queue
- /// using asio::io_service::post(). When the handler executes, it
- /// calls the asyncLookup() method in the server object to start a
- /// DNS lookup. When the lookup is complete, the server object is
- /// scheduled to resume, again using io_service::post().
- ///
- /// Note that the calling object is copied into the handler object,
- /// not referenced. This is because, once the calling object yields
- /// control to the handler, it falls out of scope and may disappear
- template <typename T>
- class AsyncLookup {
- public:
- AsyncLookup(T& caller) : caller_(caller) {}
- void operator()() { caller_.asyncLookup(); }
- private:
- T caller_;
- };
-
- /// \brief Carries out a DNS lookup.
- ///
- /// This function calls the \c DNSLookup object specified by the
- /// DNS server when the \c IOService was created, passing along
- /// the details of the query and a pointer back to the current
- /// server object. It is called asynchronously via the AsyncLookup
- /// handler class.
- virtual void asyncLookup() { self_->asyncLookup(); }
-
-private:
- DNSServer* self_;
-};
-
-
-} // asiolink
-#endif // __ASIOLINK_DNS_SERVER_H
diff --git a/src/lib/asiolink/dns_service.cc b/src/lib/asiolink/dns_service.cc
deleted file mode 100644
index f17bb44..0000000
--- a/src/lib/asiolink/dns_service.cc
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <unistd.h> // for some IPC/network system calls
-
-#include <boost/lexical_cast.hpp>
-
-#include <config.h>
-
-#include <log/dummylog.h>
-
-#include <asio.hpp>
-#include <asiolink/dns_service.h>
-#include <asiolink/io_service.h>
-#include <asiolink/io_service.h>
-#include <asiolink/tcp_server.h>
-#include <asiolink/udp_server.h>
-
-#include <log/dummylog.h>
-
-#include <boost/lexical_cast.hpp>
-#include <boost/foreach.hpp>
-
-using isc::log::dlog;
-
-namespace asiolink {
-
-class SimpleCallback;
-class DNSLookup;
-class DNSAnswer;
-
-namespace {
-
-asio::ip::address
-convertAddr(const std::string& address) {
- asio::error_code err;
- asio::ip::address addr = asio::ip::address::from_string(address, err);
- if (err) {
- isc_throw(IOError, "Invalid IP address '" << &address << "': "
- << err.message());
- }
- return (addr);
-}
-
-}
-
-
-class DNSServiceImpl {
-public:
- DNSServiceImpl(IOService& io_service, const char& port,
- const asio::ip::address* v4addr,
- const asio::ip::address* v6addr,
- SimpleCallback* checkin, DNSLookup* lookup,
- DNSAnswer* answer);
-
- IOService& io_service_;
-
- typedef boost::shared_ptr<UDPServer> UDPServerPtr;
- typedef boost::shared_ptr<TCPServer> TCPServerPtr;
- typedef boost::shared_ptr<DNSServer> DNSServerPtr;
- std::vector<DNSServerPtr> servers_;
- SimpleCallback *checkin_;
- DNSLookup *lookup_;
- DNSAnswer *answer_;
-
- void addServer(uint16_t port, const asio::ip::address& address) {
- try {
- dlog(std::string("Initialize TCP server at ") + address.to_string() + ":" + boost::lexical_cast<std::string>(port));
- TCPServerPtr tcpServer(new TCPServer(io_service_.get_io_service(),
- address, port, checkin_, lookup_, answer_));
- (*tcpServer)();
- servers_.push_back(tcpServer);
- dlog(std::string("Initialize UDP server at ") + address.to_string() + ":" + boost::lexical_cast<std::string>(port));
- UDPServerPtr udpServer(new UDPServer(io_service_.get_io_service(),
- address, port, checkin_, lookup_, answer_));
- (*udpServer)();
- servers_.push_back(udpServer);
- }
- catch (const asio::system_error& err) {
- // We need to catch and convert any ASIO level exceptions.
- // This can happen for unavailable address, binding a privilege port
- // without the privilege, etc.
- isc_throw(IOError, "Failed to initialize network servers: " <<
- err.what());
- }
- }
- void addServer(const char& port, const asio::ip::address& address) {
- uint16_t portnum;
- try {
- // XXX: SunStudio with stlport4 doesn't reject some invalid
- // representation such as "-1" by lexical_cast<uint16_t>, so
- // we convert it into a signed integer of a larger size and perform
- // range check ourselves.
- const int32_t portnum32 = boost::lexical_cast<int32_t>(&port);
- if (portnum32 < 0 || portnum32 > 65535) {
- isc_throw(IOError, "Invalid port number '" << &port);
- }
- portnum = portnum32;
- } catch (const boost::bad_lexical_cast& ex) {
- isc_throw(IOError, "Invalid port number '" << &port << "': " <<
- ex.what());
- }
- addServer(portnum, address);
- }
-};
-
-DNSServiceImpl::DNSServiceImpl(IOService& io_service,
- const char& port,
- const asio::ip::address* const v4addr,
- const asio::ip::address* const v6addr,
- SimpleCallback* checkin,
- DNSLookup* lookup,
- DNSAnswer* answer) :
- io_service_(io_service),
- checkin_(checkin),
- lookup_(lookup),
- answer_(answer)
-{
-
- if (v4addr) {
- addServer(port, *v4addr);
- }
- if (v6addr) {
- addServer(port, *v6addr);
- }
-}
-
-DNSService::DNSService(IOService& io_service,
- const char& port, const char& address,
- SimpleCallback* checkin,
- DNSLookup* lookup,
- DNSAnswer* answer) :
- impl_(new DNSServiceImpl(io_service, port, NULL, NULL, checkin, lookup,
- answer)), io_service_(io_service)
-{
- addServer(port, &address);
-}
-
-DNSService::DNSService(IOService& io_service,
- const char& port,
- const bool use_ipv4, const bool use_ipv6,
- SimpleCallback* checkin,
- DNSLookup* lookup,
- DNSAnswer* answer) :
- impl_(NULL), io_service_(io_service)
-{
- const asio::ip::address v4addr_any =
- asio::ip::address(asio::ip::address_v4::any());
- const asio::ip::address* const v4addrp = use_ipv4 ? &v4addr_any : NULL;
- const asio::ip::address v6addr_any =
- asio::ip::address(asio::ip::address_v6::any());
- const asio::ip::address* const v6addrp = use_ipv6 ? &v6addr_any : NULL;
- impl_ = new DNSServiceImpl(io_service, port, v4addrp, v6addrp, checkin, lookup, answer);
-}
-
-DNSService::DNSService(IOService& io_service, SimpleCallback* checkin,
- DNSLookup* lookup, DNSAnswer *answer) :
- impl_(new DNSServiceImpl(io_service, *"0", NULL, NULL, checkin, lookup,
- answer)), io_service_(io_service)
-{
-}
-
-DNSService::~DNSService() {
- delete impl_;
-}
-
-void
-DNSService::addServer(const char& port, const std::string& address) {
- impl_->addServer(port, convertAddr(address));
-}
-
-void
-DNSService::addServer(uint16_t port, const std::string& address) {
- impl_->addServer(port, convertAddr(address));
-}
-
-void
-DNSService::clearServers() {
- BOOST_FOREACH(const DNSServiceImpl::DNSServerPtr& s, impl_->servers_) {
- s->stop();
- }
- impl_->servers_.clear();
-}
-
-
-
-} // namespace asiolink
diff --git a/src/lib/asiolink/dns_service.h b/src/lib/asiolink/dns_service.h
deleted file mode 100644
index 9a3fb4c..0000000
--- a/src/lib/asiolink/dns_service.h
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __ASIOLINK_DNS_SERVICE_H
-#define __ASIOLINK_DNS_SERVICE_H 1
-
-#include <resolve/resolver_interface.h>
-
-#include <asiolink/io_service.h>
-
-namespace asiolink {
-
-class SimpleCallback;
-class DNSLookup;
-class DNSAnswer;
-class DNSServiceImpl;
-
-/// \brief Handle DNS Queries
-///
-/// DNSService is the service that handles DNS queries and answers with
-/// a given IOService. This class is mainly intended to hold all the
-/// logic that is shared between the authoritative and the recursive
-/// server implementations. As such, it handles asio, including config
-/// updates (through the 'Checkinprovider'), and listening sockets.
-class DNSService {
- ///
- /// \name Constructors and Destructor
- ///
- /// Note: The copy constructor and the assignment operator are
- /// intentionally defined as private, making this class non-copyable.
- //@{
-private:
- DNSService(const DNSService& source);
- DNSService& operator=(const DNSService& source);
-
-public:
- /// \brief The constructor with a specific IP address and port on which
- /// the services listen on.
- ///
- /// \param io_service The IOService to work with
- /// \param port the port to listen on
- /// \param address the IP address to listen on
- /// \param checkin Provider for cc-channel events (see \c SimpleCallback)
- /// \param lookup The lookup provider (see \c DNSLookup)
- /// \param answer The answer provider (see \c DNSAnswer)
- DNSService(IOService& io_service, const char& port,
- const char& address, SimpleCallback* checkin,
- DNSLookup* lookup, DNSAnswer* answer);
- /// \brief The constructor with a specific port on which the services
- /// listen on.
- ///
- /// It effectively listens on "any" IPv4 and/or IPv6 addresses.
- /// IPv4/IPv6 services will be available if and only if \c use_ipv4
- /// or \c use_ipv6 is \c true, respectively.
- ///
- /// \param io_service The IOService to work with
- /// \param port the port to listen on
- /// \param use_ipv4 If true, listen on ipv4 'any'
- /// \param use_ipv6 If true, listen on ipv6 'any'
- /// \param checkin Provider for cc-channel events (see \c SimpleCallback)
- /// \param lookup The lookup provider (see \c DNSLookup)
- /// \param answer The answer provider (see \c DNSAnswer)
- DNSService(IOService& io_service, const char& port,
- const bool use_ipv4, const bool use_ipv6,
- SimpleCallback* checkin, DNSLookup* lookup,
- DNSAnswer* answer);
- /// \brief The constructor without any servers.
- ///
- /// Use addServer() to add some servers.
- DNSService(IOService& io_service, SimpleCallback* checkin,
- DNSLookup* lookup, DNSAnswer* answer);
- /// \brief The destructor.
- ~DNSService();
- //@}
-
- /// \brief Add another server to the service
- void addServer(uint16_t port, const std::string &address);
- void addServer(const char &port, const std::string &address);
- /// \brief Remove all servers from the service
- void clearServers();
-
- /// \brief Return the native \c io_service object used in this wrapper.
- ///
- /// This is a short term work around to support other BIND 10 modules
- /// that share the same \c io_service with the authoritative server.
- /// It will eventually be removed once the wrapper interface is
- /// generalized.
- asio::io_service& get_io_service() { return io_service_.get_io_service(); }
-
- /// \brief Return the IO Service Object
- ///
- /// \return IOService object for this DNS service.
- asiolink::IOService& getIOService() { return (io_service_);}
-
-private:
- DNSServiceImpl* impl_;
- IOService& io_service_;
-};
-
-} // namespace asiolink
-#endif // __ASIOLINK_DNS_SERVICE_H
diff --git a/src/lib/asiolink/dummy_io_cb.h b/src/lib/asiolink/dummy_io_cb.h
index 0006b95..2081906 100644
--- a/src/lib/asiolink/dummy_io_cb.h
+++ b/src/lib/asiolink/dummy_io_cb.h
@@ -20,6 +20,7 @@
#include <asio/error.hpp>
#include <asio/error_code.hpp>
+namespace isc {
namespace asiolink {
/// \brief Asynchronous I/O Completion Callback
@@ -55,5 +56,6 @@ public:
};
} // namespace asiolink
+} // namespace isc
#endif // __DUMMY_IO_CB_H
diff --git a/src/lib/asiolink/interval_timer.cc b/src/lib/asiolink/interval_timer.cc
index 8efb102..9873e9b 100644
--- a/src/lib/asiolink/interval_timer.cc
+++ b/src/lib/asiolink/interval_timer.cc
@@ -14,11 +14,9 @@
#include <config.h>
-#include <unistd.h> // for some IPC/network system calls
-#include <sys/socket.h>
-#include <netinet/in.h>
-
#include <boost/bind.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/shared_ptr.hpp>
#include <exceptions/exceptions.h>
@@ -26,9 +24,19 @@
#include <asiolink/interval_timer.h>
#include <asiolink/io_service.h>
+namespace isc {
namespace asiolink {
-class IntervalTimerImpl {
+/// This class holds a call back function of asynchronous operations.
+/// To ensure the object is alive while an asynchronous operation refers
+/// to it, we use shared_ptr and enable_shared_from_this.
+/// The object will be destructed in case IntervalTimer has been destructed
+/// and no asynchronous operation refers to it.
+/// Please follow the link to get an example:
+/// http://think-async.com/asio/asio-1.4.8/doc/asio/tutorial/tutdaytime3.html#asio.tutorial.tutdaytime3.the_tcp_connection_class
+class IntervalTimerImpl :
+ public boost::enable_shared_from_this<IntervalTimerImpl>
+{
private:
// prohibit copy
IntervalTimerImpl(const IntervalTimerImpl& source);
@@ -52,14 +60,18 @@ private:
long interval_;
// asio timer
asio::deadline_timer timer_;
+ // interval_ will be set to this value in destructor in order to detect
+ // use-after-free type of bugs.
+ static const long INVALIDATED_INTERVAL = -1;
};
IntervalTimerImpl::IntervalTimerImpl(IOService& io_service) :
interval_(0), timer_(io_service.get_io_service())
{}
-IntervalTimerImpl::~IntervalTimerImpl()
-{}
+IntervalTimerImpl::~IntervalTimerImpl() {
+ interval_ = INVALIDATED_INTERVAL;
+}
void
IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
@@ -80,42 +92,46 @@ IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
// At this point the timer is not running yet and will not expire.
// After calling IOService::run(), the timer will expire.
update();
- return;
}
void
IntervalTimerImpl::update() {
- if (interval_ == 0) {
- // timer has been canceled. Do nothing.
- return;
- }
try {
// Update expire time to (current time + interval_).
timer_.expires_from_now(boost::posix_time::millisec(interval_));
+ // Reset timer.
+ // Pass a function bound with a shared_ptr to this.
+ timer_.async_wait(boost::bind(&IntervalTimerImpl::callback,
+ shared_from_this(),
+ asio::placeholders::error));
} catch (const asio::system_error& e) {
- isc_throw(isc::Unexpected, "Failed to update timer");
+ isc_throw(isc::Unexpected, "Failed to update timer: " << e.what());
+ } catch (const boost::bad_weak_ptr&) {
+ // Can't happen. It means a severe internal bug.
+ assert(0);
}
- // Reset timer.
- timer_.async_wait(boost::bind(&IntervalTimerImpl::callback, this, _1));
}
void
-IntervalTimerImpl::callback(const asio::error_code& cancelled) {
- // Do not call cbfunc_ in case the timer was cancelled.
- // The timer will be canelled in the destructor of asio::deadline_timer.
- if (!cancelled) {
- cbfunc_();
+IntervalTimerImpl::callback(const asio::error_code& ec) {
+ assert(interval_ != INVALIDATED_INTERVAL);
+ if (interval_ == 0 || ec) {
+ // timer has been canceled. Do nothing.
+ } else {
// Set next expire time.
update();
+ // Invoke the call back function.
+ cbfunc_();
}
}
-IntervalTimer::IntervalTimer(IOService& io_service) {
- impl_ = new IntervalTimerImpl(io_service);
-}
+IntervalTimer::IntervalTimer(IOService& io_service) :
+ impl_(new IntervalTimerImpl(io_service))
+{}
IntervalTimer::~IntervalTimer() {
- delete impl_;
+ // Cancel the timer to make sure cbfunc_() will not be called any more.
+ cancel();
}
void
@@ -133,4 +149,5 @@ IntervalTimer::getInterval() const {
return (impl_->getInterval());
}
-}
+} // namespace asiolink
+} // namespace isc
diff --git a/src/lib/asiolink/interval_timer.h b/src/lib/asiolink/interval_timer.h
index 6c43327..57ec1c3 100644
--- a/src/lib/asiolink/interval_timer.h
+++ b/src/lib/asiolink/interval_timer.h
@@ -16,12 +16,14 @@
#define __ASIOLINK_INTERVAL_TIMER_H 1
#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
#include <asiolink/io_service.h>
+namespace isc {
namespace asiolink {
-struct IntervalTimerImpl;
+class IntervalTimerImpl;
/// \brief The \c IntervalTimer class is a wrapper for the ASIO
/// \c asio::deadline_timer class.
@@ -41,9 +43,6 @@ struct IntervalTimerImpl;
/// The call back function will not be called if the instance of this class is
/// destroyed before the timer is expired.
///
-/// Note: Destruction of an instance of this class while call back is pending
-/// causes throwing an exception from \c IOService.
-///
/// Sample code:
/// \code
/// void function_to_call_back() {
@@ -99,12 +98,12 @@ public:
/// \param interval Interval in milliseconds (greater than 0)
///
/// Note: IntervalTimer will not pass \c asio::error_code to
- /// call back function. In case the timer is cancelled, the function
+ /// call back function. In case the timer is canceled, the function
/// will not be called.
///
/// \throw isc::InvalidParameter cbfunc is empty
/// \throw isc::BadValue interval is less than or equal to 0
- /// \throw isc::Unexpected ASIO library error
+ /// \throw isc::Unexpected internal runtime error
void setup(const Callback& cbfunc, const long interval);
/// Cancel the timer.
@@ -126,8 +125,9 @@ public:
long getInterval() const;
private:
- IntervalTimerImpl* impl_;
+ boost::shared_ptr<IntervalTimerImpl> impl_;
};
-} // namespace asiolink
+} // namespace asiolink
+} // namespace isc
#endif // __ASIOLINK_INTERVAL_TIMER_H
diff --git a/src/lib/asiolink/io_address.cc b/src/lib/asiolink/io_address.cc
index 70e8374..7f7a6fc 100644
--- a/src/lib/asiolink/io_address.cc
+++ b/src/lib/asiolink/io_address.cc
@@ -31,6 +31,7 @@ using asio::ip::tcp;
using namespace std;
+namespace isc {
namespace asiolink {
// XXX: we cannot simply construct the address in the initialization list,
@@ -62,4 +63,5 @@ IOAddress::getFamily() const {
}
}
-}
+} // namespace asiolink
+} // namespace isc
diff --git a/src/lib/asiolink/io_address.h b/src/lib/asiolink/io_address.h
index 53c1a7a..655b727 100644
--- a/src/lib/asiolink/io_address.h
+++ b/src/lib/asiolink/io_address.h
@@ -26,6 +26,7 @@
#include <exceptions/exceptions.h>
+namespace isc {
namespace asiolink {
/// \brief The \c IOAddress class represents an IP addresses (version
@@ -119,5 +120,6 @@ private:
asio::ip::address asio_address_;
};
-} // asiolink
+} // namespace asiolink
+} // namespace isc
#endif // __IO_ADDRESS_H
diff --git a/src/lib/asiolink/io_asio_socket.h b/src/lib/asiolink/io_asio_socket.h
index ac793a6..864708c 100644
--- a/src/lib/asiolink/io_asio_socket.h
+++ b/src/lib/asiolink/io_asio_socket.h
@@ -26,12 +26,12 @@
#include <exceptions/exceptions.h>
#include <coroutine.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <asiolink/io_error.h>
#include <asiolink/io_socket.h>
-
+namespace isc {
namespace asiolink {
/// \brief Socket not open
@@ -270,7 +270,7 @@ public:
virtual bool processReceivedData(const void* staging, size_t length,
size_t& cumulative, size_t& offset,
size_t& expected,
- isc::dns::OutputBufferPtr& outbuff) = 0;
+ isc::util::OutputBufferPtr& outbuff) = 0;
/// \brief Cancel I/O On AsioSocket
virtual void cancel() = 0;
@@ -372,7 +372,7 @@ public:
virtual bool receiveComplete(const void* staging, size_t length,
size_t& cumulative, size_t& offset,
size_t& expected,
- isc::dns::OutputBufferPtr& outbuff)
+ isc::util::OutputBufferPtr& outbuff)
{
return (true);
}
@@ -395,5 +395,6 @@ private:
};
} // namespace asiolink
+} // namespace isc
#endif // __IO_ASIO_SOCKET_H
diff --git a/src/lib/asiolink/io_endpoint.cc b/src/lib/asiolink/io_endpoint.cc
index 97e9c91..63830a5 100644
--- a/src/lib/asiolink/io_endpoint.cc
+++ b/src/lib/asiolink/io_endpoint.cc
@@ -28,6 +28,7 @@
using namespace std;
+namespace isc {
namespace asiolink {
const IOEndpoint*
@@ -44,4 +45,18 @@ IOEndpoint::create(const int protocol, const IOAddress& address,
protocol);
}
+bool
+IOEndpoint::operator==(const IOEndpoint& other) const {
+ return (getProtocol() == other.getProtocol() &&
+ getPort() == other.getPort() &&
+ getFamily() == other.getFamily() &&
+ getAddress() == other.getAddress());
}
+
+bool
+IOEndpoint::operator!=(const IOEndpoint& other) const {
+ return (!operator==(other));
+}
+
+} // namespace asiolink
+} // namespace isc
diff --git a/src/lib/asiolink/io_endpoint.h b/src/lib/asiolink/io_endpoint.h
index 2ec4083..11ea97b 100644
--- a/src/lib/asiolink/io_endpoint.h
+++ b/src/lib/asiolink/io_endpoint.h
@@ -20,12 +20,15 @@
// See the description of the namespace below.
#include <unistd.h> // for some network system calls
+#include <sys/socket.h> // for sockaddr
+
#include <functional>
#include <string>
#include <exceptions/exceptions.h>
#include <asiolink/io_address.h>
+namespace isc {
namespace asiolink {
/// \brief The \c IOEndpoint class is an abstract base class to represent
@@ -89,6 +92,47 @@ public:
/// \brief Returns the address family of the endpoint.
virtual short getFamily() const = 0;
+ /// \brief Returns the address of the endpoint in the form of sockaddr
+ /// structure.
+ ///
+ /// The actual instance referenced by the returned value of this method
+ /// is of per address family structure: For IPv4 (AF_INET), it's
+ /// \c sockaddr_in; for IPv6 (AF_INET6), it's \c sockaddr_in6.
+ /// The corresponding port and address members of the underlying structure
+ /// will be set in the network byte order.
+ ///
+ /// This method is "redundant" in that all information to construct the
+ /// \c sockaddr is available via the other "get" methods.
+ /// It is still defined for performance sensitive applications that need
+ /// to get the address information, such as for address based access
+ /// control at a high throughput. Internally it is implemented with
+ /// minimum overhead such as data copy (this is another reason why this
+ /// method returns a reference).
+ ///
+ /// As a tradeoff, this method is more fragile; it assumes that the
+ /// underlying ASIO implementation stores the address information in
+ /// the form of \c sockaddr and it can be accessed in an efficient way.
+ /// This is the case as of this writing, but if the underlying
+ /// implementation changes this method may become much slower or its
+ /// interface may have to be changed, too.
+ ///
+ /// It is therefore discouraged for normal applications to use this
+ /// method. Unless the application is very performance sensitive, it
+ /// should use the other "get" method to retrieve specific information
+ /// of the endpoint.
+ ///
+ /// The returned reference is only valid while the corresponding
+ /// \c IOEndpoint is valid. Once it's destructed the reference will
+ /// become invalid.
+ ///
+ /// \exception None
+ /// \return Reference to a \c sockaddr structure corresponding to the
+ /// endpoint.
+ virtual const struct sockaddr& getSockAddr() const = 0;
+
+ bool operator==(const IOEndpoint& other) const;
+ bool operator!=(const IOEndpoint& other) const;
+
/// \brief A polymorphic factory of endpoint from address and port.
///
/// This method creates a new instance of (a derived class of)
@@ -114,5 +158,10 @@ public:
const unsigned short port);
};
-} // asiolink
+} // namespace asiolink
+} // namespace isc
#endif // __IO_ENDPOINT_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/asiolink/io_error.h b/src/lib/asiolink/io_error.h
index 2869e0b..c19d91c 100644
--- a/src/lib/asiolink/io_error.h
+++ b/src/lib/asiolink/io_error.h
@@ -18,6 +18,7 @@
#include <exceptions/exceptions.h>
+namespace isc {
namespace asiolink {
/// \brief An exception that is thrown if an error occurs within the IO
@@ -30,6 +31,7 @@ public:
};
-} // asiolink
+} // namespace asiolink
+} // namespace isc
#endif // __IO_ERROR_H
diff --git a/src/lib/asiolink/io_fetch.cc b/src/lib/asiolink/io_fetch.cc
deleted file mode 100644
index 3ff44c0..0000000
--- a/src/lib/asiolink/io_fetch.cc
+++ /dev/null
@@ -1,355 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <config.h>
-
-#include <unistd.h> // for some IPC/network system calls
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-#include <boost/bind.hpp>
-#include <boost/scoped_ptr.hpp>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
-
-#include <dns/message.h>
-#include <dns/messagerenderer.h>
-#include <dns/opcode.h>
-#include <dns/rcode.h>
-#include <log/logger.h>
-
-#include <asiolink/qid_gen.h>
-
-#include <asio.hpp>
-#include <asio/deadline_timer.hpp>
-
-#include <asiolink/asiodef.h>
-#include <asiolink/io_address.h>
-#include <asiolink/io_asio_socket.h>
-#include <asiolink/io_endpoint.h>
-#include <asiolink/io_fetch.h>
-#include <asiolink/io_service.h>
-#include <asiolink/tcp_endpoint.h>
-#include <asiolink/tcp_socket.h>
-#include <asiolink/udp_endpoint.h>
-#include <asiolink/udp_socket.h>
-
-using namespace asio;
-using namespace isc::dns;
-using namespace isc::log;
-using namespace std;
-
-namespace asiolink {
-
-/// Use the ASIO logger
-
-isc::log::Logger logger("asiolink");
-
-/// \brief IOFetch Data
-///
-/// The data for IOFetch is held in a separate struct pointed to by a shared_ptr
-/// object. This is because the IOFetch object will be copied often (it is used
-/// as a coroutine and passed as callback to many async_*() functions) and we
-/// want keep the same data). Organising the data in this way keeps copying to
-/// a minimum.
-struct IOFetchData {
-
- // The first two members are shared pointers to a base class because what is
- // actually instantiated depends on whether the fetch is over UDP or TCP,
- // which is not known until construction of the IOFetch. Use of a shared
- // pointer here is merely to ensure deletion when the data object is deleted.
- boost::scoped_ptr<IOAsioSocket<IOFetch> > socket;
- ///< Socket to use for I/O
- boost::scoped_ptr<IOEndpoint> remote; ///< Where the fetch was sent
- isc::dns::Question question; ///< Question to be asked
- isc::dns::OutputBufferPtr msgbuf; ///< Wire buffer for question
- isc::dns::OutputBufferPtr received; ///< Received data put here
- IOFetch::Callback* callback; ///< Called on I/O Completion
- asio::deadline_timer timer; ///< Timer to measure timeouts
- IOFetch::Protocol protocol; ///< Protocol being used
- size_t cumulative; ///< Cumulative received amount
- size_t expected; ///< Expected amount of data
- size_t offset; ///< Offset to receive data
- bool stopped; ///< Have we stopped running?
- int timeout; ///< Timeout in ms
-
- // In case we need to log an error, the origin of the last asynchronous
- // I/O is recorded. To save time and simplify the code, this is recorded
- // as the ID of the error message that would be generated if the I/O failed.
- // This means that we must make sure that all possible "origins" take the
- // same arguments in their message in the same order.
- isc::log::MessageID origin; ///< Origin of last asynchronous I/O
- uint8_t staging[IOFetch::STAGING_LENGTH];
- ///< Temporary array for received data
-
- /// \brief Constructor
- ///
- /// Just fills in the data members of the IOFetchData structure
- ///
- /// \param proto Either IOFetch::TCP or IOFetch::UDP.
- /// \param service I/O Service object to handle the asynchronous
- /// operations.
- /// \param query DNS question to send to the upstream server.
- /// \param address IP address of upstream server
- /// \param port Port to use for the query
- /// \param buff Output buffer into which the response (in wire format)
- /// is written (if a response is received).
- /// \param cb Callback object containing the callback to be called
- /// when we terminate. The caller is responsible for managing this
- /// object and deleting it if necessary.
- /// \param wait Timeout for the fetch (in ms).
- ///
- /// TODO: May need to alter constructor (see comment 4 in Trac ticket #554)
- IOFetchData(IOFetch::Protocol proto, IOService& service,
- const isc::dns::Question& query, const IOAddress& address,
- uint16_t port, isc::dns::OutputBufferPtr& buff, IOFetch::Callback* cb,
- int wait)
- :
- socket((proto == IOFetch::UDP) ?
- static_cast<IOAsioSocket<IOFetch>*>(
- new UDPSocket<IOFetch>(service)) :
- static_cast<IOAsioSocket<IOFetch>*>(
- new TCPSocket<IOFetch>(service))
- ),
- remote((proto == IOFetch::UDP) ?
- static_cast<IOEndpoint*>(new UDPEndpoint(address, port)) :
- static_cast<IOEndpoint*>(new TCPEndpoint(address, port))
- ),
- question(query),
- msgbuf(new isc::dns::OutputBuffer(512)),
- received(buff),
-
- callback(cb),
- timer(service.get_io_service()),
- protocol(proto),
- cumulative(0),
- expected(0),
- offset(0),
- stopped(false),
- timeout(wait),
- origin(ASIO_UNKORIGIN),
- staging()
- {}
-};
-
-/// IOFetch Constructor - just initialize the private data
-
-IOFetch::IOFetch(Protocol protocol, IOService& service,
- const isc::dns::Question& question, const IOAddress& address, uint16_t port,
- OutputBufferPtr& buff, Callback* cb, int wait)
- :
- data_(new IOFetchData(protocol, service, question, address,
- port, buff, cb, wait))
-{
-}
-
-// Return protocol in use.
-
-IOFetch::Protocol
-IOFetch::getProtocol() const {
- return (data_->protocol);
-}
-
-/// The function operator is implemented with the "stackless coroutine"
-/// pattern; see internal/coroutine.h for details.
-
-void
-IOFetch::operator()(asio::error_code ec, size_t length) {
-
- if (data_->stopped) {
- return;
- } else if (ec) {
- logIOFailure(ec);
- return;
- }
-
- CORO_REENTER (this) {
-
- /// Generate the upstream query and render it to wire format
- /// This is done in a different scope to allow inline variable
- /// declarations.
- {
- Message msg(Message::RENDER);
- msg.setQid(QidGenerator::getInstance().generateQid());
- msg.setOpcode(Opcode::QUERY());
- msg.setRcode(Rcode::NOERROR());
- msg.setHeaderFlag(Message::HEADERFLAG_RD);
- msg.addQuestion(data_->question);
- MessageRenderer renderer(*data_->msgbuf);
- msg.toWire(renderer);
- }
-
- // If we timeout, we stop, which will can cancel outstanding I/Os and
- // shutdown everything.
- if (data_->timeout != -1) {
- data_->timer.expires_from_now(boost::posix_time::milliseconds(
- data_->timeout));
- data_->timer.async_wait(boost::bind(&IOFetch::stop, *this,
- TIME_OUT));
- }
-
- // Open a connection to the target system. For speed, if the operation
- // is synchronous (i.e. UDP operation) we bypass the yield.
- data_->origin = ASIO_OPENSOCK;
- if (data_->socket->isOpenSynchronous()) {
- data_->socket->open(data_->remote.get(), *this);
- } else {
- CORO_YIELD data_->socket->open(data_->remote.get(), *this);
- }
-
- // Begin an asynchronous send, and then yield. When the send completes,
- // we will resume immediately after this point.
- data_->origin = ASIO_SENDSOCK;
- CORO_YIELD data_->socket->asyncSend(data_->msgbuf->getData(),
- data_->msgbuf->getLength(), data_->remote.get(), *this);
-
- // Now receive the response. Since TCP may not receive the entire
- // message in one operation, we need to loop until we have received
- // it. (This can't be done within the asyncReceive() method because
- // each I/O operation will be done asynchronously and between each one
- // we need to yield ... and we *really* don't want to set up another
- // coroutine within that method.) So after each receive (and yield),
- // we check if the operation is complete and if not, loop to read again.
- //
- // Another concession to TCP is that the amount of is contained in the
- // first two bytes. This leads to two problems:
- //
- // a) We don't want those bytes in the return buffer.
- // b) They may not both arrive in the first I/O.
- //
- // So... we need to loop until we have at least two bytes, then store
- // the expected amount of data. Then we need to loop until we have
- // received all the data before copying it back to the user's buffer.
- // And we want to minimise the amount of copying...
-
- data_->origin = ASIO_RECVSOCK;
- data_->cumulative = 0; // No data yet received
- data_->offset = 0; // First data into start of buffer
- do {
- CORO_YIELD data_->socket->asyncReceive(data_->staging,
- static_cast<size_t>(STAGING_LENGTH),
- data_->offset,
- data_->remote.get(), *this);
- } while (!data_->socket->processReceivedData(data_->staging, length,
- data_->cumulative, data_->offset,
- data_->expected, data_->received));
-
- // Finished with this socket, so close it. This will not generate an
- // I/O error, but reset the origin to unknown in case we change this.
- data_->origin = ASIO_UNKORIGIN;
- data_->socket->close();
-
- /// We are done
- stop(SUCCESS);
- }
-}
-
-// Function that stops the coroutine sequence. It is called either when the
-// query finishes or when the timer times out. Either way, it sets the
-// "stopped_" flag and cancels anything that is in progress.
-//
-// As the function may be entered multiple times as things wind down, it checks
-// if the stopped_ flag is already set. If it is, the call is a no-op.
-
-void
-IOFetch::stop(Result result) {
-
- if (!data_->stopped) {
-
- // Mark the fetch as stopped to prevent other completion callbacks
- // (invoked because of the calls to cancel()) from executing the
- // cancel calls again.
- //
- // In a single threaded environment, the callbacks won't be invoked
- // until this one completes. In a multi-threaded environment, they may
- // well be, in which case the testing (and setting) of the stopped_
- // variable should be done inside a mutex (and the stopped_ variable
- // declared as "volatile").
- //
- // The numeric arguments indicate the debug level, with the lower
- // numbers indicating the most important information. The relative
- // values are somewhat arbitrary.
- //
- // Although Logger::debug checks the debug flag internally, doing it
- // below before calling Logger::debug avoids the overhead of a string
- // conversion in the common case when debug is not enabled.
- //
- // TODO: Update testing of stopped_ if threads are used.
- data_->stopped = true;
- switch (result) {
- case TIME_OUT:
- if (logger.isDebugEnabled(1)) {
- logger.debug(20, ASIO_RECVTMO,
- data_->remote->getAddress().toText().c_str(),
- static_cast<int>(data_->remote->getPort()));
- }
- break;
-
- case SUCCESS:
- if (logger.isDebugEnabled(50)) {
- logger.debug(30, ASIO_FETCHCOMP,
- data_->remote->getAddress().toText().c_str(),
- static_cast<int>(data_->remote->getPort()));
- }
- break;
-
- case STOPPED:
- // Fetch has been stopped for some other reason. This is
- // allowed but as it is unusual it is logged, but with a lower
- // debug level than a timeout (which is totally normal).
- logger.debug(1, ASIO_FETCHSTOP,
- data_->remote->getAddress().toText().c_str(),
- static_cast<int>(data_->remote->getPort()));
- break;
-
- default:
- logger.error(ASIO_UNKRESULT, static_cast<int>(result),
- data_->remote->getAddress().toText().c_str(),
- static_cast<int>(data_->remote->getPort()));
- }
-
- // Stop requested, cancel and I/O's on the socket and shut it down,
- // and cancel the timer.
- data_->socket->cancel();
- data_->socket->close();
-
- data_->timer.cancel();
-
- // Execute the I/O completion callback (if present).
- if (data_->callback) {
- (*(data_->callback))(result);
- }
- }
-}
-
-// Log an error - called on I/O failure
-
-void IOFetch::logIOFailure(asio::error_code ec) {
-
- // Should only get here with a known error code.
- assert((data_->origin == ASIO_OPENSOCK) ||
- (data_->origin == ASIO_SENDSOCK) ||
- (data_->origin == ASIO_RECVSOCK) ||
- (data_->origin == ASIO_UNKORIGIN));
-
- static const char* PROTOCOL[2] = {"TCP", "UDP"};
- logger.error(data_->origin,
- ec.value(),
- ((data_->remote->getProtocol() == IPPROTO_TCP) ?
- PROTOCOL[0] : PROTOCOL[1]),
- data_->remote->getAddress().toText().c_str(),
- static_cast<int>(data_->remote->getPort()));
-}
-
-} // namespace asiolink
-
diff --git a/src/lib/asiolink/io_fetch.h b/src/lib/asiolink/io_fetch.h
deleted file mode 100644
index 0723777..0000000
--- a/src/lib/asiolink/io_fetch.h
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __IO_FETCH_H
-#define __IO_FETCH_H 1
-
-#include <config.h>
-
-#include <boost/shared_array.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
-
-#include <coroutine.h>
-
-#include <asio/error_code.hpp>
-
-#include <dns/buffer.h>
-#include <dns/question.h>
-
-namespace asiolink {
-
-// Forward declarations
-class IOAddress;
-class IOFetchData;
-class IOService;
-
-/// \brief Upstream Fetch Processing
-///
-/// IOFetch is the class used to send upstream fetches and to handle responses.
-///
-/// \param E Endpoint type to use.
-
-class IOFetch : public coroutine {
-public:
- /// \brief Protocol to use on the fetch
- enum Protocol {
- UDP = 0,
- TCP = 1
- };
-
- /// \brief Origin of Asynchronous I/O Call
- ///
- /// Indicates what initiated an asynchronous I/O call and used in deciding
- /// what error message to output if the I/O fails.
- enum Origin {
- NONE = 0, ///< No asynchronous call outstanding
- OPEN = 1,
- SEND = 2,
- RECEIVE = 3,
- CLOSE = 4
- };
-
- /// \brief Result of Upstream Fetch
- ///
- /// Note that this applies to the status of I/Os in the fetch - a fetch
- /// that resulted in a packet being received from the server is a SUCCESS,
- /// even if the contents of the packet indicate that some error occurred.
- enum Result {
- SUCCESS = 0, ///< Success, fetch completed
- TIME_OUT = 1, ///< Failure, fetch timed out
- STOPPED = 2, ///< Control code, fetch has been stopped
- NOTSET = 3 ///< For testing, indicates value not set
- };
-
- // The next enum is a "trick" to allow constants to be defined in a class
- // declaration.
-
- /// \brief Integer Constants
- enum {
- STAGING_LENGTH = 8192 ///< Size of staging buffer
- };
-
- /// \brief I/O Fetch Callback
- ///
- /// Class of callback object for when the fetch itself has completed - an
- /// object of this class is passed to the IOFetch constructor and its
- /// operator() method called when the fetch completes.
- ///
- /// Note the difference between the two operator() methods:
- /// - IOFetch::operator() callback is called when an asynchronous I/O has
- /// completed.
- /// - IOFetch::Callback::operator() is called when an upstream fetch - which
- /// may have involved several asynchronous I/O operations - has completed.
- ///
- /// This is an abstract class.
- class Callback {
- public:
- /// \brief Default Constructor
- Callback()
- {}
-
- /// \brief Virtual Destructor
- virtual ~Callback()
- {}
-
- /// \brief Callback method
- ///
- /// This is the method called when the fetch completes.
- ///
- /// \param result Result of the fetch
- virtual void operator()(Result result) = 0;
- };
-
- /// \brief Constructor.
- ///
- /// Creates the object that will handle the upstream fetch.
- ///
- /// TODO: Need to randomise the source port
- ///
- /// \param protocol Fetch protocol, either IOFetch::TCP or IOFetch::UDP
- /// \param service I/O Service object to handle the asynchronous
- /// operations.
- /// \param question DNS question to send to the upstream server.
- /// \param buff Output buffer into which the response (in wire format)
- /// is written (if a response is received).
- /// \param cb Callback object containing the callback to be called
- /// when we terminate. The caller is responsible for managing this
- /// object and deleting it if necessary.
- /// \param address IP address of upstream server
- /// \param port Port to which to connect on the upstream server
- /// (default = 53)
- /// \param wait Timeout for the fetch (in ms). The default value of
- /// -1 indicates no timeout.
- IOFetch(Protocol protocol, IOService& service,
- const isc::dns::Question& question, const IOAddress& address,
- uint16_t port, isc::dns::OutputBufferPtr& buff, Callback* cb,
- int wait = -1);
-
- /// \brief Return Current Protocol
- ///
- /// \return Protocol associated with this IOFetch object.
- Protocol getProtocol() const;
-
- /// \brief Coroutine entry point
- ///
- /// The operator() method is the method in which the coroutine code enters
- /// this object when an operation has been completed.
- ///
- /// \param ec Error code, the result of the last asynchronous I/O operation.
- /// \param length Amount of data received on the last asynchronous read
- void operator()(asio::error_code ec = asio::error_code(), size_t length = 0);
-
- /// \brief Terminate query
- ///
- /// This method can be called at any point. It terminates the current
- /// query with the specified reason.
- ///
- /// \param reason Reason for terminating the query
- void stop(Result reason = STOPPED);
-
-private:
- /// \brief Log I/O Failure
- ///
- /// Records an I/O failure to the log file
- ///
- /// \param ec ASIO error code
- void logIOFailure(asio::error_code ec);
-
- // Member variables. All data is in a structure pointed to by a shared
- // pointer. The IOFetch object is copied a number of times during its
- // life, and only requiring a pointer to be copied reduces overhead.
- boost::shared_ptr<IOFetchData> data_; ///< Private data
-
-};
-
-} // namespace asiolink
-
-#endif // __IO_FETCH_H
diff --git a/src/lib/asiolink/io_message.h b/src/lib/asiolink/io_message.h
index e857bd9..81f6da1 100644
--- a/src/lib/asiolink/io_message.h
+++ b/src/lib/asiolink/io_message.h
@@ -28,6 +28,7 @@
#include <asiolink/io_endpoint.h>
#include <asiolink/io_socket.h>
+namespace isc {
namespace asiolink {
/// \brief The \c IOMessage class encapsulates an incoming message received
@@ -96,5 +97,6 @@ private:
};
-} // asiolink
+} // namespace asiolink
+} // namespace isc
#endif // __IO_MESSAGE_H
diff --git a/src/lib/asiolink/io_service.cc b/src/lib/asiolink/io_service.cc
index 55fc4b3..70cc18b 100644
--- a/src/lib/asiolink/io_service.cc
+++ b/src/lib/asiolink/io_service.cc
@@ -21,6 +21,7 @@
#include <asio.hpp>
#include <asiolink/io_service.h>
+namespace isc {
namespace asiolink {
class IOServiceImpl {
@@ -95,4 +96,5 @@ IOService::get_io_service() {
return (io_impl_->get_io_service());
}
-} // namepsace asiolink
+} // namespace asiolink
+} // namespace isc
diff --git a/src/lib/asiolink/io_service.h b/src/lib/asiolink/io_service.h
index 66558b7..75aaee6 100644
--- a/src/lib/asiolink/io_service.h
+++ b/src/lib/asiolink/io_service.h
@@ -19,9 +19,10 @@ namespace asio {
class io_service;
}
+namespace isc {
namespace asiolink {
-struct IOServiceImpl;
+class IOServiceImpl;
/// \brief The \c IOService class is a wrapper for the ASIO \c io_service
/// class.
@@ -73,5 +74,6 @@ private:
IOServiceImpl* io_impl_;
};
-} // namespace asiolink
+} // namespace asiolink
+} // namespace isc
#endif // __ASIOLINK_IO_SERVICE_H
diff --git a/src/lib/asiolink/io_socket.cc b/src/lib/asiolink/io_socket.cc
index fb325e9..e1498dc 100644
--- a/src/lib/asiolink/io_socket.cc
+++ b/src/lib/asiolink/io_socket.cc
@@ -1,5 +1,3 @@
-// Copyright (C) 2010 CZ NIC
-// Copyed from other version of auth/asiolink.cc which is:
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
@@ -18,8 +16,7 @@
#include <asio.hpp>
-using namespace asio;
-
+namespace isc {
namespace asiolink {
/// \brief The \c DummySocket class is a concrete derived class of
@@ -64,4 +61,5 @@ IOSocket::getDummyTCPSocket() {
return (socket);
}
-}
+} // namespace asiolink
+} // namespace isc
diff --git a/src/lib/asiolink/io_socket.h b/src/lib/asiolink/io_socket.h
index bebc8b6..ab6479c 100644
--- a/src/lib/asiolink/io_socket.h
+++ b/src/lib/asiolink/io_socket.h
@@ -25,6 +25,7 @@
#include <exceptions/exceptions.h>
+namespace isc {
namespace asiolink {
/// \brief The \c IOSocket class is an abstract base class to represent
@@ -120,5 +121,6 @@ public:
};
} // namespace asiolink
+} // namespace isc
#endif // __IO_SOCKET_H
diff --git a/src/lib/asiolink/qid_gen.cc b/src/lib/asiolink/qid_gen.cc
deleted file mode 100644
index 4063b39..0000000
--- a/src/lib/asiolink/qid_gen.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-// qid_gen defines a generator for query id's
-//
-// We probably want to merge this with the weighted random in the nsas
-// (and other parts where we need randomness, perhaps another thing
-// for a general libutil?)
-
-#include <asiolink/qid_gen.h>
-
-#include <sys/time.h>
-
-namespace {
- asiolink::QidGenerator qid_generator_instance;
-}
-
-namespace asiolink {
-
-QidGenerator&
-QidGenerator::getInstance() {
- return (qid_generator_instance);
-}
-
-QidGenerator::QidGenerator() : dist_(0, 65535),
- vgen_(generator_, dist_)
-{
- seed();
-}
-
-void
-QidGenerator::seed() {
- struct timeval tv;
- gettimeofday(&tv, 0);
- generator_.seed((tv.tv_sec * 1000000) + tv.tv_usec);
-}
-
-isc::dns::qid_t
-QidGenerator::generateQid() {
- return (vgen_());
-}
-
-} // namespace asiolink
diff --git a/src/lib/asiolink/qid_gen.h b/src/lib/asiolink/qid_gen.h
deleted file mode 100644
index a5caa17..0000000
--- a/src/lib/asiolink/qid_gen.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-// qid_gen defines a generator for query id's
-//
-// We probably want to merge this with the weighted random in the nsas
-// (and other parts where we need randomness, perhaps another thing
-// for a general libutil?)
-
-#ifndef __QID_GEN_H
-#define __QID_GEN_H
-
-#include <dns/message.h>
-#include <boost/random/mersenne_twister.hpp>
-#include <boost/random/uniform_int.hpp>
-#include <boost/random/variate_generator.hpp>
-
-
-namespace asiolink {
-
-/// This class generates Qids for outgoing queries
-///
-/// It is implemented as a singleton; the public way to access it
-/// is to call getInstance()->generateQid().
-///
-/// It automatically seeds it with the current time when it is first
-/// used.
-class QidGenerator {
-public:
- /// \brief Returns the singleton instance of the QidGenerator
- ///
- /// Returns a reference to the singleton instance of the generator
- static QidGenerator& getInstance();
-
- /// \brief Default constructor
- ///
- /// It is recommended that getInstance is used rather than creating
- /// separate instances of this class.
- ///
- /// The constructor automatically seeds the generator with the
- /// current time.
- QidGenerator();
-
- /// Generate a Qid
- ///
- /// \return A random Qid
- isc::dns::qid_t generateQid();
-
- /// \brief Seeds the QidGenerator (based on the current time)
- ///
- /// This is automatically called by the constructor
- void seed();
-
-private:
- // "Mersenne Twister: A 623-dimensionally equidistributed
- // uniform pseudo-random number generator", Makoto Matsumoto and
- // Takuji Nishimura, ACM Transactions on Modeling and Computer
- // Simulation: Special Issue on Uniform Random Number Generation,
- // Vol. 8, No. 1, January 1998, pp. 3-30.
- //
- // mt19937 is an implementation of one of the pseudo random
- // generators described in this paper.
- boost::mt19937 generator_;
-
- // For qid's we want a uniform distribution
- boost::uniform_int<> dist_;
-
- boost::variate_generator<boost::mt19937&, boost::uniform_int<> > vgen_;
-};
-
-
-} // namespace asiolink
-
-#endif // __QID_GEN_H
diff --git a/src/lib/asiolink/simple_callback.h b/src/lib/asiolink/simple_callback.h
index ab5deaf..92093ec 100644
--- a/src/lib/asiolink/simple_callback.h
+++ b/src/lib/asiolink/simple_callback.h
@@ -17,6 +17,7 @@
#include <asiolink/io_message.h>
+namespace isc {
namespace asiolink {
/// \brief The \c SimpleCallback class is an abstract base class for a
@@ -67,5 +68,6 @@ private:
SimpleCallback* self_;
};
-} // namespace asiolink
+} // namespace asiolink
+} // namespace isc
#endif // __ASIOLINK_SIMPLE_CALLBACK_H
diff --git a/src/lib/asiolink/tcp_endpoint.h b/src/lib/asiolink/tcp_endpoint.h
index 158ca4a..a54f6b2 100644
--- a/src/lib/asiolink/tcp_endpoint.h
+++ b/src/lib/asiolink/tcp_endpoint.h
@@ -21,6 +21,7 @@
#include <asiolink/io_endpoint.h>
+namespace isc {
namespace asiolink {
/// \brief The \c TCPEndpoint class is a concrete derived class of
@@ -83,6 +84,10 @@ public:
return (asio_endpoint_.address());
}
+ virtual const struct sockaddr& getSockAddr() const {
+ return (*asio_endpoint_.data());
+ }
+
virtual uint16_t getPort() const {
return (asio_endpoint_.port());
}
@@ -109,5 +114,10 @@ private:
asio::ip::tcp::endpoint& asio_endpoint_;
};
-} // namespace asiolink
+} // namespace asiolink
+} // namespace isc
#endif // __TCP_ENDPOINT_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/asiolink/tcp_server.cc b/src/lib/asiolink/tcp_server.cc
deleted file mode 100644
index 0bc56d8..0000000
--- a/src/lib/asiolink/tcp_server.cc
+++ /dev/null
@@ -1,239 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <config.h>
-
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <unistd.h> // for some IPC/network system calls
-#include <errno.h>
-
-#include <boost/shared_array.hpp>
-
-#include <log/dummylog.h>
-
-#include <asio.hpp>
-#include <asiolink/dummy_io_cb.h>
-#include <asiolink/tcp_endpoint.h>
-#include <asiolink/tcp_socket.h>
-#include <asiolink/tcp_server.h>
-
-
-using namespace asio;
-using asio::ip::udp;
-using asio::ip::tcp;
-
-using namespace std;
-using namespace isc::dns;
-
-namespace asiolink {
-
-/// The following functions implement the \c TCPServer class.
-///
-/// The constructor
-TCPServer::TCPServer(io_service& io_service,
- const ip::address& addr, const uint16_t port,
- const SimpleCallback* checkin,
- const DNSLookup* lookup,
- const DNSAnswer* answer) :
- io_(io_service), done_(false), stopped_by_hand_(false),
- checkin_callback_(checkin), lookup_callback_(lookup),
- answer_callback_(answer)
-{
- tcp::endpoint endpoint(addr, port);
- acceptor_.reset(new tcp::acceptor(io_service));
- acceptor_->open(endpoint.protocol());
- // Set v6-only (we use a separate instantiation for v4,
- // otherwise asio will bind to both v4 and v6
- if (addr.is_v6()) {
- acceptor_->set_option(ip::v6_only(true));
- }
- acceptor_->set_option(tcp::acceptor::reuse_address(true));
- acceptor_->bind(endpoint);
- acceptor_->listen();
-}
-
-void
-TCPServer::operator()(error_code ec, size_t length) {
- /// Because the coroutine reentry block is implemented as
- /// a switch statement, inline variable declarations are not
- /// permitted. Certain variables used below can be declared here.
-
- /// If user has stopped the server, we won't enter the
- /// coroutine body, just return
- if (stopped_by_hand_) {
- return;
- }
-
- boost::array<const_buffer,2> bufs;
- OutputBuffer lenbuf(TCP_MESSAGE_LENGTHSIZE);
-
- CORO_REENTER (this) {
- do {
- /// Create a socket to listen for connections
- socket_.reset(new tcp::socket(acceptor_->get_io_service()));
-
- /// Wait for new connections. In the event of non-fatal error,
- /// try again
- do {
- CORO_YIELD acceptor_->async_accept(*socket_, *this);
- // Abort on fatal errors
- // TODO: Log error?
- if (ec) {
- using namespace asio::error;
- if (ec.value() != would_block && ec.value() != try_again &&
- ec.value() != connection_aborted &&
- ec.value() != interrupted) {
- return;
- }
- }
- } while (ec);
-
- /// Fork the coroutine by creating a copy of this one and
- /// scheduling it on the ASIO service queue. The parent
- /// will continue listening for DNS connections while the
- /// handles the one that has just arrived.
- CORO_FORK io_.post(TCPServer(*this));
- } while (is_parent());
-
- /// Instantiate the data buffer that will be used by the
- /// asynchronous read call.
- data_.reset(new char[MAX_LENGTH]);
-
- /// Read the message, in two parts. First, the message length:
- CORO_YIELD async_read(*socket_, asio::buffer(data_.get(),
- TCP_MESSAGE_LENGTHSIZE), *this);
- if (ec) {
- socket_->close();
- CORO_YIELD return;
- }
-
- /// Now read the message itself. (This is done in a different scope
- /// to allow inline variable declarations.)
- CORO_YIELD {
- InputBuffer dnsbuffer(data_.get(), length);
- uint16_t msglen = dnsbuffer.readUint16();
- async_read(*socket_, asio::buffer(data_.get(), msglen), *this);
- }
-
- if (ec) {
- socket_->close();
- CORO_YIELD return;
- }
-
- // Create an \c IOMessage object to store the query.
- //
- // (XXX: It would be good to write a factory function
- // that would quickly generate an IOMessage object without
- // all these calls to "new".)
- peer_.reset(new TCPEndpoint(socket_->remote_endpoint()));
-
- // The TCP socket class has been extended with asynchronous functions
- // and takes as a template parameter a completion callback class. As
- // TCPServer does not use these extended functions (only those defined
- // in the IOSocket base class) - but needs a TCPSocket to get hold of
- // the underlying Boost TCP socket - DummyIOCallback is used. This
- // provides the appropriate operator() but is otherwise functionless.
- iosock_.reset(new TCPSocket<DummyIOCallback>(*socket_));
- io_message_.reset(new IOMessage(data_.get(), length, *iosock_, *peer_));
- bytes_ = length;
-
- // Perform any necessary operations prior to processing the incoming
- // packet (e.g., checking for queued configuration messages).
- //
- // (XXX: it may be a performance issue to have this called for
- // every single incoming packet; we may wish to throttle it somehow
- // in the future.)
- if (checkin_callback_ != NULL) {
- (*checkin_callback_)(*io_message_);
- }
-
- // If we don't have a DNS Lookup provider, there's no point in
- // continuing; we exit the coroutine permanently.
- if (lookup_callback_ == NULL) {
- socket_->close();
- CORO_YIELD return;
- }
-
- // Reset or instantiate objects that will be needed by the
- // DNS lookup and the write call.
- respbuf_.reset(new OutputBuffer(0));
- query_message_.reset(new Message(Message::PARSE));
- answer_message_.reset(new Message(Message::RENDER));
-
- // Schedule a DNS lookup, and yield. When the lookup is
- // finished, the coroutine will resume immediately after
- // this point.
- CORO_YIELD io_.post(AsyncLookup<TCPServer>(*this));
-
- // The 'done_' flag indicates whether we have an answer
- // to send back. If not, exit the coroutine permanently.
- if (!done_) {
- // TODO: should we keep the connection open for a short time
- // to see if new requests come in?
- socket_->close();
- CORO_YIELD return;
- }
-
- // Call the DNS answer provider to render the answer into
- // wire format
- (*answer_callback_)(*io_message_, query_message_,
- answer_message_, respbuf_);
-
- // Set up the response, beginning with two length bytes.
- lenbuf.writeUint16(respbuf_->getLength());
- bufs[0] = buffer(lenbuf.getData(), lenbuf.getLength());
- bufs[1] = buffer(respbuf_->getData(), respbuf_->getLength());
-
- // Begin an asynchronous send, and then yield. When the
- // send completes, we will resume immediately after this point
- // (though we have nothing further to do, so the coroutine
- // will simply exit at that time).
- CORO_YIELD async_write(*socket_, bufs, *this);
-
- // TODO: should we keep the connection open for a short time
- // to see if new requests come in?
- socket_->close();
- }
-}
-
-/// Call the DNS lookup provider. (Expected to be called by the
-/// AsyncLookup<TCPServer> handler.)
-void
-TCPServer::asyncLookup() {
- (*lookup_callback_)(*io_message_, query_message_,
- answer_message_, respbuf_, this);
-}
-
-void TCPServer::stop() {
- // server should not be stopped twice
- if (stopped_by_hand_) {
- return;
- }
-
- stopped_by_hand_ = true;
- acceptor_->close();
- socket_->close();
-}
-/// Post this coroutine on the ASIO service queue so that it will
-/// resume processing where it left off. The 'done' parameter indicates
-/// whether there is an answer to return to the client.
-void
-TCPServer::resume(const bool done) {
- done_ = done;
- io_.post(*this);
-}
-
-} // namespace asiolink
-
diff --git a/src/lib/asiolink/tcp_server.h b/src/lib/asiolink/tcp_server.h
deleted file mode 100644
index 9df335d..0000000
--- a/src/lib/asiolink/tcp_server.h
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __TCP_SERVER_H
-#define __TCP_SERVER_H 1
-
-#ifndef ASIO_HPP
-#error "asio.hpp must be included before including this, see asiolink.h as to why"
-#endif
-
-#include <boost/shared_array.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include <asiolink/asiolink.h>
-#include <coroutine.h>
-
-
-namespace asiolink {
-
-/// \brief A TCP-specific \c DNSServer object.
-///
-/// This class inherits from both \c DNSServer and from \c coroutine,
-/// defined in coroutine.h.
-class TCPServer : public virtual DNSServer, public virtual coroutine {
-public:
- explicit TCPServer(asio::io_service& io_service,
- const asio::ip::address& addr, const uint16_t port,
- const SimpleCallback* checkin = NULL,
- const DNSLookup* lookup = NULL,
- const DNSAnswer* answer = NULL);
-
- void operator()(asio::error_code ec = asio::error_code(),
- size_t length = 0);
- void asyncLookup();
- void stop();
- void resume(const bool done);
- bool hasAnswer() { return (done_); }
- int value() { return (get_value()); }
-
- DNSServer* clone() {
- TCPServer* s = new TCPServer(*this);
- return (s);
- }
-
-private:
- enum { MAX_LENGTH = 65535 };
- static const size_t TCP_MESSAGE_LENGTHSIZE = 2;
-
- // The ASIO service object
- asio::io_service& io_;
-
- // Class member variables which are dynamic, and changes to which
- // need to accessible from both sides of a coroutine fork or from
- // outside of the coroutine (i.e., from an asynchronous I/O call),
- // should be declared here as pointers and allocated in the
- // constructor or in the coroutine. This allows state information
- // to persist when an individual copy of the coroutine falls out
- // scope while waiting for an event, *so long as* there is another
- // object that is referencing the same data. As a side-benefit, using
- // pointers also reduces copy overhead for coroutine objects.
- //
- // Note: Currently these objects are allocated by "new" in the
- // constructor, or in the function operator while processing a query.
- // Repeated allocations from the heap for every incoming query is
- // clearly a performance issue; this must be optimized in the future.
- // The plan is to have a structure pre-allocate several "server state"
- // objects which can be pulled off a free list and placed on an in-use
- // list whenever a query comes in. This will serve the dual purpose
- // of improving performance and guaranteeing that state information
- // will *not* be destroyed when any one instance of the coroutine
- // falls out of scope while waiting for an event.
- //
- // An ASIO acceptor object to handle new connections. Created in
- // the constructor.
- boost::shared_ptr<asio::ip::tcp::acceptor> acceptor_;
-
- // Socket used to for listen for queries. Created in the
- // constructor and stored in a shared_ptr because socket objects
- // are not copyable.
- boost::shared_ptr<asio::ip::tcp::socket> socket_;
-
- // The buffer into which the response is written
- boost::shared_ptr<isc::dns::OutputBuffer> respbuf_;
-
- // \c IOMessage and \c Message objects to be passed to the
- // DNS lookup and answer providers
- boost::shared_ptr<asiolink::IOMessage> io_message_;
- isc::dns::MessagePtr query_message_;
- isc::dns::MessagePtr answer_message_;
-
- // The buffer into which the query packet is written
- boost::shared_array<char>data_;
-
- // State information that is entirely internal to a given instance
- // of the coroutine can be declared here.
- size_t bytes_;
- bool done_;
-
- // whether user has stopped the server
- bool stopped_by_hand_;
-
- // Callback functions provided by the caller
- const SimpleCallback* checkin_callback_;
- const DNSLookup* lookup_callback_;
- const DNSAnswer* answer_callback_;
-
- boost::shared_ptr<IOEndpoint> peer_;
- boost::shared_ptr<IOSocket> iosock_;
-};
-
-} // namespace asiolink
-#endif // __TCP_SERVER_H
diff --git a/src/lib/asiolink/tcp_socket.h b/src/lib/asiolink/tcp_socket.h
index e6e0863..c8876c8 100644
--- a/src/lib/asiolink/tcp_socket.h
+++ b/src/lib/asiolink/tcp_socket.h
@@ -33,14 +33,15 @@
#include <config.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
+#include <util/io_utilities.h>
-#include <asiolink/asiolink_utilities.h>
#include <asiolink/io_asio_socket.h>
#include <asiolink/io_endpoint.h>
#include <asiolink/io_service.h>
#include <asiolink/tcp_endpoint.h>
+namespace isc {
namespace asiolink {
/// \brief Buffer Too Large
@@ -155,7 +156,7 @@ public:
virtual bool processReceivedData(const void* staging, size_t length,
size_t& cumulative, size_t& offset,
size_t& expected,
- isc::dns::OutputBufferPtr& outbuff);
+ isc::util::OutputBufferPtr& outbuff);
/// \brief Cancel I/O On Socket
virtual void cancel();
@@ -185,7 +186,7 @@ private:
// The option of sending the data in two operations, the count followed by
// the data was discounted as that would lead to two callbacks which would
// cause problems with the stackless coroutine code.
- isc::dns::OutputBufferPtr send_buffer_; ///< Send buffer
+ isc::util::OutputBufferPtr send_buffer_; ///< Send buffer
};
// Constructor - caller manages socket
@@ -268,7 +269,7 @@ TCPSocket<C>::asyncSend(const void* data, size_t length,
uint16_t count = boost::numeric_cast<uint16_t>(length);
// Copy data into a buffer preceded by the count field.
- send_buffer_.reset(new isc::dns::OutputBuffer(length + 2));
+ send_buffer_.reset(new isc::util::OutputBuffer(length + 2));
send_buffer_->writeUint16(count);
send_buffer_->writeData(data, length);
@@ -333,7 +334,7 @@ template <typename C> bool
TCPSocket<C>::processReceivedData(const void* staging, size_t length,
size_t& cumulative, size_t& offset,
size_t& expected,
- isc::dns::OutputBufferPtr& outbuff)
+ isc::util::OutputBufferPtr& outbuff)
{
// Point to the data in the staging buffer and note how much there is.
const uint8_t* data = static_cast<const uint8_t*>(staging);
@@ -358,7 +359,7 @@ TCPSocket<C>::processReceivedData(const void* staging, size_t length,
}
// Have enough data to interpret the packet count, so do so now.
- expected = readUint16(data);
+ expected = isc::util::readUint16(data);
// 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,
@@ -412,5 +413,6 @@ TCPSocket<C>::close() {
}
} // namespace asiolink
+} // namespace isc
#endif // __TCP_SOCKET_H
diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am
index 000e6f2..984cf07 100644
--- a/src/lib/asiolink/tests/Makefile.am
+++ b/src/lib/asiolink/tests/Makefile.am
@@ -1,6 +1,6 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/util -I$(top_srcdir)/src/util
AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\"
@@ -10,34 +10,33 @@ if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
-run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
-run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
-run_unittests_SOURCES += asiolink_utilities_unittest.cc
run_unittests_SOURCES += io_address_unittest.cc
run_unittests_SOURCES += io_endpoint_unittest.cc
-run_unittests_SOURCES += io_fetch_unittest.cc
run_unittests_SOURCES += io_socket_unittest.cc
-run_unittests_SOURCES += io_service_unittest.cc
run_unittests_SOURCES += interval_timer_unittest.cc
run_unittests_SOURCES += tcp_endpoint_unittest.cc
run_unittests_SOURCES += tcp_socket_unittest.cc
run_unittests_SOURCES += udp_endpoint_unittest.cc
run_unittests_SOURCES += udp_socket_unittest.cc
-run_unittests_SOURCES += qid_gen_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(SQLITE_LIBS)
-run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
diff --git a/src/lib/asiolink/tests/asiolink_utilities_unittest.cc b/src/lib/asiolink/tests/asiolink_utilities_unittest.cc
deleted file mode 100644
index 51f565f..0000000
--- a/src/lib/asiolink/tests/asiolink_utilities_unittest.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-/// \brief Test of asiolink utilties
-///
-/// Tests the fuctionality of the asiolink utilities code by comparing them
-/// with the equivalent methods in isc::dns::[Input/Output]Buffer.
-
-#include <cstddef>
-
-#include <gtest/gtest.h>
-
-#include <dns/buffer.h>
-#include <asiolink/asiolink_utilities.h>
-
-using namespace asiolink;
-using namespace isc::dns;
-
-TEST(asioutil, readUint16) {
-
- // Reference buffer
- uint8_t data[2];
- isc::dns::InputBuffer buffer(data, sizeof(data));
-
- // Avoid possible compiler warnings by only setting uint8_t variables to
- // uint8_t values.
- uint8_t i8 = 0;
- uint8_t j8 = 0;
- for (int i = 0; i < (2 << 8); ++i, ++i8) {
- for (int j = 0; j < (2 << 8); ++j, ++j8) {
- data[0] = i8;
- data[1] = j8;
- buffer.setPosition(0);
- EXPECT_EQ(buffer.readUint16(), readUint16(data));
- }
- }
-}
-
-
-TEST(asioutil, writeUint16) {
-
- // Reference buffer
- isc::dns::OutputBuffer buffer(2);
- uint8_t test[2];
-
- // Avoid possible compiler warnings by only setting uint16_t variables to
- // uint16_t values.
- uint16_t i16 = 0;
- for (uint32_t i = 0; i < (2 << 16); ++i, ++i16) {
-
- // Write the reference data
- buffer.clear();
- buffer.writeUint16(i16);
-
- // ... and the test data
- writeUint16(i16, test);
-
- // ... and compare
- const uint8_t* ref = static_cast<const uint8_t*>(buffer.getData());
- EXPECT_EQ(ref[0], test[0]);
- EXPECT_EQ(ref[1], test[1]);
- }
-}
diff --git a/src/lib/asiolink/tests/interval_timer_unittest.cc b/src/lib/asiolink/tests/interval_timer_unittest.cc
index 7e0e7bc..8e8ef81 100644
--- a/src/lib/asiolink/tests/interval_timer_unittest.cc
+++ b/src/lib/asiolink/tests/interval_timer_unittest.cc
@@ -18,7 +18,7 @@
#include <asio.hpp>
#include <asiolink/asiolink.h>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
namespace {
// TODO: Consider this margin
@@ -26,7 +26,7 @@ const boost::posix_time::time_duration TIMER_MARGIN_MSEC =
boost::posix_time::milliseconds(50);
}
-using namespace asiolink;
+using namespace isc::asiolink;
// This fixture is for testing IntervalTimer. Some callback functors are
// registered as callback function of the timer to test if they are called
@@ -166,16 +166,22 @@ TEST_F(IntervalTimerTest, startIntervalTimer) {
io_service_.run();
// reaches here after timer expired
// delta: difference between elapsed time and 100 milliseconds.
+ boost::posix_time::time_duration test_runtime =
+ boost::posix_time::microsec_clock::universal_time() - start;
+ EXPECT_FALSE(test_runtime.is_negative()) <<
+ "test duration " << test_runtime <<
+ " negative - clock skew?";
boost::posix_time::time_duration delta =
- (boost::posix_time::microsec_clock::universal_time() - start)
- - boost::posix_time::millisec(100);
+ test_runtime - boost::posix_time::milliseconds(100);
if (delta.is_negative()) {
delta.invert_sign();
}
// expect TimerCallBack is called; timer_called_ is true
EXPECT_TRUE(timer_called_);
// expect interval is 100 milliseconds +/- TIMER_MARGIN_MSEC.
- EXPECT_TRUE(delta < TIMER_MARGIN_MSEC);
+ EXPECT_TRUE(delta < TIMER_MARGIN_MSEC) <<
+ "delta " << delta.total_milliseconds() << "msec " <<
+ ">= " << TIMER_MARGIN_MSEC.total_milliseconds();
}
TEST_F(IntervalTimerTest, destructIntervalTimer) {
@@ -283,14 +289,20 @@ TEST_F(IntervalTimerTest, overwriteIntervalTimer) {
// + 400 milliseconds for TimerCallBackOverwriter (stop)
// = 800 milliseconds.
// delta: difference between elapsed time and 400 + 100 milliseconds
+ boost::posix_time::time_duration test_runtime =
+ boost::posix_time::microsec_clock::universal_time() - start;
+ EXPECT_FALSE(test_runtime.is_negative()) <<
+ "test duration " << test_runtime <<
+ " negative - clock skew?";
boost::posix_time::time_duration delta =
- (boost::posix_time::microsec_clock::universal_time() - start)
- - boost::posix_time::millisec(400 + 100);
+ test_runtime - boost::posix_time::milliseconds(400 + 100);
if (delta.is_negative()) {
delta.invert_sign();
}
// expect callback function is updated: TimerCallBack is called
EXPECT_TRUE(timer_called_);
// expect interval is updated
- EXPECT_TRUE(delta < TIMER_MARGIN_MSEC);
+ EXPECT_TRUE(delta < TIMER_MARGIN_MSEC) <<
+ "delta " << delta.total_milliseconds() << " msec " <<
+ ">= " << TIMER_MARGIN_MSEC.total_milliseconds();
}
diff --git a/src/lib/asiolink/tests/io_address_unittest.cc b/src/lib/asiolink/tests/io_address_unittest.cc
index 894f143..18b181e 100644
--- a/src/lib/asiolink/tests/io_address_unittest.cc
+++ b/src/lib/asiolink/tests/io_address_unittest.cc
@@ -18,7 +18,7 @@
#include <asiolink/io_error.h>
#include <asiolink/io_address.h>
-using namespace asiolink;
+using namespace isc::asiolink;
TEST(IOAddressTest, fromText) {
IOAddress io_address_v4("192.0.2.1");
diff --git a/src/lib/asiolink/tests/io_endpoint_unittest.cc b/src/lib/asiolink/tests/io_endpoint_unittest.cc
index 6101473..f0279d1 100644
--- a/src/lib/asiolink/tests/io_endpoint_unittest.cc
+++ b/src/lib/asiolink/tests/io_endpoint_unittest.cc
@@ -15,14 +15,25 @@
#include <config.h>
#include <gtest/gtest.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <string.h>
+
+#include <boost/shared_ptr.hpp>
+
#include <asiolink/io_endpoint.h>
#include <asiolink/io_error.h>
-using namespace asiolink;
+using boost::shared_ptr;
+using namespace isc::asiolink;
+
+namespace {
+typedef shared_ptr<const IOEndpoint> ConstIOEndpointPtr;
TEST(IOEndpointTest, createUDPv4) {
- const IOEndpoint* ep;
- ep = IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.1"), 53210);
+ ConstIOEndpointPtr ep(IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("192.0.2.1"), 53210));
EXPECT_EQ("192.0.2.1", ep->getAddress().toText());
EXPECT_EQ(53210, ep->getPort());
EXPECT_EQ(AF_INET, ep->getFamily());
@@ -31,8 +42,8 @@ TEST(IOEndpointTest, createUDPv4) {
}
TEST(IOEndpointTest, createTCPv4) {
- const IOEndpoint* ep;
- ep = IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.1"), 5301);
+ ConstIOEndpointPtr ep(IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("192.0.2.1"), 5301));
EXPECT_EQ("192.0.2.1", ep->getAddress().toText());
EXPECT_EQ(5301, ep->getPort());
EXPECT_EQ(AF_INET, ep->getFamily());
@@ -41,8 +52,9 @@ TEST(IOEndpointTest, createTCPv4) {
}
TEST(IOEndpointTest, createUDPv6) {
- const IOEndpoint* ep;
- ep = IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::1234"), 5302);
+ ConstIOEndpointPtr ep(IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1234"),
+ 5302));
EXPECT_EQ("2001:db8::1234", ep->getAddress().toText());
EXPECT_EQ(5302, ep->getPort());
EXPECT_EQ(AF_INET6, ep->getFamily());
@@ -51,8 +63,9 @@ TEST(IOEndpointTest, createUDPv6) {
}
TEST(IOEndpointTest, createTCPv6) {
- const IOEndpoint* ep;
- ep = IOEndpoint::create(IPPROTO_TCP, IOAddress("2001:db8::1234"), 5303);
+ ConstIOEndpointPtr ep(IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1234"),
+ 5303));
EXPECT_EQ("2001:db8::1234", ep->getAddress().toText());
EXPECT_EQ(5303, ep->getPort());
EXPECT_EQ(AF_INET6, ep->getFamily());
@@ -60,9 +73,172 @@ TEST(IOEndpointTest, createTCPv6) {
EXPECT_EQ(IPPROTO_TCP, ep->getProtocol());
}
+TEST(IOEndpointTest, equality) {
+ std::vector<ConstIOEndpointPtr> epv;
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1234"), 5303)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1234"), 5303)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1234"), 5304)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1234"), 5304)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1235"), 5303)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1235"), 5303)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1235"), 5304)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1235"), 5304)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("192.0.2.1"), 5303)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("192.0.2.1"), 5303)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("192.0.2.1"), 5304)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("192.0.2.1"), 5304)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("192.0.2.2"), 5303)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("192.0.2.2"), 5303)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("192.0.2.2"), 5304)));
+ epv.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("192.0.2.2"), 5304)));
+
+ for (size_t i = 0; i < epv.size(); ++i) {
+ for (size_t j = 0; j < epv.size(); ++j) {
+ if (i != j) {
+ // We use EXPECT_TRUE/FALSE instead of _EQ here, since
+ // _EQ requires there is an operator<< as well
+ EXPECT_FALSE(*epv[i] == *epv[j]);
+ EXPECT_TRUE(*epv[i] != *epv[j]);
+ }
+ }
+ }
+
+ // Create a second array with exactly the same values. We use create()
+ // again to make sure we get different endpoints
+ std::vector<ConstIOEndpointPtr> epv2;
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1234"), 5303)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1234"), 5303)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1234"), 5304)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1234"), 5304)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1235"), 5303)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1235"), 5303)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1235"), 5304)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("2001:db8::1235"), 5304)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("192.0.2.1"), 5303)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("192.0.2.1"), 5303)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.1"),
+ 5304)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.1"),
+ 5304)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.2"),
+ 5303)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.2"),
+ 5303)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.2"),
+ 5304)));
+ epv2.push_back(ConstIOEndpointPtr(
+ IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.2"),
+ 5304)));
+
+ for (size_t i = 0; i < epv.size(); ++i) {
+ EXPECT_TRUE(*epv[i] == *epv2[i]);
+ EXPECT_FALSE(*epv[i] != *epv2[i]);
+ }
+}
+
TEST(IOEndpointTest, createIPProto) {
EXPECT_THROW(IOEndpoint::create(IPPROTO_IP, IOAddress("192.0.2.1"),
53210)->getAddress().toText(),
IOError);
}
+void
+sockAddrMatch(const struct sockaddr& actual_sa,
+ const char* const expected_addr_text,
+ const char* const expected_port_text)
+{
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM; // this shouldn't matter
+ hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+
+ struct addrinfo* res;
+ ASSERT_EQ(0, getaddrinfo(expected_addr_text, expected_port_text, &hints,
+ &res));
+ EXPECT_EQ(res->ai_family, actual_sa.sa_family);
+#ifdef HAVE_SA_LEN
+ // ASIO doesn't seem to set sa_len, so we set it to the expected value
+ res->ai_addr->sa_len = actual_sa.sa_len;
+#endif
+ EXPECT_EQ(0, memcmp(res->ai_addr, &actual_sa, res->ai_addrlen));
+ free(res);
+}
+
+TEST(IOEndpointTest, getSockAddr) {
+ // UDP/IPv4
+ ConstIOEndpointPtr ep(IOEndpoint::create(IPPROTO_UDP,
+ IOAddress("192.0.2.1"), 53210));
+ sockAddrMatch(ep->getSockAddr(), "192.0.2.1", "53210");
+
+ // UDP/IPv6
+ ep.reset(IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::53"), 53));
+ sockAddrMatch(ep->getSockAddr(), "2001:db8::53", "53");
+
+ // TCP/IPv4
+ ep.reset(IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.2"), 53211));
+ sockAddrMatch(ep->getSockAddr(), "192.0.2.2", "53211");
+
+ // TCP/IPv6
+ ep.reset(IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::5300"), 35));
+ sockAddrMatch(ep->getSockAddr(), "2001:db8::5300", "35");
+}
+
+}
diff --git a/src/lib/asiolink/tests/io_fetch_unittest.cc b/src/lib/asiolink/tests/io_fetch_unittest.cc
deleted file mode 100644
index 901df45..0000000
--- a/src/lib/asiolink/tests/io_fetch_unittest.cc
+++ /dev/null
@@ -1,608 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <algorithm>
-#include <cstdlib>
-#include <string>
-#include <iostream>
-#include <iomanip>
-#include <iterator>
-#include <vector>
-
-#include <gtest/gtest.h>
-#include <boost/bind.hpp>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
-
-#include <asio.hpp>
-
-#include <dns/buffer.h>
-#include <dns/question.h>
-#include <dns/message.h>
-#include <dns/messagerenderer.h>
-#include <dns/opcode.h>
-#include <dns/name.h>
-#include <dns/rcode.h>
-
-#include <asiolink/asiolink_utilities.h>
-#include <asiolink/io_address.h>
-#include <asiolink/io_endpoint.h>
-#include <asiolink/io_fetch.h>
-#include <asiolink/io_service.h>
-
-using namespace asio;
-using namespace isc::dns;
-using namespace asio::ip;
-using namespace std;
-
-namespace asiolink {
-
-const asio::ip::address TEST_HOST(asio::ip::address::from_string("127.0.0.1"));
-const uint16_t TEST_PORT(5301);
-const int SEND_INTERVAL = 250; // Interval in ms between TCP sends
-const size_t MAX_SIZE = 64 * 1024; // Should be able to take 64kB
-
-// The tests are complex, so debug output has been left in (although disabled).
-// Set this to true to enable it.
-const bool DEBUG = false;
-
-/// \brief Test fixture for the asiolink::IOFetch.
-class IOFetchTest : public virtual ::testing::Test, public virtual IOFetch::Callback
-{
-public:
- IOService service_; ///< Service to run the query
- IOFetch::Result expected_; ///< Expected result of the callback
- bool run_; ///< Did the callback run already?
- Question question_; ///< What to ask
- OutputBufferPtr result_buff_; ///< Buffer to hold result of fetch
- OutputBufferPtr msgbuf_; ///< Buffer corresponding to known question
- IOFetch udp_fetch_; ///< For UDP query test
- IOFetch tcp_fetch_; ///< For TCP query test
- IOFetch::Protocol protocol_; ///< Protocol being tested
- size_t cumulative_; ///< Cumulative data received by "server".
- deadline_timer timer_; ///< Timer to measure timeouts
-
- // The next member is the buffer in which the "server" (implemented by the
- // response handler methods in this class) receives the question sent by the
- // fetch object.
- uint8_t receive_buffer_[MAX_SIZE]; ///< Server receive buffer
- vector<uint8_t> send_buffer_; ///< Server send buffer
- uint16_t send_cumulative_; ///< Data sent so far
-
- // Other data.
- string return_data_; ///< Data returned by server
- string test_data_; ///< Large string - here for convenience
- bool debug_; ///< true to enable debug output
- size_t tcp_send_size_; ///< Max size of TCP send
-
- /// \brief Constructor
- IOFetchTest() :
- service_(),
- expected_(IOFetch::NOTSET),
- run_(false),
- question_(Name("example.net"), RRClass::IN(), RRType::A()),
- result_buff_(new OutputBuffer(512)),
- msgbuf_(new OutputBuffer(512)),
- udp_fetch_(IOFetch::UDP, service_, question_, IOAddress(TEST_HOST),
- TEST_PORT, result_buff_, this, 100),
- tcp_fetch_(IOFetch::TCP, service_, question_, IOAddress(TEST_HOST),
- TEST_PORT, result_buff_, this, (16 * SEND_INTERVAL)),
- // Timeout interval chosen to ensure no timeout
- protocol_(IOFetch::TCP), // for initialization - will be changed
- cumulative_(0),
- timer_(service_.get_io_service()),
- receive_buffer_(),
- send_buffer_(),
- send_cumulative_(0),
- return_data_(""),
- test_data_(""),
- debug_(DEBUG),
- tcp_send_size_(0)
- {
- // Construct the data buffer for question we expect to receive.
- Message msg(Message::RENDER);
- msg.setQid(0);
- msg.setOpcode(Opcode::QUERY());
- msg.setRcode(Rcode::NOERROR());
- msg.setHeaderFlag(Message::HEADERFLAG_RD);
- msg.addQuestion(question_);
- MessageRenderer renderer(*msgbuf_);
- msg.toWire(renderer);
-
- // Initialize the test data to be returned: tests will return a
- // substring of this data. (It's convenient to have this as a member of
- // the class.)
- //
- // We could initialize the data with a single character, but as an added
- // check we'll make ssre that it has some structure.
-
- test_data_.clear();
- test_data_.reserve(MAX_SIZE);
- while (test_data_.size() < MAX_SIZE) {
- test_data_ += "A message to be returned to the client that has "
- "some sort of structure.";
- }
- }
-
- /// \brief UDP Response handler (the "remote UDP DNS server")
- ///
- /// When IOFetch is sending data, this response handler emulates the remote
- /// DNS server. It checks that the data sent by the IOFetch object is what
- /// was expected to have been sent, then sends back a known buffer of data.
- ///
- /// \param remote Endpoint to which to send the answer
- /// \param socket Socket to use to send the answer
- /// \param ec ASIO error code, completion code of asynchronous I/O issued
- /// by the "server" to receive data.
- /// \param length Amount of data received.
- void udpReceiveHandler(udp::endpoint* remote, udp::socket* socket,
- error_code ec = error_code(), size_t length = 0) {
- if (debug_) {
- cout << "udpReceiveHandler(): error = " << ec.value() <<
- ", length = " << length << endl;
- }
-
- // The QID in the incoming data is random so set it to 0 for the
- // data comparison check. (It is set to 0 in the buffer containing
- // the expected data.)
- receive_buffer_[0] = receive_buffer_[1] = 0;
-
- // Check that length of the received data and the expected data are
- // identical, then check that the data is identical as well.
- EXPECT_EQ(msgbuf_->getLength(), length);
- EXPECT_TRUE(equal(receive_buffer_, (receive_buffer_ + length - 1),
- static_cast<const uint8_t*>(msgbuf_->getData())));
-
- // Return a message back to the IOFetch object.
- socket->send_to(asio::buffer(return_data_.c_str(), return_data_.size()),
- *remote);
- if (debug_) {
- cout << "udpReceiveHandler(): returned " << return_data_.size() <<
- " bytes to the client" << endl;
- }
- }
-
- /// \brief Completion Handler for accepting TCP data
- ///
- /// Called when the remote system connects to the "server". It issues
- /// an asynchronous read on the socket to read data.
- ///
- /// \param socket Socket on which data will be received
- /// \param ec Boost error code, value should be zero.
- void tcpAcceptHandler(tcp::socket* socket, error_code ec = error_code())
- {
- if (debug_) {
- cout << "tcpAcceptHandler(): error = " << ec.value() << endl;
- }
-
- // Expect that the accept completed without a problem.
- EXPECT_EQ(0, ec.value());
-
- // Work out the maximum size of data we can send over it when we
- // respond, then subtract 1kB or so for safety.
- tcp::socket::send_buffer_size send_size;
- socket->get_option(send_size);
- if (send_size.value() < (2 * 1024)) {
- FAIL() << "TCP send size is less than 2kB";
- } else {
- tcp_send_size_ = send_size.value() - 1024;
- if (debug_) {
- cout << "tcpacceptHandler(): will use send size = " << tcp_send_size_ << endl;
- }
- }
-
- // Initiate a read on the socket.
- cumulative_ = 0;
- socket->async_receive(asio::buffer(receive_buffer_, sizeof(receive_buffer_)),
- boost::bind(&IOFetchTest::tcpReceiveHandler, this, socket, _1, _2));
- }
-
- /// \brief Completion handler for receiving TCP data
- ///
- /// When IOFetch is sending data, this response handler emulates the remote
- /// DNS server. It that all the data sent by the IOFetch object has been
- /// received, issuing another read if not. If the data is complete, it is
- /// compared to what is expected and a reply sent back to the IOFetch.
- ///
- /// \param socket Socket to use to send the answer
- /// \param ec ASIO error code, completion code of asynchronous I/O issued
- /// by the "server" to receive data.
- /// \param length Amount of data received.
- void tcpReceiveHandler(tcp::socket* socket, error_code ec = error_code(),
- size_t length = 0)
- {
- if (debug_) {
- cout << "tcpReceiveHandler(): error = " << ec.value() <<
- ", length = " << length << endl;
- }
- // Expect that the receive completed without a problem.
- EXPECT_EQ(0, ec.value());
-
- // If we haven't received all the data, issue another read.
- cumulative_ += length;
- bool complete = false;
- if (cumulative_ > 2) {
- uint16_t dns_length = readUint16(receive_buffer_);
- complete = ((dns_length + 2) == cumulative_);
- }
-
- if (!complete) {
- socket->async_receive(asio::buffer((receive_buffer_ + cumulative_),
- (sizeof(receive_buffer_) - cumulative_)),
- boost::bind(&IOFetchTest::tcpReceiveHandler, this, socket, _1, _2));
- return;
- }
-
- // Check that length of the DNS message received is that expected, then
- // compare buffers, zeroing the QID in the received buffer to match
- // that set in our expected question. Note that due to the length
- // field the QID in the received buffer is in the third and fourth
- // bytes.
- EXPECT_EQ(msgbuf_->getLength() + 2, cumulative_);
- receive_buffer_[2] = receive_buffer_[3] = 0;
- EXPECT_TRUE(equal((receive_buffer_ + 2), (receive_buffer_ + cumulative_ - 2),
- static_cast<const uint8_t*>(msgbuf_->getData())));
-
- // ... and return a message back. This has to be preceded by a two-byte
- // count field.
- send_buffer_.clear();
- send_buffer_.push_back(0);
- send_buffer_.push_back(0);
- writeUint16(return_data_.size(), &send_buffer_[0]);
- copy(return_data_.begin(), return_data_.end(), back_inserter(send_buffer_));
-
- // Send the data. This is done in multiple writes with a delay between
- // each to check that the reassembly of TCP packets from fragments works.
- send_cumulative_ = 0;
- tcpSendData(socket);
- }
-
- /// \brief Sent Data Over TCP
- ///
- /// Send the TCP data back to the IOFetch object. The data is sent in
- /// three chunks - two of 16 bytes and the remainder, with a 250ms gap
- /// between each. (Amounts of data smaller than one 32 bytes are sent in
- /// one or two packets.)
- ///
- /// \param socket Socket over which send should take place
- void tcpSendData(tcp::socket* socket) {
- if (debug_) {
- cout << "tcpSendData()" << endl;
- }
-
- // Decide what to send based on the cumulative count. At most we'll do
- // two chunks of 16 bytes (with a 250ms gap between) and then the
- // remainder.
- uint8_t* send_ptr = &send_buffer_[send_cumulative_];
- // Pointer to data to send
- size_t amount = 16; // Amount of data to send
- if (send_cumulative_ < (2 * amount)) {
-
- // First or second time through, send at most 16 bytes
- amount = min(amount, (send_buffer_.size() - send_cumulative_));
-
- } else {
-
- // For all subsequent times, send the remainder, maximised to
- // whatever we have chosen for the maximum send size.
- amount = min(tcp_send_size_,
- (send_buffer_.size() - send_cumulative_));
- }
- if (debug_) {
- cout << "tcpSendData(): sending " << amount << " bytes" << endl;
- }
-
-
- // ... and send it. The amount sent is also passed as the first
- // argument of the send callback, as a check.
- socket->async_send(asio::buffer(send_ptr, amount),
- boost::bind(&IOFetchTest::tcpSendHandler, this,
- amount, socket, _1, _2));
- }
-
- /// \brief Completion Handler for Sending TCP data
- ///
- /// Called when the asynchronous send of data back to the IOFetch object
- /// by the TCP "server" in this class has completed. (This send has to
- /// be asynchronous because control needs to return to the caller in order
- /// for the IOService "run()" method to be called to run the handlers.)
- ///
- /// If not all the data has been sent, a short delay is instigated (during
- /// which control returns to the IOService). This should force the queued
- /// data to actually be sent and the IOFetch receive handler to be triggered.
- /// In this way, the ability of IOFetch to handle fragmented TCP packets
- /// should be checked.
- ///
- /// \param expected Number of bytes that were expected to have been sent.
- /// \param socket Socket over which the send took place. Only used to
- /// pass back to the send method.
- /// \param ec Boost error code, value should be zero.
- /// \param length Number of bytes sent.
- void tcpSendHandler(size_t expected, tcp::socket* socket,
- error_code ec = error_code(), size_t length = 0)
- {
- if (debug_) {
- cout << "tcpSendHandler(): error = " << ec.value() <<
- ", length = " << length << endl;
- }
-
- EXPECT_EQ(0, ec.value()); // Expect no error
- EXPECT_EQ(expected, length); // And that amount sent is as expected
-
- // Do we need to send more?
- send_cumulative_ += length;
- if (send_cumulative_ < send_buffer_.size()) {
-
- // Yes - set up a timer: the callback handler for the timer is
- // tcpSendData, which will then send the next chunk. We pass the
- // socket over which data should be sent as an argument to that
- // function.
- timer_.expires_from_now(boost::posix_time::milliseconds(SEND_INTERVAL));
- timer_.async_wait(boost::bind(&IOFetchTest::tcpSendData, this,
- socket));
- }
- }
-
- /// \brief Fetch completion callback
- ///
- /// This is the callback's operator() method which is called when the fetch
- /// is complete. It checks that the data received is the wire format of the
- /// data sent back by the server.
- ///
- /// \param result Result indicated by the callback
- void operator()(IOFetch::Result result) {
- if (debug_) {
- cout << "operator()(): result = " << result << endl;
- }
-
- EXPECT_EQ(expected_, result); // Check correct result returned
- EXPECT_FALSE(run_); // Check it is run only once
- run_ = true; // Note success
-
- // If the expected result for SUCCESS, then this should have been called
- // when one of the "servers" in this class has sent back return_data_.
- // Check the data is as expected/
- if (expected_ == IOFetch::SUCCESS) {
- EXPECT_EQ(return_data_.size(), result_buff_->getLength());
-
- const uint8_t* start = static_cast<const uint8_t*>(result_buff_->getData());
- EXPECT_TRUE(equal(return_data_.begin(), return_data_.end(), start));
- }
-
- // ... and cause the run loop to exit.
- service_.stop();
- }
-
- // The next set of methods are the tests themselves. A number of the TCP
- // and UDP tests are very similar.
-
- /// \brief Check for stop()
- ///
- /// Test that when we run the query and stop it after it was run, it returns
- /// "stopped" correctly. (That is why stop() is posted to the service_ as
- /// well instead of calling it.)
- ///
- /// \param protocol Test protocol
- /// \param fetch Fetch object being tested
- void stopTest(IOFetch::Protocol protocol, IOFetch& fetch) {
- protocol_ = protocol;
- expected_ = IOFetch::STOPPED;
-
- // Post the query
- service_.get_io_service().post(fetch);
-
- // Post query_.stop() (yes, the boost::bind thing is just
- // query_.stop()).
- service_.get_io_service().post(
- boost::bind(&IOFetch::stop, fetch, IOFetch::STOPPED));
-
- // Run both of them. run() returns when everything in the I/O service
- // queue has completed.
- service_.run();
- EXPECT_TRUE(run_);
- }
-
- /// \brief Premature stop test
- ///
- /// Test that when we queue the query to service_ and call stop() before it
- /// gets executed, it acts sanely as well (eg. has the same result as
- /// running stop() after - calls the callback).
- ///
- /// \param protocol Test protocol
- /// \param fetch Fetch object being tested
- void prematureStopTest(IOFetch::Protocol protocol, IOFetch& fetch) {
- protocol_ = protocol;
- expected_ = IOFetch::STOPPED;
-
- // Stop before it is started
- fetch.stop();
- service_.get_io_service().post(fetch);
-
- service_.run();
- EXPECT_TRUE(run_);
- }
-
- /// \brief Timeout test
- ///
- /// Test that fetch times out when no answer arrives.
- ///
- /// \param protocol Test protocol
- /// \param fetch Fetch object being tested
- void timeoutTest(IOFetch::Protocol protocol, IOFetch& fetch) {
- protocol_ = protocol;
- expected_ = IOFetch::TIME_OUT;
-
- service_.get_io_service().post(fetch);
- service_.run();
- EXPECT_TRUE(run_);
- }
-
- /// \brief Send/Receive Test
- ///
- /// Send a query to the server then receives a response.
- ///
- /// \param Test data to return to client
- void tcpSendReturnTest(const std::string& return_data) {
- if (debug_) {
- cout << "tcpSendReturnTest(): data size = " << return_data.size() << endl;
- }
- return_data_ = return_data;
- protocol_ = IOFetch::TCP;
- expected_ = IOFetch::SUCCESS;
-
- // Socket into which the connection will be accepted.
- tcp::socket socket(service_.get_io_service());
-
- // Acceptor object - called when the connection is made, the handler
- // will initiate a read on the socket.
- tcp::acceptor acceptor(service_.get_io_service(),
- tcp::endpoint(tcp::v4(), TEST_PORT));
- acceptor.async_accept(socket,
- boost::bind(&IOFetchTest::tcpAcceptHandler, this, &socket, _1));
-
- // Post the TCP fetch object to send the query and receive the response.
- service_.get_io_service().post(tcp_fetch_);
-
- // ... and execute all the callbacks. This exits when the fetch
- // completes.
- service_.run();
- EXPECT_TRUE(run_); // Make sure the callback did execute
-
- // Tidy up
- socket.close();
- }
-};
-
-// Check the protocol
-TEST_F(IOFetchTest, Protocol) {
- EXPECT_EQ(IOFetch::UDP, udp_fetch_.getProtocol());
- EXPECT_EQ(IOFetch::TCP, tcp_fetch_.getProtocol());
-}
-
-// UDP Stop test - see IOFetchTest::stopTest() header.
-TEST_F(IOFetchTest, UdpStop) {
- stopTest(IOFetch::UDP, udp_fetch_);
-}
-
-// UDP premature stop test - see IOFetchTest::prematureStopTest() header.
-TEST_F(IOFetchTest, UdpPrematureStop) {
- prematureStopTest(IOFetch::UDP, udp_fetch_);
-}
-
-// UDP premature stop test - see IOFetchTest::timeoutTest() header.
-TEST_F(IOFetchTest, UdpTimeout) {
- timeoutTest(IOFetch::UDP, udp_fetch_);
-}
-
-// UDP SendReceive test. Set up a UDP server then ports a UDP fetch object.
-// This will send question_ to the server and receive the answer back from it.
-TEST_F(IOFetchTest, UdpSendReceive) {
- protocol_ = IOFetch::UDP;
- expected_ = IOFetch::SUCCESS;
-
- // Set up the server.
- udp::socket socket(service_.get_io_service(), udp::v4());
- socket.set_option(socket_base::reuse_address(true));
- socket.bind(udp::endpoint(TEST_HOST, TEST_PORT));
- return_data_ = "Message returned to the client";
-
- udp::endpoint remote;
- socket.async_receive_from(asio::buffer(receive_buffer_, sizeof(receive_buffer_)),
- remote,
- boost::bind(&IOFetchTest::udpReceiveHandler, this, &remote, &socket,
- _1, _2));
- service_.get_io_service().post(udp_fetch_);
- if (debug_) {
- cout << "udpSendReceive: async_receive_from posted, waiting for callback" <<
- endl;
- }
- service_.run();
-
- socket.close();
-
- EXPECT_TRUE(run_);;
-}
-
-// Do the same tests for TCP transport
-
-TEST_F(IOFetchTest, TcpStop) {
- stopTest(IOFetch::TCP, tcp_fetch_);
-}
-
-TEST_F(IOFetchTest, TcpPrematureStop) {
- prematureStopTest(IOFetch::TCP, tcp_fetch_);
-}
-
-TEST_F(IOFetchTest, TcpTimeout) {
- timeoutTest(IOFetch::TCP, tcp_fetch_);
-}
-
-// Test with values at or near 0, then at or near the chunk size (16 and 32
-// bytes, the sizes of the first two packets) then up to 65535. These are done
-// in separate tests because in practice a new IOFetch is created for each
-// query/response exchange and we don't want to confuse matters in the test
-// by running the test with an IOFetch that has already done one exchange.
-
-TEST_F(IOFetchTest, TcpSendReceive0) {
- tcpSendReturnTest(test_data_.substr(0, 0));
-}
-
-TEST_F(IOFetchTest, TcpSendReceive1) {
- tcpSendReturnTest(test_data_.substr(0, 1));
-}
-
-TEST_F(IOFetchTest, TcpSendReceive15) {
- tcpSendReturnTest(test_data_.substr(0, 15));
-}
-
-TEST_F(IOFetchTest, TcpSendReceive16) {
- tcpSendReturnTest(test_data_.substr(0, 16));
-}
-
-TEST_F(IOFetchTest, TcpSendReceive17) {
- tcpSendReturnTest(test_data_.substr(0, 17));
-}
-
-TEST_F(IOFetchTest, TcpSendReceive31) {
- tcpSendReturnTest(test_data_.substr(0, 31));
-}
-
-TEST_F(IOFetchTest, TcpSendReceive32) {
- tcpSendReturnTest(test_data_.substr(0, 32));
-}
-
-TEST_F(IOFetchTest, TcpSendReceive33) {
- tcpSendReturnTest(test_data_.substr(0, 33));
-}
-
-TEST_F(IOFetchTest, TcpSendReceive4096) {
- tcpSendReturnTest(test_data_.substr(0, 4096));
-}
-
-TEST_F(IOFetchTest, TcpSendReceive8192) {
- tcpSendReturnTest(test_data_.substr(0, 8192));
-}
-
-TEST_F(IOFetchTest, TcpSendReceive16384) {
- tcpSendReturnTest(test_data_.substr(0, 16384));
-}
-
-TEST_F(IOFetchTest, TcpSendReceive32768) {
- tcpSendReturnTest(test_data_.substr(0, 32768));
-}
-
-TEST_F(IOFetchTest, TcpSendReceive65535) {
- tcpSendReturnTest(test_data_.substr(0, 65535));
-}
-
-} // namespace asiolink
diff --git a/src/lib/asiolink/tests/io_service_unittest.cc b/src/lib/asiolink/tests/io_service_unittest.cc
deleted file mode 100644
index 779d03e..0000000
--- a/src/lib/asiolink/tests/io_service_unittest.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <config.h>
-#include <gtest/gtest.h>
-
-#include <asio.hpp>
-#include <asiolink/asiolink.h>
-
-using namespace asiolink;
-
-const char* const TEST_SERVER_PORT = "53535";
-const char* const TEST_CLIENT_PORT = "53536";
-const char* const TEST_IPV6_ADDR = "::1";
-const char* const TEST_IPV4_ADDR = "127.0.0.1";
-
-TEST(IOServiceTest, badPort) {
- IOService io_service;
- EXPECT_THROW(DNSService(io_service, *"65536", true, false, NULL, NULL, NULL), IOError);
- EXPECT_THROW(DNSService(io_service, *"53210.0", true, false, NULL, NULL, NULL), IOError);
- EXPECT_THROW(DNSService(io_service, *"-1", true, false, NULL, NULL, NULL), IOError);
- EXPECT_THROW(DNSService(io_service, *"domain", true, false, NULL, NULL, NULL), IOError);
-}
-
-TEST(IOServiceTest, badAddress) {
- IOService io_service;
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.1.1", NULL, NULL, NULL), IOError);
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"2001:db8:::1", NULL, NULL, NULL), IOError);
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"localhost", NULL, NULL, NULL), IOError);
-}
-
-TEST(IOServiceTest, unavailableAddress) {
- IOService io_service;
- // These addresses should generally be unavailable as a valid local
- // address, although there's no guarantee in theory.
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.0", NULL, NULL, NULL), IOError);
-
- // Some OSes would simply reject binding attempt for an AF_INET6 socket
- // to an IPv4-mapped IPv6 address. Even if those that allow it, since
- // the corresponding IPv4 address is the same as the one used in the
- // AF_INET socket case above, it should at least show the same result
- // as the previous one.
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:192.0.2.0", NULL, NULL, NULL), IOError);
-}
-
-TEST(IOServiceTest, duplicateBind_v6) {
- // In each sub test case, second attempt should fail due to duplicate bind
- IOService io_service;
-
- // IPv6, "any" address
- DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL);
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL), IOError);
- delete dns_service;
-
-}
-
-TEST(IOServiceTest, duplicateBind_v6_address) {
- // In each sub test case, second attempt should fail due to duplicate bind
- IOService io_service;
-
- // IPv6, specific address
- DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL);
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL), IOError);
- delete dns_service;
-
-}
-
-TEST(IOServiceTest, duplicateBind_v4) {
- // In each sub test case, second attempt should fail due to duplicate bind
- IOService io_service;
-
- // IPv4, "any" address
- DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL);
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL), IOError);
- delete dns_service;
-
-}
-
-TEST(IOServiceTest, duplicateBind_v4_address) {
- // In each sub test case, second attempt should fail due to duplicate bind
- IOService io_service;
-
- // IPv4, specific address
- DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL);
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL), IOError);
- delete dns_service;
-}
-
-// Disabled because IPv4-mapped addresses don't seem to be working with
-// the IOService constructor
-TEST(IOServiceTest, DISABLED_IPv4MappedDuplicateBind) {
- IOService io_service;
- // Duplicate bind on IPv4-mapped IPv6 address
- DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL);
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL), IOError);
- delete dns_service;
-
- // XXX:
- // Currently, this throws an "invalid argument" exception. I have
- // not been able to get IPv4-mapped addresses to work.
- dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL);
- EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL), IOError);
- delete dns_service;
-}
-
diff --git a/src/lib/asiolink/tests/io_socket_unittest.cc b/src/lib/asiolink/tests/io_socket_unittest.cc
index 6538550..15afc17 100644
--- a/src/lib/asiolink/tests/io_socket_unittest.cc
+++ b/src/lib/asiolink/tests/io_socket_unittest.cc
@@ -20,7 +20,7 @@
#include <asio.hpp>
#include <asiolink/io_socket.h>
-using namespace asiolink;
+using namespace isc::asiolink;
TEST(IOSocketTest, dummySockets) {
EXPECT_EQ(IPPROTO_UDP, IOSocket::getDummyUDPSocket().getProtocol());
diff --git a/src/lib/asiolink/tests/qid_gen_unittest.cc b/src/lib/asiolink/tests/qid_gen_unittest.cc
deleted file mode 100644
index 3ad8a03..0000000
--- a/src/lib/asiolink/tests/qid_gen_unittest.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-
-/// \brief Test of QidGenerator
-///
-
-#include <gtest/gtest.h>
-
-#include <asiolink/qid_gen.h>
-#include <dns/message.h>
-
-// Tests the operation of the Qid generator
-
-// Check that getInstance returns a singleton
-TEST(QidGenerator, singleton) {
- asiolink::QidGenerator& g1 = asiolink::QidGenerator::getInstance();
- asiolink::QidGenerator& g2 = asiolink::QidGenerator::getInstance();
-
- EXPECT_TRUE(&g1 == &g2);
-}
-
-TEST(QidGenerator, generate) {
- // We'll assume that boost's generator is 'good enough', and won't
- // do full statistical checking here. Let's just call it the xkcd
- // test (http://xkcd.com/221/), and check if three consecutive
- // generates are not all the same.
- isc::dns::qid_t one, two, three;
- asiolink::QidGenerator& gen = asiolink::QidGenerator::getInstance();
- one = gen.generateQid();
- two = gen.generateQid();
- three = gen.generateQid();
- ASSERT_FALSE((one == two) && (one == three));
-}
diff --git a/src/lib/asiolink/tests/run_unittests.cc b/src/lib/asiolink/tests/run_unittests.cc
index c285f9e..b07ce7e 100644
--- a/src/lib/asiolink/tests/run_unittests.cc
+++ b/src/lib/asiolink/tests/run_unittests.cc
@@ -13,16 +13,13 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
-
-#include <log/root_logger_name.h>
-#include <dns/tests/unittest_util.h>
+#include <util/unittests/run_all.h>
+#include <log/logger_manager.h>
int
main(int argc, char* argv[])
{
::testing::InitGoogleTest(&argc, argv); // Initialize Google test
- isc::log::setRootLoggerName("unittest"); // Set a root logger name
- isc::UnitTestUtil::addDataPath(TEST_DATA_DIR); // Add location of test data
-
- return (RUN_ALL_TESTS());
+ isc::log::LoggerManager::init("unittest"); // Set a root logger name
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/asiolink/tests/tcp_endpoint_unittest.cc b/src/lib/asiolink/tests/tcp_endpoint_unittest.cc
index 3787e1c..6988082 100644
--- a/src/lib/asiolink/tests/tcp_endpoint_unittest.cc
+++ b/src/lib/asiolink/tests/tcp_endpoint_unittest.cc
@@ -22,7 +22,7 @@
#include <asiolink/io_address.h>
#include <asiolink/tcp_endpoint.h>
-using namespace asiolink;
+using namespace isc::asiolink;
using namespace std;
// This test checks that the endpoint can manage its own internal
diff --git a/src/lib/asiolink/tests/tcp_socket_unittest.cc b/src/lib/asiolink/tests/tcp_socket_unittest.cc
index f0a45ee..538cf48 100644
--- a/src/lib/asiolink/tests/tcp_socket_unittest.cc
+++ b/src/lib/asiolink/tests/tcp_socket_unittest.cc
@@ -35,19 +35,19 @@
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
-#include <dns/buffer.h>
+#include <util/buffer.h>
+#include <util/io_utilities.h>
#include <asio.hpp>
-#include <asiolink/asiolink_utilities.h>
#include <asiolink/io_service.h>
#include <asiolink/tcp_endpoint.h>
#include <asiolink/tcp_socket.h>
using namespace asio;
using namespace asio::ip;
-using namespace asiolink;
-using namespace isc::dns;
+using namespace isc::util;
+using namespace isc::asiolink;
using namespace std;
namespace {
diff --git a/src/lib/asiolink/tests/udp_endpoint_unittest.cc b/src/lib/asiolink/tests/udp_endpoint_unittest.cc
index 18135ec..03de6b8 100644
--- a/src/lib/asiolink/tests/udp_endpoint_unittest.cc
+++ b/src/lib/asiolink/tests/udp_endpoint_unittest.cc
@@ -22,7 +22,7 @@
#include <asiolink/io_address.h>
#include <asiolink/udp_endpoint.h>
-using namespace asiolink;
+using namespace isc::asiolink;
using namespace std;
// This test checks that the endpoint can manage its own internal
diff --git a/src/lib/asiolink/tests/udp_socket_unittest.cc b/src/lib/asiolink/tests/udp_socket_unittest.cc
index 8563d22..1ab1a09 100644
--- a/src/lib/asiolink/tests/udp_socket_unittest.cc
+++ b/src/lib/asiolink/tests/udp_socket_unittest.cc
@@ -35,18 +35,18 @@
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
-#include <dns/buffer.h>
+#include <util/buffer.h>
+#include <util/io_utilities.h>
#include <asio.hpp>
-#include <asiolink/asiolink_utilities.h>
#include <asiolink/io_service.h>
#include <asiolink/udp_endpoint.h>
#include <asiolink/udp_socket.h>
using namespace asio;
-using namespace asiolink;
-using namespace isc::dns;
+using namespace isc::util;
+using namespace isc::asiolink;
using namespace std;
namespace {
diff --git a/src/lib/asiolink/udp_endpoint.h b/src/lib/asiolink/udp_endpoint.h
index 99dc27f..c5ba3bd 100644
--- a/src/lib/asiolink/udp_endpoint.h
+++ b/src/lib/asiolink/udp_endpoint.h
@@ -21,6 +21,7 @@
#include <asiolink/io_endpoint.h>
+namespace isc {
namespace asiolink {
/// \brief The \c UDPEndpoint class is a concrete derived class of
@@ -83,6 +84,10 @@ public:
return (asio_endpoint_.address());
}
+ virtual const struct sockaddr& getSockAddr() const {
+ return (*asio_endpoint_.data());
+ }
+
virtual uint16_t getPort() const {
return (asio_endpoint_.port());
}
@@ -109,5 +114,10 @@ private:
asio::ip::udp::endpoint& asio_endpoint_;
};
-} // namespace asiolink
+} // namespace asiolink
+} // namespace isc
#endif // __UDP_ENDPOINT_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/asiolink/udp_server.cc b/src/lib/asiolink/udp_server.cc
deleted file mode 100644
index 063926e..0000000
--- a/src/lib/asiolink/udp_server.cc
+++ /dev/null
@@ -1,322 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <unistd.h> // for some IPC/network system calls
-#include <errno.h>
-
-#include <boost/shared_array.hpp>
-
-#include <config.h>
-
-#include <log/dummylog.h>
-
-#include <asio.hpp>
-#include <asiolink/dummy_io_cb.h>
-#include <asiolink/udp_endpoint.h>
-#include <asiolink/udp_server.h>
-#include <asiolink/udp_socket.h>
-
-#include <dns/opcode.h>
-
-using namespace asio;
-using asio::ip::udp;
-using isc::log::dlog;
-
-using namespace std;
-using namespace isc::dns;
-
-namespace asiolink {
-
-/*
- * Some of the member variables here are shared_ptrs and some are
- * auto_ptrs. There will be one instance of Data for the lifetime
- * of packet. The variables that are state only for a single packet
- * use auto_ptr, as it is more lightweight. In the case of shared
- * configuration (eg. the callbacks, socket), we use shared_ptrs.
- */
-struct UDPServer::Data {
- /*
- * Constructor from parameters passed to UDPServer constructor.
- * This instance will not be used to retrieve and answer the actual
- * query, it will only hold parameters until we wait for the
- * first packet. But we do initialize the socket in here.
- */
- Data(io_service& io_service, const ip::address& addr, const uint16_t port,
- SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) :
- io_(io_service), done_(false), stopped_by_hand_(false),
- checkin_callback_(checkin),lookup_callback_(lookup),
- answer_callback_(answer)
- {
- // We must use different instantiations for v4 and v6;
- // otherwise ASIO will bind to both
- udp proto = addr.is_v4() ? udp::v4() : udp::v6();
- socket_.reset(new udp::socket(io_service, proto));
- socket_->set_option(socket_base::reuse_address(true));
- if (addr.is_v6()) {
- socket_->set_option(asio::ip::v6_only(true));
- }
- socket_->bind(udp::endpoint(addr, port));
- }
-
- /*
- * Copy constructor. Default one would probably do, but it is unnecessary
- * to copy many of the member variables every time we fork to handle
- * another packet.
- *
- * We also allocate data for receiving the packet here.
- */
- Data(const Data& other) :
- io_(other.io_), socket_(other.socket_), done_(false),
- stopped_by_hand_(false),
- checkin_callback_(other.checkin_callback_),
- lookup_callback_(other.lookup_callback_),
- answer_callback_(other.answer_callback_)
- {
- // Instantiate the data buffer and endpoint that will
- // be used by the asynchronous receive call.
- data_.reset(new char[MAX_LENGTH]);
- sender_.reset(new udp::endpoint());
- }
-
- // The ASIO service object
- asio::io_service& io_;
-
- // Class member variables which are dynamic, and changes to which
- // need to accessible from both sides of a coroutine fork or from
- // outside of the coroutine (i.e., from an asynchronous I/O call),
- // should be declared here as pointers and allocated in the
- // constructor or in the coroutine. This allows state information
- // to persist when an individual copy of the coroutine falls out
- // scope while waiting for an event, *so long as* there is another
- // object that is referencing the same data. As a side-benefit, using
- // pointers also reduces copy overhead for coroutine objects.
- //
- // Note: Currently these objects are allocated by "new" in the
- // constructor, or in the function operator while processing a query.
- // Repeated allocations from the heap for every incoming query is
- // clearly a performance issue; this must be optimized in the future.
- // The plan is to have a structure pre-allocate several "Data"
- // objects which can be pulled off a free list and placed on an in-use
- // list whenever a query comes in. This will serve the dual purpose
- // of improving performance and guaranteeing that state information
- // will *not* be destroyed when any one instance of the coroutine
- // falls out of scope while waiting for an event.
- //
- // Socket used to for listen for queries. Created in the
- // constructor and stored in a shared_ptr because socket objects
- // are not copyable.
- boost::shared_ptr<asio::ip::udp::socket> socket_;
-
- // The ASIO-internal endpoint object representing the client
- std::auto_ptr<asio::ip::udp::endpoint> sender_;
-
- // \c IOMessage and \c Message objects to be passed to the
- // DNS lookup and answer providers
- std::auto_ptr<asiolink::IOMessage> io_message_;
-
- // The original query as sent by the client
- isc::dns::MessagePtr query_message_;
-
- // The response message we are building
- isc::dns::MessagePtr answer_message_;
-
- // The buffer into which the response is written
- isc::dns::OutputBufferPtr respbuf_;
-
- // The buffer into which the query packet is written
- boost::shared_array<char> data_;
-
- // State information that is entirely internal to a given instance
- // of the coroutine can be declared here.
- size_t bytes_;
- bool done_;
-
- //whether user explicitly stop the server
- bool stopped_by_hand_;
-
- // Callback functions provided by the caller
- const SimpleCallback* checkin_callback_;
- const DNSLookup* lookup_callback_;
- const DNSAnswer* answer_callback_;
-
- std::auto_ptr<IOEndpoint> peer_;
- std::auto_ptr<IOSocket> iosock_;
-};
-
-/// The following functions implement the \c UDPServer class.
-///
-/// The constructor. It just creates new internal state object
-/// and lets it handle the initialization.
-UDPServer::UDPServer(io_service& io_service, const ip::address& addr,
- const uint16_t port, SimpleCallback* checkin, DNSLookup* lookup,
- DNSAnswer* answer) :
- data_(new Data(io_service, addr, port, checkin, lookup, answer))
-{ }
-
-/// The function operator is implemented with the "stackless coroutine"
-/// pattern; see internal/coroutine.h for details.
-void
-UDPServer::operator()(error_code ec, size_t length) {
- /// Because the coroutine reentry block is implemented as
- /// a switch statement, inline variable declarations are not
- /// permitted. Certain variables used below can be declared here.
-
- /// if user stopped the server, we won't enter the coroutine body
- /// just return
- if (data_->stopped_by_hand_) {
- return;
- }
-
- CORO_REENTER (this) {
- do {
- /*
- * This is preparation for receiving a packet. We get a new
- * state object for the lifetime of the next packet to come.
- * It allocates the buffers to receive data into.
- */
- data_.reset(new Data(*data_));
-
- do {
- // Begin an asynchronous receive, then yield.
- // When the receive event is posted, the coroutine
- // will resume immediately after this point.
- CORO_YIELD data_->socket_->async_receive_from(
- buffer(data_->data_.get(), MAX_LENGTH), *data_->sender_,
- *this);
- // Abort on fatal errors
- if (ec) {
- using namespace asio::error;
- if (ec.value() != would_block && ec.value() != try_again &&
- ec.value() != interrupted) {
- return;
- }
- }
- } while (ec || length == 0);
-
- data_->bytes_ = length;
-
- /*
- * We fork the coroutine now. One (the child) will keep
- * the current state and handle the packet, then die and
- * drop ownership of the state. The other (parent) will just
- * go into the loop again and replace the current state with
- * a new one for a new object.
- *
- * Actually, both of the coroutines will be a copy of this
- * one, but that's just internal implementation detail.
- */
- CORO_FORK data_->io_.post(UDPServer(*this));
- } while (is_parent());
-
- // Create an \c IOMessage object to store the query.
- //
- // (XXX: It would be good to write a factory function
- // that would quickly generate an IOMessage object without
- // all these calls to "new".)
- data_->peer_.reset(new UDPEndpoint(*data_->sender_));
-
- // The UDP socket class has been extended with asynchronous functions
- // and takes as a template parameter a completion callback class. As
- // UDPServer does not use these extended functions (only those defined
- // in the IOSocket base class) - but needs a UDPSocket to get hold of
- // the underlying Boost UDP socket - DummyIOCallback is used. This
- // provides the appropriate operator() but is otherwise functionless.
- data_->iosock_.reset(
- new UDPSocket<DummyIOCallback>(*data_->socket_));
-
- data_->io_message_.reset(new IOMessage(data_->data_.get(),
- data_->bytes_, *data_->iosock_, *data_->peer_));
-
- // Perform any necessary operations prior to processing an incoming
- // query (e.g., checking for queued configuration messages).
- //
- // (XXX: it may be a performance issue to check in for every single
- // incoming query; we may wish to throttle this in the future.)
- if (data_->checkin_callback_ != NULL) {
- (*data_->checkin_callback_)(*data_->io_message_);
- }
-
- // If we don't have a DNS Lookup provider, there's no point in
- // continuing; we exit the coroutine permanently.
- if (data_->lookup_callback_ == NULL) {
- CORO_YIELD return;
- }
-
- // Instantiate objects that will be needed by the
- // asynchronous DNS lookup and/or by the send call.
- data_->respbuf_.reset(new OutputBuffer(0));
- data_->query_message_.reset(new Message(Message::PARSE));
- data_->answer_message_.reset(new Message(Message::RENDER));
-
- // Schedule a DNS lookup, and yield. When the lookup is
- // finished, the coroutine will resume immediately after
- // this point.
- CORO_YIELD data_->io_.post(AsyncLookup<UDPServer>(*this));
-
- // The 'done_' flag indicates whether we have an answer
- // to send back. If not, exit the coroutine permanently.
- if (!data_->done_) {
- CORO_YIELD return;
- }
-
- // Call the DNS answer provider to render the answer into
- // wire format
- (*data_->answer_callback_)(*data_->io_message_, data_->query_message_,
- data_->answer_message_, data_->respbuf_);
-
- // Begin an asynchronous send, and then yield. When the
- // send completes, we will resume immediately after this point
- // (though we have nothing further to do, so the coroutine
- // will simply exit at that time).
- CORO_YIELD data_->socket_->async_send_to(
- buffer(data_->respbuf_->getData(), data_->respbuf_->getLength()),
- *data_->sender_, *this);
- }
-}
-
-/// Call the DNS lookup provider. (Expected to be called by the
-/// AsyncLookup<UDPServer> handler.)
-void
-UDPServer::asyncLookup() {
- (*data_->lookup_callback_)(*data_->io_message_,
- data_->query_message_, data_->answer_message_, data_->respbuf_, this);
-}
-
-/// Stop the UDPServer
-void
-UDPServer::stop() {
- //server should not be stopped twice
- if (data_->stopped_by_hand_)
- return;
- data_->stopped_by_hand_ = true;
- data_->socket_->close();
-}
-
-/// Post this coroutine on the ASIO service queue so that it will
-/// resume processing where it left off. The 'done' parameter indicates
-/// whether there is an answer to return to the client.
-void
-UDPServer::resume(const bool done) {
- data_->done_ = done;
- data_->io_.post(*this);
-}
-
-bool
-UDPServer::hasAnswer() {
- return (data_->done_);
-}
-
-} // namespace asiolink
diff --git a/src/lib/asiolink/udp_server.h b/src/lib/asiolink/udp_server.h
deleted file mode 100644
index 1d37471..0000000
--- a/src/lib/asiolink/udp_server.h
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __UDP_SERVER_H
-#define __UDP_SERVER_H 1
-
-#ifndef ASIO_HPP
-#error "asio.hpp must be included before including this, see asiolink.h as to why"
-#endif
-
-#include <asiolink/dns_server.h>
-#include <asiolink/simple_callback.h>
-#include <asiolink/dns_lookup.h>
-#include <asiolink/dns_answer.h>
-
-#include <coroutine.h>
-
-namespace asiolink {
-
-//
-// Asynchronous UDP server coroutine
-//
-///
-/// \brief This class implements the coroutine to handle UDP
-/// DNS query event. As such, it is both a \c DNSServer and
-/// a \c coroutine
-///
-class UDPServer : public virtual DNSServer, public virtual coroutine {
-public:
- /// \brief Constructor
- /// \param io_service the asio::io_service to work with
- /// \param addr the IP address to listen for queries on
- /// \param port the port to listen for queries on
- /// \param checkin the callbackprovider for non-DNS events
- /// \param lookup the callbackprovider for DNS lookup events
- /// \param answer the callbackprovider for DNS answer events
- explicit UDPServer(asio::io_service& io_service,
- const asio::ip::address& addr, const uint16_t port,
- SimpleCallback* checkin = NULL,
- DNSLookup* lookup = NULL,
- DNSAnswer* answer = NULL);
-
- /// \brief The function operator
- void operator()(asio::error_code ec = asio::error_code(),
- size_t length = 0);
-
- /// \brief Calls the lookup callback
- void asyncLookup();
-
- /// \brief Stop the running server
- /// \note once the server stopped, it can't restart
- void stop();
-
- /// \brief Resume operation
- ///
- /// \param done Set this to true if the lookup action is done and
- /// we have an answer
- void resume(const bool done);
-
- /// \brief Check if we have an answer
- ///
- /// \return true if we have an answer
- bool hasAnswer();
-
- /// \brief Returns the coroutine state value
- ///
- /// \return the coroutine state value
- int value() { return (get_value()); }
-
- /// \brief Clones the object
- ///
- /// \return a newly allocated copy of this object
- DNSServer* clone() {
- UDPServer* s = new UDPServer(*this);
- return (s);
- }
-
-private:
- enum { MAX_LENGTH = 4096 };
-
- /**
- * \brief Internal state and data.
- *
- * We use the pimple design pattern, but not because we need to hide
- * internal data. This class and whole header is for private use anyway.
- * It turned out that UDPServer is copied a lot, because it is a coroutine.
- * This way the overhead of copying is lower, we copy only one shared
- * pointer instead of about 10 of them.
- */
- class Data;
- boost::shared_ptr<Data> data_;
-};
-
-} // namespace asiolink
-#endif // __UDP_SERVER_H
diff --git a/src/lib/asiolink/udp_socket.h b/src/lib/asiolink/udp_socket.h
index 35fc7b1..c061fba 100644
--- a/src/lib/asiolink/udp_socket.h
+++ b/src/lib/asiolink/udp_socket.h
@@ -33,6 +33,7 @@
#include <asiolink/io_service.h>
#include <asiolink/udp_endpoint.h>
+namespace isc {
namespace asiolink {
/// \brief The \c UDPSocket class is a concrete derived class of \c IOAsioSocket
@@ -141,7 +142,7 @@ public:
virtual bool processReceivedData(const void* staging, size_t length,
size_t& cumulative, size_t& offset,
size_t& expected,
- isc::dns::OutputBufferPtr& outbuff);
+ isc::util::OutputBufferPtr& outbuff);
/// \brief Cancel I/O On Socket
virtual void cancel();
@@ -283,7 +284,7 @@ template <typename C> bool
UDPSocket<C>::processReceivedData(const void* staging, size_t length,
size_t& cumulative, size_t& offset,
size_t& expected,
- isc::dns::OutputBufferPtr& outbuff)
+ isc::util::OutputBufferPtr& outbuff)
{
// Set return values to what we should expect.
cumulative = length;
@@ -318,5 +319,6 @@ UDPSocket<C>::close() {
}
} // namespace asiolink
+} // namespace isc
#endif // __UDP_SOCKET_H
diff --git a/src/lib/bench/benchmark_util.cc b/src/lib/bench/benchmark_util.cc
index ca2ca1b..c67a851 100644
--- a/src/lib/bench/benchmark_util.cc
+++ b/src/lib/bench/benchmark_util.cc
@@ -19,7 +19,7 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/name.h>
#include <dns/message.h>
@@ -35,6 +35,7 @@
using namespace std;
using namespace isc;
using namespace isc::dns;
+using namespace isc::util;
namespace isc {
namespace bench {
diff --git a/src/lib/bench/tests/Makefile.am b/src/lib/bench/tests/Makefile.am
index 63d3c87..3ebdf29 100644
--- a/src/lib/bench/tests/Makefile.am
+++ b/src/lib/bench/tests/Makefile.am
@@ -14,9 +14,10 @@ run_unittests_SOURCES += loadquery_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD = $(top_builddir)/src/lib/bench/libbench.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/bench/libbench.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(GTEST_LDADD)
endif
diff --git a/src/lib/bench/tests/loadquery_unittest.cc b/src/lib/bench/tests/loadquery_unittest.cc
index a53e191..93130d2 100644
--- a/src/lib/bench/tests/loadquery_unittest.cc
+++ b/src/lib/bench/tests/loadquery_unittest.cc
@@ -17,7 +17,7 @@
#include <vector>
#include <sstream>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/message.h>
#include <dns/name.h>
#include <dns/opcode.h>
@@ -32,6 +32,7 @@
using namespace std;
using namespace isc::bench;
using namespace isc::dns;
+using namespace isc::util;
namespace {
typedef pair<string, string> QueryParam;
diff --git a/src/lib/bench/tests/run_unittests.cc b/src/lib/bench/tests/run_unittests.cc
index 85d4548..450f5dc 100644
--- a/src/lib/bench/tests/run_unittests.cc
+++ b/src/lib/bench/tests/run_unittests.cc
@@ -13,10 +13,11 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/cache/Makefile.am b/src/lib/cache/Makefile.am
index 107fc9a..9871a5e 100644
--- a/src/lib/cache/Makefile.am
+++ b/src/lib/cache/Makefile.am
@@ -2,6 +2,7 @@ SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/nsas -I$(top_builddir)/src/lib/nsas
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/cache -I$(top_builddir)/src/lib/cache
@@ -30,5 +31,14 @@ libcache_la_SOURCES += cache_entry_key.h cache_entry_key.cc
libcache_la_SOURCES += rrset_copy.h rrset_copy.cc
libcache_la_SOURCES += local_zone_data.h local_zone_data.cc
libcache_la_SOURCES += message_utility.h message_utility.cc
+libcache_la_SOURCES += logger.h logger.cc
+nodist_libcache_la_SOURCES = cache_messages.cc cache_messages.h
-CLEANFILES = *.gcno *.gcda
+BUILT_SOURCES = cache_messages.cc cache_messages.h
+
+cache_messages.cc cache_messages.h: cache_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/cache/cache_messages.mes
+
+CLEANFILES = *.gcno *.gcda cache_messages.cc cache_messages.h
+
+EXTRA_DIST = cache_messages.mes
diff --git a/src/lib/cache/TODO b/src/lib/cache/TODO
index aa7e3b0..31825e4 100644
--- a/src/lib/cache/TODO
+++ b/src/lib/cache/TODO
@@ -12,7 +12,8 @@
* When the rrset beging updated is an NS rrset, NSAS should be updated
together.
* Share the NXDOMAIN info between different type queries. current implementation
- can only cache for the type that user quired, for example, if user query A
+ can only cache for the type that user queried, for example, if user query A
record of a.example. and the server replied with NXDOMAIN, this should be
cached for all the types queries of a.example.
-
+* Add the interfaces for resizing and serialization (loading and dumping) to
+ cache.
diff --git a/src/lib/cache/cache_messages.mes b/src/lib/cache/cache_messages.mes
new file mode 100644
index 0000000..2a68cc2
--- /dev/null
+++ b/src/lib/cache/cache_messages.mes
@@ -0,0 +1,148 @@
+# Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+$NAMESPACE isc::cache
+
+% CACHE_ENTRY_MISSING_RRSET missing RRset to generate message for %1
+The cache tried to generate the complete answer message. It knows the structure
+of the message, but some of the RRsets to be put there are not in cache (they
+probably expired already). Therefore it pretends the message was not found.
+
+% CACHE_LOCALZONE_FOUND found entry with key %1 in local zone data
+Debug message, noting that the requested data was successfully found in the
+local zone data of the cache.
+
+% CACHE_LOCALZONE_UNKNOWN entry with key %1 not found in local zone data
+Debug message. The requested data was not found in the local zone data.
+
+% CACHE_LOCALZONE_UPDATE updating local zone element at key %1
+Debug message issued when there's update to the local zone section of cache.
+
+% CACHE_MESSAGES_DEINIT deinitialized message cache
+Debug message. It is issued when the server deinitializes the message cache.
+
+% CACHE_MESSAGES_EXPIRED found an expired message entry for %1 in the message cache
+Debug message. The requested data was found in the message cache, but it
+already expired. Therefore the cache removes the entry and pretends it found
+nothing.
+
+% CACHE_MESSAGES_FOUND found a message entry for %1 in the message cache
+Debug message. We found the whole message in the cache, so it can be returned
+to user without any other lookups.
+
+% CACHE_MESSAGES_INIT initialized message cache for %1 messages of class %2
+Debug message issued when a new message cache is issued. It lists the class
+of messages it can hold and the maximum size of the cache.
+
+% CACHE_MESSAGES_REMOVE removing old instance of %1/%2/%3 first
+Debug message. This may follow CACHE_MESSAGES_UPDATE and indicates that, while
+updating, the old instance is being removed prior of inserting a new one.
+
+% CACHE_MESSAGES_UNCACHEABLE not inserting uncacheable message %1/%2/%3
+Debug message, noting that the given message can not be cached. This is because
+there's no SOA record in the message. See RFC 2308 section 5 for more
+information.
+
+% CACHE_MESSAGES_UNKNOWN no entry for %1 found in the message cache
+Debug message. The message cache didn't find any entry for the given key.
+
+% CACHE_MESSAGES_UPDATE updating message entry %1/%2/%3
+Debug message issued when the message cache is being updated with a new
+message. Either the old instance is removed or, if none is found, new one
+is created.
+
+% CACHE_RESOLVER_DEEPEST looking up deepest NS for %1/%2
+Debug message. The resolver cache is looking up the deepest known nameserver,
+so the resolution doesn't have to start from the root.
+
+% CACHE_RESOLVER_INIT_INFO initializing resolver cache for class %1
+Debug message, the resolver cache is being created for this given class. The
+difference from CACHE_RESOLVER_INIT is only in different format of passed
+information, otherwise it does the same.
+
+% CACHE_RESOLVER_INIT initializing resolver cache for class %1
+Debug message. The resolver cache is being created for this given class.
+
+% CACHE_RESOLVER_LOCAL_MSG message for %1/%2 found in local zone data
+Debug message. The resolver cache found a complete message for the user query
+in the zone data.
+
+% CACHE_RESOLVER_LOCAL_RRSET RRset for %1/%2 found in local zone data
+Debug message. The resolver cache found a requested RRset in the local zone
+data.
+
+% CACHE_RESOLVER_LOOKUP_MSG looking up message in resolver cache for %1/%2
+Debug message. The resolver cache is trying to find a message to answer the
+user query.
+
+% CACHE_RESOLVER_LOOKUP_RRSET looking up RRset in resolver cache for %1/%2
+Debug message. The resolver cache is trying to find an RRset (which usually
+originates as internally from resolver).
+
+% CACHE_RESOLVER_NO_QUESTION answer message for %1/%2 has empty question section
+The cache tried to fill in found data into the response message. But it
+discovered the message contains no question section, which is invalid.
+This is likely a programmer error, please submit a bug report.
+
+% CACHE_RESOLVER_UNKNOWN_CLASS_MSG no cache for class %1
+Debug message. While trying to lookup a message in the resolver cache, it was
+discovered there's no cache for this class at all. Therefore no message is
+found.
+
+% CACHE_RESOLVER_UNKNOWN_CLASS_RRSET no cache for class %1
+Debug message. While trying to lookup an RRset in the resolver cache, it was
+discovered there's no cache for this class at all. Therefore no data is found.
+
+% CACHE_RESOLVER_UPDATE_MSG updating message for %1/%2/%3
+Debug message. The resolver is updating a message in the cache.
+
+% CACHE_RESOLVER_UPDATE_RRSET updating RRset for %1/%2/%3
+Debug message. The resolver is updating an RRset in the cache.
+
+% CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_MSG no cache for class %1
+Debug message. While trying to insert a message into the cache, it was
+discovered that there's no cache for the class of message. Therefore
+the message will not be cached.
+
+% CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_RRSET no cache for class %1
+Debug message. While trying to insert an RRset into the cache, it was
+discovered that there's no cache for the class of the RRset. Therefore
+the message will not be cached.
+
+% CACHE_RRSET_EXPIRED found expired RRset %1/%2/%3
+Debug message. The requested data was found in the RRset cache. However, it is
+expired, so the cache removed it and is going to pretend nothing was found.
+
+% CACHE_RRSET_INIT initializing RRset cache for %2 RRsets of class %1
+Debug message. The RRset cache to hold at most this many RRsets for the given
+class is being created.
+
+% CACHE_RRSET_LOOKUP looking up %1/%2/%3 in RRset cache
+Debug message. The resolver is trying to look up data in the RRset cache.
+
+% CACHE_RRSET_NOT_FOUND no RRset found for %1/%2/%3
+Debug message which can follow CACHE_RRSET_LOOKUP. This means the data is not
+in the cache.
+
+% CACHE_RRSET_REMOVE_OLD removing old RRset for %1/%2/%3 to make space for new one
+Debug message which can follow CACHE_RRSET_UPDATE. During the update, the cache
+removed an old instance of the RRset to replace it with the new one.
+
+% CACHE_RRSET_UNTRUSTED not replacing old RRset for %1/%2/%3, it has higher trust level
+Debug message which can follow CACHE_RRSET_UPDATE. The cache already holds the
+same RRset, but from more trusted source, so the old one is kept and new one
+ignored.
+
+% CACHE_RRSET_UPDATE updating RRset %1/%2/%3 in the cache
+Debug message. The RRset is updating its data with this given RRset.
diff --git a/src/lib/cache/local_zone_data.cc b/src/lib/cache/local_zone_data.cc
index 61ce35a..13d1d75 100644
--- a/src/lib/cache/local_zone_data.cc
+++ b/src/lib/cache/local_zone_data.cc
@@ -16,6 +16,7 @@
#include "local_zone_data.h"
#include "cache_entry_key.h"
#include "rrset_copy.h"
+#include "logger.h"
using namespace std;
using namespace isc::dns;
@@ -33,8 +34,10 @@ LocalZoneData::lookup(const isc::dns::Name& name,
string key = genCacheEntryName(name, type);
RRsetMapIterator iter = rrsets_map_.find(key);
if (iter == rrsets_map_.end()) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_LOCALZONE_UNKNOWN).arg(key);
return (RRsetPtr());
} else {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_LOCALZONE_FOUND).arg(key);
return (iter->second);
}
}
@@ -43,6 +46,7 @@ void
LocalZoneData::update(const isc::dns::RRset& rrset) {
//TODO Do we really need to recreate the rrset again?
string key = genCacheEntryName(rrset.getName(), rrset.getType());
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_LOCALZONE_UPDATE).arg(key);
RRset* rrset_copy = new RRset(rrset.getName(), rrset.getClass(),
rrset.getType(), rrset.getTTL());
diff --git a/src/lib/cache/logger.cc b/src/lib/cache/logger.cc
new file mode 100644
index 0000000..f4b0f25
--- /dev/null
+++ b/src/lib/cache/logger.cc
@@ -0,0 +1,23 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cache/logger.h>
+
+namespace isc {
+namespace cache {
+
+isc::log::Logger logger("cache");
+
+}
+}
diff --git a/src/lib/cache/logger.h b/src/lib/cache/logger.h
new file mode 100644
index 0000000..8159ed4
--- /dev/null
+++ b/src/lib/cache/logger.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __DATASRC_LOGGER_H
+#define __DATASRC_LOGGER_H
+
+#include <log/macros.h>
+#include <cache/cache_messages.h>
+
+/// \file logger.h
+/// \brief Cache library global logger
+///
+/// This holds the logger for the cache library. It is a private header
+/// and should not be included in any publicly used header, only in local
+/// cc files.
+
+namespace isc {
+namespace cache {
+
+/// \brief The logger for this library
+extern isc::log::Logger logger;
+
+enum {
+ /// \brief Trace basic operations
+ DBG_TRACE_BASIC = 10,
+ /// \brief Trace data operations
+ DBG_TRACE_DATA = 40,
+};
+
+}
+}
+
+#endif
diff --git a/src/lib/cache/message_cache.cc b/src/lib/cache/message_cache.cc
index 50922fd..e141bb5 100644
--- a/src/lib/cache/message_cache.cc
+++ b/src/lib/cache/message_cache.cc
@@ -1,6 +1,7 @@
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
+//
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
@@ -20,6 +21,7 @@
#include "message_cache.h"
#include "message_utility.h"
#include "cache_entry_key.h"
+#include "logger.h"
namespace isc {
namespace cache {
@@ -39,6 +41,14 @@ MessageCache::MessageCache(const RRsetCachePtr& rrset_cache,
message_lru_((3 * cache_size),
new HashDeleter<MessageEntry>(message_table_))
{
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, CACHE_MESSAGES_INIT).arg(cache_size).
+ arg(RRClass(message_class));
+}
+
+MessageCache::~MessageCache() {
+ // Destroy all the message entries in the cache.
+ message_lru_.clear();
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, CACHE_MESSAGES_DEINIT);
}
bool
@@ -52,26 +62,38 @@ MessageCache::lookup(const isc::dns::Name& qname,
if(msg_entry) {
// Check whether the message entry has expired.
if (msg_entry->getExpireTime() > time(NULL)) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_MESSAGES_FOUND).
+ arg(entry_name);
message_lru_.touch(msg_entry);
return (msg_entry->genMessage(time(NULL), response));
} else {
// message entry expires, remove it from hash table and lru list.
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_MESSAGES_EXPIRED).
+ arg(entry_name);
message_table_.remove(entry_key);
message_lru_.remove(msg_entry);
return (false);
}
}
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_MESSAGES_UNKNOWN).arg(entry_name);
return (false);
}
bool
MessageCache::update(const Message& msg) {
if (!canMessageBeCached(msg)){
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_MESSAGES_UNCACHEABLE).
+ arg((*msg.beginQuestion())->getName()).
+ arg((*msg.beginQuestion())->getType()).
+ arg((*msg.beginQuestion())->getClass());
return (false);
}
QuestionIterator iter = msg.beginQuestion();
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_MESSAGES_UPDATE).
+ arg((*iter)->getName()).arg((*iter)->getType()).
+ arg((*iter)->getClass());
std::string entry_name = genCacheEntryName((*iter)->getName(),
(*iter)->getType());
HashKey entry_key = HashKey(entry_name, RRClass(message_class_));
@@ -83,6 +105,9 @@ MessageCache::update(const Message& msg) {
// add the message entry, maybe there is one way to touch it once.
MessageEntryPtr old_msg_entry = message_table_.get(entry_key);
if (old_msg_entry) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_MESSAGES_REMOVE).
+ arg((*iter)->getName()).arg((*iter)->getType()).
+ arg((*iter)->getClass());
message_lru_.remove(old_msg_entry);
}
@@ -92,24 +117,6 @@ MessageCache::update(const Message& msg) {
return (message_table_.add(msg_entry, entry_key, true));
}
-#if 0
-void
-MessageCache::dump(const std::string&) {
- //TODO
-}
-
-void
-MessageCache::load(const std::string&) {
- //TODO
-}
-
-bool
-MessageCache::resize(uint32_t) {
- //TODO
- return (true);
-}
-#endif
-
} // namespace cache
} // namespace isc
diff --git a/src/lib/cache/message_cache.h b/src/lib/cache/message_cache.h
index 63db681..44d7fd1 100644
--- a/src/lib/cache/message_cache.h
+++ b/src/lib/cache/message_cache.h
@@ -20,7 +20,7 @@
#include <dns/message.h>
#include "message_entry.h"
#include <nsas/hash_table.h>
-#include <nsas/lru_list.h>
+#include <util/lru_list.h>
#include "rrset_cache.h"
namespace isc {
@@ -30,6 +30,8 @@ namespace cache {
/// The object of MessageCache represents the cache for class-specific
/// messages.
///
+/// \todo The message cache class should provide the interfaces for
+/// loading, dumping and resizing.
class MessageCache {
// Noncopyable
private:
@@ -37,7 +39,7 @@ private:
MessageCache& operator=(const MessageCache& source);
public:
/// \param rrset_cache The cache that stores the RRsets that the
- /// message entry will points to
+ /// message entry will point to
/// \param cache_size The size of message cache.
/// \param message_class The class of the message cache
/// \param negative_soa_cache The cache that stores the SOA record
@@ -47,7 +49,7 @@ public:
const RRsetCachePtr& negative_soa_cache);
/// \brief Destructor function
- virtual ~MessageCache() {}
+ virtual ~MessageCache();
/// \brief Look up message in cache.
/// \param message generated response message if the message entry
@@ -64,20 +66,6 @@ public:
/// If the message doesn't exist in the cache, it will be added
/// directly.
bool update(const isc::dns::Message& msg);
-
-#if 0
- /// \brief Dump the message cache to specified file.
- /// \todo It should can be dumped to one configured database.
- void dump(const std::string& file_name);
-
- /// \brief Load the cache from one file.
- /// \todo It should can be loaded from one configured database.
- void load(const std::string& file_name);
-
- /// \brief Resize the size of message cache in runtime.
- bool resize(uint32_t size);
-#endif
-
protected:
/// \brief Get the hash key for the message entry in the cache.
/// \param name query name of the message.
@@ -92,7 +80,7 @@ protected:
RRsetCachePtr rrset_cache_;
RRsetCachePtr negative_soa_cache_;
isc::nsas::HashTable<MessageEntry> message_table_;
- isc::nsas::LruList<MessageEntry> message_lru_;
+ isc::util::LruList<MessageEntry> message_lru_;
};
typedef boost::shared_ptr<MessageCache> MessageCachePtr;
diff --git a/src/lib/cache/message_entry.cc b/src/lib/cache/message_entry.cc
index de4ea89..d9560a6 100644
--- a/src/lib/cache/message_entry.cc
+++ b/src/lib/cache/message_entry.cc
@@ -20,6 +20,7 @@
#include "message_entry.h"
#include "message_utility.h"
#include "rrset_cache.h"
+#include "logger.h"
using namespace isc::dns;
using namespace std;
@@ -64,7 +65,7 @@ static uint32_t MAX_UINT32 = numeric_limits<uint32_t>::max();
// tunable. Values of one to three hours have been found to work well
// and would make sensible a default. Values exceeding one day have
// been found to be problematic. (sec 5, RFC2308)
-// The default value is 3 hourse (10800 seconds)
+// The default value is 3 hours (10800 seconds)
// TODO:Give an option to let user configure
static uint32_t MAX_NEGATIVE_CACHE_TTL = 10800;
@@ -142,6 +143,8 @@ MessageEntry::genMessage(const time_t& time_now,
// has expired, if it is, return false.
vector<RRsetEntryPtr> rrset_entry_vec;
if (false == getRRsetEntries(rrset_entry_vec, time_now)) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_ENTRY_MISSING_RRSET).
+ arg(entry_name_);
return (false);
}
diff --git a/src/lib/cache/message_entry.h b/src/lib/cache/message_entry.h
index 7d86495..6775ff6 100644
--- a/src/lib/cache/message_entry.h
+++ b/src/lib/cache/message_entry.h
@@ -76,6 +76,8 @@ public:
const RRsetCachePtr& rrset_cache,
const RRsetCachePtr& negative_soa_cache);
+ ~MessageEntry() { delete hash_key_ptr_; };
+
/// \brief generate one dns message according
/// the rrsets information of the message.
///
diff --git a/src/lib/cache/resolver_cache.cc b/src/lib/cache/resolver_cache.cc
index 261db3c..57935c0 100644
--- a/src/lib/cache/resolver_cache.cc
+++ b/src/lib/cache/resolver_cache.cc
@@ -17,6 +17,7 @@
#include "resolver_cache.h"
#include "dns/message.h"
#include "rrset_cache.h"
+#include "logger.h"
#include <string>
#include <algorithm>
@@ -29,6 +30,7 @@ namespace cache {
ResolverClassCache::ResolverClassCache(const RRClass& cache_class) :
cache_class_(cache_class)
{
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, CACHE_RESOLVER_INIT).arg(cache_class);
local_zone_data_ = LocalZoneDataPtr(new LocalZoneData(cache_class_.getCode()));
rrsets_cache_ = RRsetCachePtr(new RRsetCache(RRSET_CACHE_DEFAULT_SIZE,
cache_class_.getCode()));
@@ -45,6 +47,8 @@ ResolverClassCache::ResolverClassCache(const RRClass& cache_class) :
ResolverClassCache::ResolverClassCache(const CacheSizeInfo& cache_info) :
cache_class_(cache_info.cclass)
{
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, CACHE_RESOLVER_INIT_INFO).
+ arg(cache_class_);
uint16_t klass = cache_class_.getCode();
// TODO We should find one way to load local zone data.
local_zone_data_ = LocalZoneDataPtr(new LocalZoneData(klass));
@@ -69,8 +73,11 @@ ResolverClassCache::lookup(const isc::dns::Name& qname,
const isc::dns::RRType& qtype,
isc::dns::Message& response) const
{
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_LOOKUP_MSG).
+ arg(qname).arg(qtype);
// message response should has question section already.
if (response.beginQuestion() == response.endQuestion()) {
+ LOG_ERROR(logger, CACHE_RESOLVER_NO_QUESTION).arg(qname).arg(qtype);
isc_throw(MessageNoQuestionSection, "Message has no question section");
}
@@ -79,6 +86,8 @@ ResolverClassCache::lookup(const isc::dns::Name& qname,
// answer section.
RRsetPtr rrset_ptr = local_zone_data_->lookup(qname, qtype);
if (rrset_ptr) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_LOCAL_MSG).
+ arg(qname).arg(qtype);
response.addRRset(Message::SECTION_ANSWER, rrset_ptr);
return (true);
}
@@ -91,11 +100,15 @@ isc::dns::RRsetPtr
ResolverClassCache::lookup(const isc::dns::Name& qname,
const isc::dns::RRType& qtype) const
{
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_LOOKUP_RRSET).
+ arg(qname).arg(qtype);
// Algorithm:
// 1. Search in local zone data first,
// 2. Then do search in rrsets_cache_.
RRsetPtr rrset_ptr = local_zone_data_->lookup(qname, qtype);
if (rrset_ptr) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_LOCAL_RRSET).
+ arg(qname).arg(qtype);
return (rrset_ptr);
} else {
RRsetEntryPtr rrset_entry = rrsets_cache_->lookup(qname, qtype);
@@ -109,6 +122,10 @@ ResolverClassCache::lookup(const isc::dns::Name& qname,
bool
ResolverClassCache::update(const isc::dns::Message& msg) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_UPDATE_MSG).
+ arg((*msg.beginQuestion())->getName()).
+ arg((*msg.beginQuestion())->getType()).
+ arg((*msg.beginQuestion())->getClass());
return (messages_cache_->update(msg));
}
@@ -130,6 +147,9 @@ ResolverClassCache::updateRRsetCache(const isc::dns::ConstRRsetPtr& rrset_ptr,
bool
ResolverClassCache::update(const isc::dns::ConstRRsetPtr& rrset_ptr) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_UPDATE_RRSET).
+ arg(rrset_ptr->getName()).arg(rrset_ptr->getType()).
+ arg(rrset_ptr->getClass());
// First update local zone, then update rrset cache.
local_zone_data_->update((*rrset_ptr.get()));
updateRRsetCache(rrset_ptr, rrsets_cache_);
@@ -166,6 +186,8 @@ ResolverCache::lookup(const isc::dns::Name& qname,
if (cc) {
return (cc->lookup(qname, qtype, response));
} else {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_UNKNOWN_CLASS_MSG).
+ arg(qclass);
return (false);
}
}
@@ -179,6 +201,8 @@ ResolverCache::lookup(const isc::dns::Name& qname,
if (cc) {
return (cc->lookup(qname, qtype));
} else {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_UNKNOWN_CLASS_RRSET).
+ arg(qclass);
return (RRsetPtr());
}
}
@@ -187,6 +211,8 @@ isc::dns::RRsetPtr
ResolverCache::lookupDeepestNS(const isc::dns::Name& qname,
const isc::dns::RRClass& qclass) const
{
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RESOLVER_DEEPEST).arg(qname).
+ arg(qclass);
isc::dns::RRType qtype = RRType::NS();
ResolverClassCache* cc = getClassCache(qclass);
if (cc) {
@@ -213,6 +239,9 @@ ResolverCache::update(const isc::dns::Message& msg) {
if (cc) {
return (cc->update(msg));
} else {
+ LOG_DEBUG(logger, DBG_TRACE_DATA,
+ CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_MSG).
+ arg((*msg.beginQuestion())->getClass());
return (false);
}
}
@@ -223,20 +252,13 @@ ResolverCache::update(const isc::dns::ConstRRsetPtr& rrset_ptr) {
if (cc) {
return (cc->update(rrset_ptr));
} else {
+ LOG_DEBUG(logger, DBG_TRACE_DATA,
+ CACHE_RESOLVER_UPDATE_UNKNOWN_CLASS_RRSET).
+ arg(rrset_ptr->getClass());
return (false);
}
}
-void
-ResolverCache::dump(const std::string&) {
- //TODO
-}
-
-void
-ResolverCache::load(const std::string&) {
- //TODO
-}
-
ResolverClassCache*
ResolverCache::getClassCache(const isc::dns::RRClass& cache_class) const {
for (int i = 0; i < class_caches_.size(); ++i) {
diff --git a/src/lib/cache/resolver_cache.h b/src/lib/cache/resolver_cache.h
index 49818b5..9ad4388 100644
--- a/src/lib/cache/resolver_cache.h
+++ b/src/lib/cache/resolver_cache.h
@@ -76,6 +76,9 @@ public:
///
/// \note Public interaction with the cache should be through ResolverCache,
/// not directly with this one. (TODO: make this private/hidden/local to the .cc?)
+///
+/// \todo The resolver cache class should provide the interfaces for
+/// loading, dumping and resizing.
class ResolverClassCache {
public:
/// \brief Default Constructor.
@@ -300,23 +303,6 @@ public:
///
bool update(const isc::dns::ConstRRsetPtr& rrset_ptr);
- /// \name Cache Serialization
- //@{
- /// \brief Dump the cache content to one file.
- ///
- /// \param file_name file to write to
- ///
- /// \todo It should can be dumped to one configured database.
- void dump(const std::string& file_name);
-
- /// \brief Load the cache from one file.
- ///
- /// \param file to load from
- ///
- /// \todo It should can be loaded from one configured database.
- void load(const std::string& file_name);
- //@}
-
private:
/// \brief Returns the class-specific subcache
///
diff --git a/src/lib/cache/rrset_cache.cc b/src/lib/cache/rrset_cache.cc
index f538320..1a5fd48 100644
--- a/src/lib/cache/rrset_cache.cc
+++ b/src/lib/cache/rrset_cache.cc
@@ -14,8 +14,9 @@
#include <config.h>
-#include <string>
#include "rrset_cache.h"
+#include "logger.h"
+#include <string>
#include <nsas/nsas_entry_compare.h>
#include <nsas/hash_table.h>
#include <nsas/hash_deleter.h>
@@ -34,20 +35,28 @@ RRsetCache::RRsetCache(uint32_t cache_size,
rrset_lru_((3 * cache_size),
new HashDeleter<RRsetEntry>(rrset_table_))
{
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, CACHE_RRSET_INIT).arg(cache_size).
+ arg(RRClass(rrset_class));
}
RRsetEntryPtr
RRsetCache::lookup(const isc::dns::Name& qname,
const isc::dns::RRType& qtype)
{
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RRSET_LOOKUP).arg(qname).
+ arg(qtype).arg(RRClass(class_));
const string entry_name = genCacheEntryName(qname, qtype);
- RRsetEntryPtr entry_ptr = rrset_table_.get(HashKey(entry_name, RRClass(class_)));
+
+ RRsetEntryPtr entry_ptr = rrset_table_.get(HashKey(entry_name,
+ RRClass(class_)));
if (entry_ptr) {
if (entry_ptr->getExpireTime() > time(NULL)) {
// Only touch the non-expired rrset entries
rrset_lru_.touch(entry_ptr);
return (entry_ptr);
} else {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RRSET_EXPIRED).arg(qname).
+ arg(qtype).arg(RRClass(class_));
// the rrset entry has expired, so just remove it from
// hash table and lru list.
rrset_table_.remove(entry_ptr->hashKey());
@@ -55,19 +64,31 @@ RRsetCache::lookup(const isc::dns::Name& qname,
}
}
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RRSET_NOT_FOUND).arg(qname).
+ arg(qtype).arg(RRClass(class_));
return (RRsetEntryPtr());
}
RRsetEntryPtr
-RRsetCache::update(const isc::dns::RRset& rrset, const RRsetTrustLevel& level) {
+RRsetCache::update(const isc::dns::RRset& rrset,
+ const RRsetTrustLevel& level)
+{
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RRSET_UPDATE).arg(rrset.getName()).
+ arg(rrset.getType()).arg(rrset.getClass());
// TODO: If the RRset is an NS, we should update the NSAS as well
// lookup first
RRsetEntryPtr entry_ptr = lookup(rrset.getName(), rrset.getType());
if (entry_ptr) {
if (entry_ptr->getTrustLevel() > level) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RRSET_UNTRUSTED).
+ arg(rrset.getName()).arg(rrset.getType()).
+ arg(rrset.getClass());
// existed rrset entry is more authoritative, just return it
return (entry_ptr);
} else {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RRSET_REMOVE_OLD).
+ arg(rrset.getName()).arg(rrset.getType()).
+ arg(rrset.getClass());
// Remove the old rrset entry from the lru list.
rrset_lru_.remove(entry_ptr);
}
@@ -79,24 +100,6 @@ RRsetCache::update(const isc::dns::RRset& rrset, const RRsetTrustLevel& level) {
return (entry_ptr);
}
-#if 0
-void
-RRsetCache::dump(const std::string&) {
- //TODO
-}
-
-void
-RRsetCache::load(const std::string&) {
- //TODO
-}
-
-bool
-RRsetCache::resize(uint32_t) {
- //TODO
- return (true);
-}
-#endif
-
} // namespace cache
} // namespace isc
diff --git a/src/lib/cache/rrset_cache.h b/src/lib/cache/rrset_cache.h
index 5bf2730..73f9b58 100644
--- a/src/lib/cache/rrset_cache.h
+++ b/src/lib/cache/rrset_cache.h
@@ -17,7 +17,8 @@
#include <cache/rrset_entry.h>
#include <nsas/hash_table.h>
-#include <nsas/lru_list.h>
+
+#include <util/lru_list.h>
using namespace isc::nsas;
@@ -29,6 +30,9 @@ class RRsetEntry;
/// \brief RRset Cache
/// The object of RRsetCache represented the cache for class-specific
/// RRsets.
+///
+/// \todo The rrset cache class should provide the interfaces for
+/// loading, dumping and resizing.
class RRsetCache{
///
/// \name Constructors and Destructor
@@ -40,12 +44,14 @@ private:
RRsetCache(const RRsetCache&);
RRsetCache& operator=(const RRsetCache&);
public:
- /// \brief Constructor
+ /// \brief Constructor and Destructor
///
/// \param cache_size the size of rrset cache.
/// \param rrset_class the class of rrset cache.
RRsetCache(uint32_t cache_size, uint16_t rrset_class);
- virtual ~RRsetCache() {}
+ virtual ~RRsetCache() {
+ rrset_lru_.clear(); // Clear the rrset entries in the list.
+ }
//@}
/// \brief Look up rrset in cache.
@@ -70,33 +76,11 @@ public:
RRsetEntryPtr update(const isc::dns::RRset& rrset,
const RRsetTrustLevel& level);
-#if 0
- /// \brief Dump the rrset cache to specified file.
- ///
- /// \param file_name The file to write to
- ///
- /// \todo It should can be dumped to one configured database.
- void dump(const std::string& file_name);
-
- /// \brief Load the cache from one file.
- ///
- /// \param file_name The file to read from
- ///
- /// \todo It should can be loaded from one configured database.
- void load(const std::string& file_name);
-
- /// \brief Resize the size of rrset cache in runtime.
- ///
- /// \param The size to resize to
- /// \return true
- bool resize(uint32_t size);
-#endif
-
/// \short Protected memebers, so they can be accessed by tests.
protected:
uint16_t class_; // The class of the rrset cache.
isc::nsas::HashTable<RRsetEntry> rrset_table_;
- isc::nsas::LruList<RRsetEntry> rrset_lru_;
+ isc::util::LruList<RRsetEntry> rrset_lru_;
};
typedef boost::shared_ptr<RRsetCache> RRsetCachePtr;
diff --git a/src/lib/cache/tests/Makefile.am b/src/lib/cache/tests/Makefile.am
index 4763f55..f9237af 100644
--- a/src/lib/cache/tests/Makefile.am
+++ b/src/lib/cache/tests/Makefile.am
@@ -2,6 +2,7 @@ SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/cache -I$(top_builddir)/src/lib/cache
AM_CPPFLAGS += -DTEST_DATA_SRCDIR=\"$(srcdir)/testdata\"
@@ -31,20 +32,20 @@ TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
-run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
-run_unittests_SOURCES += rrset_entry_unittest.cc
-run_unittests_SOURCES += rrset_cache_unittest.cc
-run_unittests_SOURCES += message_cache_unittest.cc
-run_unittests_SOURCES += message_entry_unittest.cc
-run_unittests_SOURCES += local_zone_data_unittest.cc
-run_unittests_SOURCES += resolver_cache_unittest.cc
-run_unittests_SOURCES += negative_cache_unittest.cc
-run_unittests_SOURCES += cache_test_messagefromfile.h
-run_unittests_SOURCES += cache_test_sectioncount.h
+run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
+run_unittests_SOURCES += rrset_entry_unittest.cc
+run_unittests_SOURCES += rrset_cache_unittest.cc
+run_unittests_SOURCES += message_cache_unittest.cc
+run_unittests_SOURCES += message_entry_unittest.cc
+run_unittests_SOURCES += local_zone_data_unittest.cc
+run_unittests_SOURCES += resolver_cache_unittest.cc
+run_unittests_SOURCES += negative_cache_unittest.cc
+run_unittests_SOURCES += cache_test_messagefromfile.h
+run_unittests_SOURCES += cache_test_sectioncount.h
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD)
# NOTE: we may have to clean up this hack later (see the note in configure.ac)
if NEED_LIBBOOST_THREAD
@@ -52,15 +53,17 @@ run_unittests_LDADD += -lboost_thread
endif
run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
noinst_PROGRAMS = $(TESTS)
-EXTRA_DIST = testdata/message_cname_referral.wire
+EXTRA_DIST = testdata/message_cname_referral.wire
EXTRA_DIST += testdata/message_example_com_soa.wire
EXTRA_DIST += testdata/message_fromWire1
EXTRA_DIST += testdata/message_fromWire2
diff --git a/src/lib/cache/tests/cache_test_messagefromfile.h b/src/lib/cache/tests/cache_test_messagefromfile.h
index 62e237c..7d55f08 100644
--- a/src/lib/cache/tests/cache_test_messagefromfile.h
+++ b/src/lib/cache/tests/cache_test_messagefromfile.h
@@ -14,7 +14,7 @@
#include <vector>
#include <dns/tests/unittest_util.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/message.h>
using namespace isc;
@@ -31,7 +31,7 @@ messageFromFile(Message& message, const char* datafile) {
std::vector<unsigned char> data;
UnitTestUtil::readWireData(datafile, data);
- InputBuffer buffer(&data[0], data.size());
+ isc::util::InputBuffer buffer(&data[0], data.size());
message.fromWire(buffer);
}
diff --git a/src/lib/cache/tests/cache_test_sectioncount.h b/src/lib/cache/tests/cache_test_sectioncount.h
index 537ca81..df7cb52 100644
--- a/src/lib/cache/tests/cache_test_sectioncount.h
+++ b/src/lib/cache/tests/cache_test_sectioncount.h
@@ -14,7 +14,7 @@
#include <vector>
#include <dns/tests/unittest_util.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/message.h>
using namespace isc;
diff --git a/src/lib/cache/tests/message_cache_unittest.cc b/src/lib/cache/tests/message_cache_unittest.cc
index fc62e21..60ae037 100644
--- a/src/lib/cache/tests/message_cache_unittest.cc
+++ b/src/lib/cache/tests/message_cache_unittest.cc
@@ -16,7 +16,7 @@
#include <string>
#include <gtest/gtest.h>
#include <dns/tests/unittest_util.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include "../message_cache.h"
#include "../rrset_cache.h"
#include "../resolver_cache.h"
@@ -25,6 +25,7 @@
using namespace isc::cache;
using namespace isc;
using namespace isc::dns;
+using namespace isc::util;
using namespace std;
namespace {
diff --git a/src/lib/cache/tests/message_entry_unittest.cc b/src/lib/cache/tests/message_entry_unittest.cc
index 2ca33ec..d9709ed 100644
--- a/src/lib/cache/tests/message_entry_unittest.cc
+++ b/src/lib/cache/tests/message_entry_unittest.cc
@@ -15,7 +15,7 @@
#include <gtest/gtest.h>
#include <dns/tests/unittest_util.h>
#include <dns/message.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include "../message_entry.h"
#include "../rrset_cache.h"
#include "../resolver_cache.h"
diff --git a/src/lib/cache/tests/run_unittests.cc b/src/lib/cache/tests/run_unittests.cc
index 2c86581..370bc69 100644
--- a/src/lib/cache/tests/run_unittests.cc
+++ b/src/lib/cache/tests/run_unittests.cc
@@ -15,14 +15,19 @@
#include <config.h>
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
+#include <log/logger_support.h>
+
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::UnitTestUtil::addDataPath(TEST_DATA_SRCDIR);
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
- return (RUN_ALL_TESTS());
+ isc::log::initLogger();
+
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/cc/Makefile.am b/src/lib/cc/Makefile.am
index 44033e9..c23b27c 100644
--- a/src/lib/cc/Makefile.am
+++ b/src/lib/cc/Makefile.am
@@ -11,6 +11,7 @@ if USE_GXX
# avoid the error. As a short term workaround we suppress this warning
# for the entire this module. See also src/bin/auth/Makefile.am.
AM_CXXFLAGS += -Wno-unused-parameter
+AM_CXXFLAGS += -fno-strict-aliasing
endif
if USE_CLANGPP
# Likewise, ASIO header files will trigger various warnings with clang++.
@@ -21,10 +22,18 @@ endif
lib_LTLIBRARIES = libcc.la
libcc_la_SOURCES = data.cc data.h session.cc session.h
+libcc_la_SOURCES += logger.cc logger.h
+nodist_libcc_la_SOURCES = cc_messages.cc cc_messages.h
+libcc_la_LIBADD = $(top_builddir)/src/lib/log/liblog.la
-CLEANFILES = *.gcno *.gcda session_config.h
+CLEANFILES = *.gcno *.gcda session_config.h cc_messages.cc cc_messages.h
session_config.h: session_config.h.pre
$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" session_config.h.pre >$@
-BUILT_SOURCES = session_config.h
+cc_messages.cc cc_messages.h: cc_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/cc/cc_messages.mes
+
+BUILT_SOURCES = session_config.h cc_messages.cc cc_messages.h
+
+EXTRA_DIST = cc_messages.mes
diff --git a/src/lib/cc/cc_messages.mes b/src/lib/cc/cc_messages.mes
new file mode 100644
index 0000000..8c62ea1
--- /dev/null
+++ b/src/lib/cc/cc_messages.mes
@@ -0,0 +1,108 @@
+# Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+$NAMESPACE isc::cc
+
+% CC_ASYNC_READ_FAILED asynchronous read failed
+This marks a low level error, we tried to read data from the message queue
+daemon asynchronously, but the ASIO library returned an error.
+
+% CC_CONN_ERROR error connecting to message queue (%1)
+It is impossible to reach the message queue daemon for the reason given. It
+is unlikely there'll be reason for whatever program this currently is to
+continue running, as the communication with the rest of BIND 10 is vital
+for the components.
+
+% CC_DISCONNECT disconnecting from message queue daemon
+The library is disconnecting from the message queue daemon. This debug message
+indicates that the program is trying to shut down gracefully.
+
+% CC_ESTABLISH trying to establish connection with message queue daemon at %1
+This debug message indicates that the command channel library is about to
+connect to the message queue daemon, which should be listening on the UNIX-domain
+socket listed in the output.
+
+% CC_ESTABLISHED successfully connected to message queue daemon
+This debug message indicates that the connection was successfully made, this
+should follow CC_ESTABLISH.
+
+% CC_GROUP_RECEIVE trying to receive a message
+Debug message, noting that a message is expected to come over the command
+channel.
+
+% CC_GROUP_RECEIVED message arrived ('%1', '%2')
+Debug message, noting that we successfully received a message (its envelope and
+payload listed). This follows CC_GROUP_RECEIVE, but might happen some time
+later, depending if we waited for it or just polled.
+
+% CC_GROUP_SEND sending message '%1' to group '%2'
+Debug message, we're about to send a message over the command channel.
+
+% CC_INVALID_LENGTHS invalid length parameters (%1, %2)
+This happens when garbage comes over the command channel or some kind of
+confusion happens in the program. The data received from the socket make no
+sense if we interpret it as lengths of message. The first one is total length
+of message, the second length of the header. The header and it's length
+(2 bytes) is counted in the total length.
+
+% CC_LENGTH_NOT_READY length not ready
+There should be data representing length of message on the socket, but it
+is not there.
+
+% CC_NO_MESSAGE no message ready to be received yet
+The program polled for incoming messages, but there was no message waiting.
+This is a debug message which may happen only after CC_GROUP_RECEIVE.
+
+% CC_NO_MSGQ unable to connect to message queue (%1)
+It isn't possible to connect to the message queue daemon, for reason listed.
+It is unlikely any program will be able continue without the communication.
+
+% CC_READ_ERROR error reading data from command channel (%1)
+A low level error happened when the library tried to read data from the
+command channel socket. The reason is listed.
+
+% CC_READ_EXCEPTION error reading data from command channel (%1)
+We received an exception while trying to read data from the command
+channel socket. The reason is listed.
+
+% CC_REPLY replying to message from '%1' with '%2'
+Debug message, noting we're sending a response to the original message
+with the given envelope.
+
+% CC_SET_TIMEOUT setting timeout to %1ms
+Debug message. A timeout for which the program is willing to wait for a reply
+is being set.
+
+% CC_START_READ starting asynchronous read
+Debug message. From now on, when a message (or command) comes, it'll wake the
+program and the library will automatically pass it over to correct place.
+
+% CC_SUBSCRIBE subscribing to communication group %1
+Debug message. The program wants to receive messages addressed to this group.
+
+% CC_TIMEOUT timeout reading data from command channel
+The program waited too long for data from the command channel (usually when it
+sent a query to different program and it didn't answer for whatever reason).
+
+% CC_UNSUBSCRIBE unsubscribing from communication group %1
+Debug message. The program no longer wants to receive messages addressed to
+this group.
+
+% CC_WRITE_ERROR error writing data to command channel (%1)
+A low level error happened when the library tried to write data to the command
+channel socket.
+
+% CC_ZERO_LENGTH invalid message length (0)
+The library received a message length being zero, which makes no sense, since
+all messages must contain at least the envelope.
diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc
index 6f7d4a2..a455d43 100644
--- a/src/lib/cc/data.cc
+++ b/src/lib/cc/data.cc
@@ -52,13 +52,6 @@ Element::toWire(std::ostream& ss) const {
toJSON(ss);
}
-//
-// The following methods are effectively empty, and their parameters are
-// unused. To silence compilers that warn unused function parameters,
-// we specify a (compiler dependent) special keyword when available.
-// It's defined in config.h, and to avoid including this header file from
-// installed files we define the methods here.
-//
bool
Element::getValue(long int&) {
return (false);
@@ -454,7 +447,9 @@ from_stringstream_map(std::istream &in, const std::string& file, int& line,
ElementPtr map = Element::createMap();
skip_chars(in, " \t\n", line, pos);
char c = in.peek();
- if (c == '}') {
+ if (c == EOF) {
+ throwJSONError(std::string("Unterminated map, <string> or } expected"), file, line, pos);
+ } else if (c == '}') {
// empty map, skip closing curly
c = in.get();
} else {
diff --git a/src/lib/cc/data.h b/src/lib/cc/data.h
index 0a363f4..5c731e6 100644
--- a/src/lib/cc/data.h
+++ b/src/lib/cc/data.h
@@ -479,7 +479,7 @@ public:
return (true);
}
using Element::setValue;
- bool setValue(std::map<std::string, ConstElementPtr>& v) {
+ bool setValue(const std::map<std::string, ConstElementPtr>& v) {
m = v;
return (true);
}
diff --git a/src/lib/cc/logger.cc b/src/lib/cc/logger.cc
new file mode 100644
index 0000000..36db88d
--- /dev/null
+++ b/src/lib/cc/logger.cc
@@ -0,0 +1,23 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cc/logger.h>
+
+namespace isc {
+namespace cc {
+
+isc::log::Logger logger("cc");
+
+}
+}
diff --git a/src/lib/cc/logger.h b/src/lib/cc/logger.h
new file mode 100644
index 0000000..567ccee
--- /dev/null
+++ b/src/lib/cc/logger.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef CC_LOGGER_H
+#define CC_LOGGER_H
+
+#include <cc/cc_messages.h>
+#include <log/macros.h>
+
+/// \file logger.h
+/// \brief Command Channel library global logger
+///
+/// This holds the logger for the CC library. It is a private header
+/// and should not be included in any publicly used header, only in local
+/// cc files.
+
+namespace isc {
+namespace cc {
+
+enum {
+ /// \brief Trace basic operation
+ DBG_TRACE_BASIC = 10,
+ /// \brief Trace even details
+ ///
+ /// This includes messages being sent and received, waiting for messages
+ /// and alike.
+ DBG_TRACE_DETAILED = 80
+};
+
+/// \brief Logger for this library
+extern isc::log::Logger logger;
+
+}
+}
+
+#endif
diff --git a/src/lib/cc/session.cc b/src/lib/cc/session.cc
index e911a86..97d5cf1 100644
--- a/src/lib/cc/session.cc
+++ b/src/lib/cc/session.cc
@@ -14,6 +14,7 @@
#include <config.h>
#include <cc/session_config.h>
+#include <cc/logger.h>
#include <stdint.h>
@@ -118,12 +119,16 @@ private:
void
SessionImpl::establish(const char& socket_file) {
try {
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, CC_ESTABLISH).arg(socket_file);
socket_.connect(asio::local::stream_protocol::endpoint(&socket_file),
error_);
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, CC_ESTABLISHED);
} catch(const asio::system_error& se) {
+ LOG_FATAL(logger, CC_CONN_ERROR).arg(se.what());
isc_throw(SessionError, se.what());
}
if (error_) {
+ LOG_FATAL(logger, CC_NO_MSGQ).arg(error_.message());
isc_throw(SessionError, "Unable to connect to message queue: " <<
error_.message());
}
@@ -131,6 +136,7 @@ SessionImpl::establish(const char& socket_file) {
void
SessionImpl::disconnect() {
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, CC_DISCONNECT);
socket_.close();
data_length_ = 0;
}
@@ -140,6 +146,7 @@ SessionImpl::writeData(const void* data, size_t datalen) {
try {
asio::write(socket_, asio::buffer(data, datalen));
} catch (const asio::system_error& asio_ex) {
+ LOG_FATAL(logger, CC_WRITE_ERROR).arg(asio_ex.what());
isc_throw(SessionError, "ASIO write failed: " << asio_ex.what());
}
}
@@ -151,6 +158,7 @@ SessionImpl::readDataLength() {
if (ret_len == 0) {
readData(&data_length_, sizeof(data_length_));
if (data_length_ == 0) {
+ LOG_ERROR(logger, CC_LENGTH_NOT_READY);
isc_throw(SessionError, "ASIO read: data length is not ready");
}
ret_len = ntohl(data_length_);
@@ -199,9 +207,11 @@ SessionImpl::readData(void* data, size_t datalen) {
// asio::error_code evaluates to false if there was no error
if (*read_result) {
if (*read_result == asio::error::operation_aborted) {
+ LOG_ERROR(logger, CC_TIMEOUT);
isc_throw(SessionTimeout,
"Timeout while reading data from cc session");
} else {
+ LOG_ERROR(logger, CC_READ_ERROR).arg(read_result->message());
isc_throw(SessionError,
"Error while reading data from cc session: " <<
read_result->message());
@@ -210,6 +220,7 @@ SessionImpl::readData(void* data, size_t datalen) {
} catch (const asio::system_error& asio_ex) {
// to hide ASIO specific exceptions, we catch them explicitly
// and convert it to SessionError.
+ LOG_FATAL(logger, CC_READ_EXCEPTION).arg(asio_ex.what());
isc_throw(SessionError, "ASIO read failed: " << asio_ex.what());
}
}
@@ -233,10 +244,12 @@ SessionImpl::internalRead(const asio::error_code& error,
assert(bytes_transferred == sizeof(data_length_));
data_length_ = ntohl(data_length_);
if (data_length_ == 0) {
+ LOG_ERROR(logger, CC_ZERO_LENGTH);
isc_throw(SessionError, "Invalid message length (0)");
}
user_handler_();
} else {
+ LOG_ERROR(logger, CC_ASYNC_READ_FAILED);
isc_throw(SessionError, "asynchronous read failed");
}
}
@@ -255,6 +268,7 @@ Session::disconnect() {
void
Session::startRead(boost::function<void()> read_callback) {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, CC_START_READ);
impl_->startRead(read_callback);
}
@@ -374,6 +388,7 @@ Session::recvmsg(ConstElementPtr& env, ConstElementPtr& msg,
unsigned short header_length = ntohs(header_length_net);
if (header_length > length || length < 2) {
+ LOG_ERROR(logger, CC_INVALID_LENGTHS).arg(length).arg(header_length);
isc_throw(SessionError, "Length parameters invalid: total=" << length
<< ", header=" << header_length);
}
@@ -417,6 +432,7 @@ Session::recvmsg(ConstElementPtr& env, ConstElementPtr& msg,
void
Session::subscribe(std::string group, std::string instance) {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, CC_SUBSCRIBE).arg(group);
ElementPtr env = Element::createMap();
env->set("type", Element::create("subscribe"));
@@ -428,6 +444,7 @@ Session::subscribe(std::string group, std::string instance) {
void
Session::unsubscribe(std::string group, std::string instance) {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, CC_UNSUBSCRIBE).arg(group);
ElementPtr env = Element::createMap();
env->set("type", Element::create("unsubscribe"));
@@ -441,6 +458,8 @@ int
Session::group_sendmsg(ConstElementPtr msg, std::string group,
std::string instance, std::string to)
{
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, CC_GROUP_SEND).arg(msg->str()).
+ arg(group);
ElementPtr env = Element::createMap();
long int nseq = ++impl_->sequence_;
@@ -460,11 +479,21 @@ bool
Session::group_recvmsg(ConstElementPtr& envelope, ConstElementPtr& msg,
bool nonblock, int seq)
{
- return (recvmsg(envelope, msg, nonblock, seq));
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, CC_GROUP_RECEIVE);
+ bool result(recvmsg(envelope, msg, nonblock, seq));
+ if (result) {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, CC_GROUP_RECEIVED).
+ arg(envelope->str()).arg(msg->str());
+ } else {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, CC_NO_MESSAGE);
+ }
+ return (result);
}
int
Session::reply(ConstElementPtr envelope, ConstElementPtr newmsg) {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, CC_REPLY).arg(envelope->str()).
+ arg(newmsg->str());
ElementPtr env = Element::createMap();
long int nseq = ++impl_->sequence_;
@@ -488,6 +517,7 @@ Session::hasQueuedMsgs() const {
void
Session::setTimeout(size_t milliseconds) {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, CC_SET_TIMEOUT).arg(milliseconds);
impl_->setTimeout(milliseconds);
}
diff --git a/src/lib/cc/tests/Makefile.am b/src/lib/cc/tests/Makefile.am
index 535c464..4760855 100644
--- a/src/lib/cc/tests/Makefile.am
+++ b/src/lib/cc/tests/Makefile.am
@@ -26,7 +26,8 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
-run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc
index 2536682..53d5ab8 100644
--- a/src/lib/cc/tests/data_unittests.cc
+++ b/src/lib/cc/tests/data_unittests.cc
@@ -396,9 +396,24 @@ TEST(Element, to_and_from_wire) {
EXPECT_EQ("1", Element::fromWire(ss, 1)->str());
// Some malformed JSON input
+ EXPECT_THROW(Element::fromJSON("{ "), isc::data::JSONError);
+ EXPECT_THROW(Element::fromJSON("{ \"a\" "), isc::data::JSONError);
+ EXPECT_THROW(Element::fromJSON("{ \"a\": "), isc::data::JSONError);
+ EXPECT_THROW(Element::fromJSON("{ \"a\": \"b\""), isc::data::JSONError);
+ EXPECT_THROW(Element::fromJSON("{ \"a\": {"), isc::data::JSONError);
+ EXPECT_THROW(Element::fromJSON("{ \"a\": {}"), isc::data::JSONError);
+ EXPECT_THROW(Element::fromJSON("{ \"a\": []"), isc::data::JSONError);
+ EXPECT_THROW(Element::fromJSON("{ \"a\": [ }"), isc::data::JSONError);
EXPECT_THROW(Element::fromJSON("{\":"), isc::data::JSONError);
EXPECT_THROW(Element::fromJSON("]"), isc::data::JSONError);
EXPECT_THROW(Element::fromJSON("[ 1, 2, }"), isc::data::JSONError);
+ EXPECT_THROW(Element::fromJSON("[ 1, 2, {}"), isc::data::JSONError);
+ EXPECT_THROW(Element::fromJSON("[ 1, 2, { ]"), isc::data::JSONError);
+ EXPECT_THROW(Element::fromJSON("[ "), isc::data::JSONError);
+ EXPECT_THROW(Element::fromJSON("{{}}"), isc::data::JSONError);
+ EXPECT_THROW(Element::fromJSON("{[]}"), isc::data::JSONError);
+ EXPECT_THROW(Element::fromJSON("{ \"a\", \"b\" }"), isc::data::JSONError);
+ EXPECT_THROW(Element::fromJSON("[ \"a\": \"b\" ]"), isc::data::JSONError);
}
ConstElementPtr
diff --git a/src/lib/cc/tests/run_unittests.cc b/src/lib/cc/tests/run_unittests.cc
index 0908071..299bd96 100644
--- a/src/lib/cc/tests/run_unittests.cc
+++ b/src/lib/cc/tests/run_unittests.cc
@@ -13,9 +13,14 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+#include <log/logger_support.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+
+ isc::log::initLogger();
+
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/config/Makefile.am b/src/lib/config/Makefile.am
index 580f240..500ff12 100644
--- a/src/lib/config/Makefile.am
+++ b/src/lib/config/Makefile.am
@@ -2,10 +2,24 @@ SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CXXFLAGS = $(B10_CXXFLAGS) -Wno-strict-aliasing
+
+# Define rule to build logging source files from message file
+config_messages.h config_messages.cc: config_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/config/config_messages.mes
+
+BUILT_SOURCES = config_messages.h config_messages.cc
lib_LTLIBRARIES = libcfgclient.la
-libcfgclient_la_SOURCES = config_data.h config_data.cc module_spec.h module_spec.cc ccsession.cc ccsession.h
+libcfgclient_la_SOURCES = config_data.h config_data.cc
+libcfgclient_la_SOURCES += module_spec.h module_spec.cc
+libcfgclient_la_SOURCES += ccsession.cc ccsession.h
+libcfgclient_la_SOURCES += config_log.h config_log.cc
+
+nodist_libcfgclient_la_SOURCES = config_messages.h config_messages.cc
+
+# The message file should be in the distribution.
+EXTRA_DIST = config_messages.mes
-CLEANFILES = *.gcno *.gcda
+CLEANFILES = *.gcno *.gcda config_messages.h config_messages.cc
diff --git a/src/lib/config/ccsession.cc b/src/lib/config/ccsession.cc
index 69621a4..6b094ec 100644
--- a/src/lib/config/ccsession.cc
+++ b/src/lib/config/ccsession.cc
@@ -12,12 +12,6 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-//
-// todo: generalize this and make it into a specific API for all modules
-// to use (i.e. connect to cc, send config and commands, get config,
-// react on config change announcements)
-//
-
#include <config.h>
#include <stdexcept>
@@ -29,6 +23,7 @@
#include <fstream>
#include <sstream>
#include <cerrno>
+#include <set>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
@@ -38,8 +33,14 @@
#include <cc/session.h>
#include <exceptions/exceptions.h>
+#include <config/config_log.h>
#include <config/ccsession.h>
+#include <log/logger_support.h>
+#include <log/logger_specification.h>
+#include <log/logger_manager.h>
+#include <log/logger_name.h>
+
using namespace std;
using isc::data::Element;
@@ -156,6 +157,163 @@ parseCommand(ConstElementPtr& arg, ConstElementPtr command) {
}
}
+namespace {
+// Temporary workaround functions for missing functionality in
+// getValue() (main problem described in ticket #993)
+// This returns either the value set for the given relative id,
+// or its default value
+// (intentially defined here so this interface does not get
+// included in ConfigData as it is)
+ConstElementPtr getValueOrDefault(ConstElementPtr config_part,
+ const std::string& relative_id,
+ const ConfigData& config_data,
+ const std::string& full_id) {
+ if (config_part->contains(relative_id)) {
+ return config_part->get(relative_id);
+ } else {
+ return config_data.getDefaultValue(full_id);
+ }
+}
+
+// Reads a output_option subelement of a logger configuration,
+// and sets the values thereing to the given OutputOption struct,
+// or defaults values if they are not provided (from config_data).
+void
+readOutputOptionConf(isc::log::OutputOption& output_option,
+ ConstElementPtr output_option_el,
+ const ConfigData& config_data)
+{
+ ConstElementPtr destination_el = getValueOrDefault(output_option_el,
+ "destination", config_data,
+ "loggers/output_options/destination");
+ output_option.destination = isc::log::getDestination(destination_el->stringValue());
+ ConstElementPtr output_el = getValueOrDefault(output_option_el,
+ "output", config_data,
+ "loggers/output_options/output");
+ if (output_option.destination == isc::log::OutputOption::DEST_CONSOLE) {
+ output_option.stream = isc::log::getStream(output_el->stringValue());
+ } else if (output_option.destination == isc::log::OutputOption::DEST_FILE) {
+ output_option.filename = output_el->stringValue();
+ } else if (output_option.destination == isc::log::OutputOption::DEST_SYSLOG) {
+ output_option.facility = output_el->stringValue();
+ }
+ output_option.flush = getValueOrDefault(output_option_el,
+ "flush", config_data,
+ "loggers/output_options/flush")->boolValue();
+ output_option.maxsize = getValueOrDefault(output_option_el,
+ "maxsize", config_data,
+ "loggers/output_options/maxsize")->intValue();
+ output_option.maxver = getValueOrDefault(output_option_el,
+ "maxver", config_data,
+ "loggers/output_options/maxver")->intValue();
+}
+
+// Reads a full 'loggers' configuration, and adds the loggers therein
+// to the given vector, fills in blanks with defaults from config_data
+void
+readLoggersConf(std::vector<isc::log::LoggerSpecification>& specs,
+ ConstElementPtr logger,
+ const ConfigData& config_data)
+{
+ std::string lname = logger->get("name")->stringValue();
+
+ ConstElementPtr severity_el = getValueOrDefault(logger,
+ "severity", config_data,
+ "loggers/severity");
+ isc::log::Severity severity = isc::log::getSeverity(
+ severity_el->stringValue());
+ int dbg_level = getValueOrDefault(logger, "debuglevel",
+ config_data,
+ "loggers/debuglevel")->intValue();
+ bool additive = getValueOrDefault(logger, "additive", config_data,
+ "loggers/additive")->boolValue();
+
+ isc::log::LoggerSpecification logger_spec(
+ lname, severity, dbg_level, additive
+ );
+
+ if (logger->contains("output_options")) {
+ BOOST_FOREACH(ConstElementPtr output_option_el,
+ logger->get("output_options")->listValue()) {
+ // create outputoptions
+ isc::log::OutputOption output_option;
+ readOutputOptionConf(output_option,
+ output_option_el,
+ config_data);
+ logger_spec.addOutputOption(output_option);
+ }
+ }
+
+ specs.push_back(logger_spec);
+}
+
+} // end anonymous namespace
+
+
+ConstElementPtr
+getRelatedLoggers(ConstElementPtr loggers) {
+ // Keep a list of names for easier lookup later
+ std::set<std::string> our_names;
+ const std::string& root_name = isc::log::getRootLoggerName();
+
+ ElementPtr result = isc::data::Element::createList();
+
+ BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) {
+ const std::string cur_name = cur_logger->get("name")->stringValue();
+ if (cur_name == root_name || cur_name.find(root_name + ".") == 0) {
+ our_names.insert(cur_name);
+ result->add(cur_logger);
+ }
+ }
+
+ // now find the * names
+ BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) {
+ std::string cur_name = cur_logger->get("name")->stringValue();
+ // if name is '*', or starts with '*.', replace * with root
+ // logger name
+ if (cur_name == "*" || cur_name.length() > 1 &&
+ cur_name[0] == '*' && cur_name[1] == '.') {
+
+ cur_name = root_name + cur_name.substr(1);
+ // now add it to the result list, but only if a logger with
+ // that name was not configured explicitely
+ if (our_names.find(cur_name) == our_names.end()) {
+ // we substitute the name here already, but as
+ // we are dealing with consts, we copy the data
+ ElementPtr new_logger(Element::createMap());
+ // since we'll only be updating one first-level element,
+ // and we return as const again, a shallow map copy is
+ // enough
+ new_logger->setValue(cur_logger->mapValue());
+ new_logger->set("name", Element::create(cur_name));
+ result->add(new_logger);
+ }
+ }
+ }
+ return result;
+}
+
+void
+default_logconfig_handler(const std::string& module_name,
+ ConstElementPtr new_config,
+ const ConfigData& config_data) {
+ config_data.getModuleSpec().validateConfig(new_config, true);
+
+ std::vector<isc::log::LoggerSpecification> specs;
+
+ if (new_config->contains("loggers")) {
+ ConstElementPtr loggers = getRelatedLoggers(new_config->get("loggers"));
+ BOOST_FOREACH(ConstElementPtr logger,
+ loggers->listValue()) {
+ readLoggersConf(specs, logger, config_data);
+ }
+ }
+
+ isc::log::LoggerManager logger_manager;
+ logger_manager.process(specs.begin(), specs.end());
+}
+
+
ModuleSpec
ModuleCCSession::readModuleSpecification(const std::string& filename) {
std::ifstream file;
@@ -164,18 +322,18 @@ ModuleCCSession::readModuleSpecification(const std::string& filename) {
// this file should be declared in a @something@ directive
file.open(filename.c_str());
if (!file) {
- cout << "error opening " << filename << ": " << strerror(errno) << endl;
- exit(1);
+ LOG_ERROR(config_logger, CONFIG_OPEN_FAIL).arg(filename).arg(strerror(errno));
+ isc_throw(CCSessionInitError, strerror(errno));
}
try {
module_spec = moduleSpecFromFile(file, true);
} catch (const JSONError& pe) {
- cout << "Error parsing module specification file: " << pe.what() << endl;
- exit(1);
+ LOG_ERROR(config_logger, CONFIG_JSON_PARSE).arg(filename).arg(pe.what());
+ isc_throw(CCSessionInitError, pe.what());
} catch (const ModuleSpecError& dde) {
- cout << "Error reading module specification file: " << dde.what() << endl;
- exit(1);
+ LOG_ERROR(config_logger, CONFIG_MOD_SPEC_FORMAT).arg(filename).arg(dde.what());
+ isc_throw(CCSessionInitError, dde.what());
}
file.close();
return (module_spec);
@@ -197,8 +355,11 @@ ModuleCCSession::ModuleCCSession(
isc::data::ConstElementPtr(*config_handler)(
isc::data::ConstElementPtr new_config),
isc::data::ConstElementPtr(*command_handler)(
- const std::string& command, isc::data::ConstElementPtr args)
+ const std::string& command, isc::data::ConstElementPtr args),
+ bool start_immediately,
+ bool handle_logging
) :
+ started_(false),
session_(session)
{
module_specification_ = readModuleSpecification(spec_file_name);
@@ -210,10 +371,8 @@ ModuleCCSession::ModuleCCSession(
session_.establish(NULL);
session_.subscribe(module_name_, "*");
- //session_.subscribe("Boss", "*");
- //session_.subscribe("statistics", "*");
- // send the data specification
+ // send the data specification
ConstElementPtr spec_msg = createCommand("module_spec",
module_specification_.getFullSpec());
unsigned int seq = session_.group_sendmsg(spec_msg, "ConfigManager");
@@ -223,7 +382,8 @@ ModuleCCSession::ModuleCCSession(
int rcode;
ConstElementPtr err = parseAnswer(rcode, answer);
if (rcode != 0) {
- std::cerr << "[" << module_name_ << "] Error in specification: " << answer << std::endl;
+ LOG_ERROR(config_logger, CONFIG_MOD_SPEC_REJECT).arg(answer->str());
+ isc_throw(CCSessionInitError, answer->str());
}
setLocalConfig(Element::fromJSON("{}"));
@@ -236,12 +396,32 @@ ModuleCCSession::ModuleCCSession(
if (rcode == 0) {
handleConfigUpdate(new_config);
} else {
- std::cerr << "[" << module_name_ << "] Error getting config: " << new_config << std::endl;
+ LOG_ERROR(config_logger, CONFIG_GET_FAIL).arg(new_config->str());
+ isc_throw(CCSessionInitError, answer->str());
}
}
+ // Keep track of logging settings automatically
+ if (handle_logging) {
+ addRemoteConfig("Logging", default_logconfig_handler, false);
+ }
+
+ if (start_immediately) {
+ start();
+ }
+
+}
+
+void
+ModuleCCSession::start() {
+ if (started_) {
+ isc_throw(CCSessionError, "Module CC session already started");
+ }
+
// register callback for asynchronous read
session_.startRead(boost::bind(&ModuleCCSession::startCheck, this));
+
+ started_ = true;
}
/// Validates the new config values, if they are correct,
@@ -348,35 +528,79 @@ ModuleCCSession::checkCommand() {
answer = checkModuleCommand(cmd_str, target_module, arg);
}
} catch (const CCSessionError& re) {
- // TODO: Once we have logging and timeouts, we should not
- // answer here (potential interference)
- answer = createAnswer(1, re.what());
+ LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG).arg(re.what());
+ } catch (const std::exception& stde) {
+ // No matter what unexpected error happens, we do not want
+ // to crash because of an incoming event, so we log the
+ // exception and continue to run
+ LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG_INTERNAL).arg(stde.what());
}
if (!isNull(answer)) {
session_.reply(routing, answer);
}
}
-
+
return (0);
}
+ModuleSpec
+ModuleCCSession::fetchRemoteSpec(const std::string& module, bool is_filename) {
+ if (is_filename) {
+ // It is a filename, simply load it.
+ return (readModuleSpecification(module));
+ } else {
+ // It's module name, request it from config manager
+
+ // Send the command
+ ConstElementPtr cmd(createCommand("get_module_spec",
+ Element::fromJSON("{\"module_name\": \"" + module +
+ "\"}")));
+ unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
+ ConstElementPtr env, answer;
+ session_.group_recvmsg(env, answer, false, seq);
+ int rcode;
+ ConstElementPtr spec_data = parseAnswer(rcode, answer);
+ if (rcode == 0 && spec_data) {
+ // received OK, construct the spec out of it
+ ModuleSpec spec = ModuleSpec(spec_data);
+ if (module != spec.getModuleName()) {
+ // It's a different module!
+ isc_throw(CCSessionError, "Module name mismatch");
+ }
+ return (spec);
+ } else {
+ isc_throw(CCSessionError, "Error getting config for " +
+ module + ": " + answer->str());
+ }
+ }
+}
+
std::string
-ModuleCCSession::addRemoteConfig(const std::string& spec_file_name) {
- ModuleSpec rmod_spec = readModuleSpecification(spec_file_name);
- std::string module_name = rmod_spec.getFullSpec()->get("module_name")->stringValue();
- ConfigData rmod_config = ConfigData(rmod_spec);
- session_.subscribe(module_name);
+ModuleCCSession::addRemoteConfig(const std::string& spec_name,
+ void (*handler)(const std::string& module,
+ ConstElementPtr,
+ const ConfigData&),
+ bool spec_is_filename)
+{
+ // First get the module name, specification and default config
+ const ModuleSpec rmod_spec(fetchRemoteSpec(spec_name, spec_is_filename));
+ const std::string module_name(rmod_spec.getModuleName());
+ ConfigData rmod_config(rmod_spec);
- // Get the current configuration values for that module
- ConstElementPtr cmd = Element::fromJSON("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name + "\"} ] }");
- unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
+ // Get the current configuration values from config manager
+ ConstElementPtr cmd(createCommand("get_config",
+ Element::fromJSON("{\"module_name\": \"" +
+ module_name + "\"}")));
+ const unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
ConstElementPtr env, answer;
session_.group_recvmsg(env, answer, false, seq);
int rcode;
ConstElementPtr new_config = parseAnswer(rcode, answer);
+ ElementPtr local_config;
if (rcode == 0 && new_config) {
- ElementPtr local_config = rmod_config.getLocalConfig();
+ // Merge the received config into existing local config
+ local_config = rmod_config.getLocalConfig();
isc::data::merge(local_config, new_config);
rmod_config.setLocalConfig(local_config);
} else {
@@ -385,6 +609,13 @@ ModuleCCSession::addRemoteConfig(const std::string& spec_file_name) {
// all ok, add it
remote_module_configs_[module_name] = rmod_config;
+ if (handler) {
+ remote_module_handlers_[module_name] = handler;
+ handler(module_name, local_config, rmod_config);
+ }
+
+ // Make sure we get updates in future
+ session_.subscribe(module_name);
return (module_name);
}
@@ -395,6 +626,7 @@ ModuleCCSession::removeRemoteConfig(const std::string& module_name) {
it = remote_module_configs_.find(module_name);
if (it != remote_module_configs_.end()) {
remote_module_configs_.erase(it);
+ remote_module_handlers_.erase(module_name);
session_.unsubscribe(module_name);
}
}
@@ -424,6 +656,11 @@ ModuleCCSession::updateRemoteConfig(const std::string& module_name,
if (it != remote_module_configs_.end()) {
ElementPtr rconf = (*it).second.getLocalConfig();
isc::data::merge(rconf, new_config);
+ std::map<std::string, RemoteHandler>::iterator hit =
+ remote_module_handlers_.find(module_name);
+ if (hit != remote_module_handlers_.end()) {
+ hit->second(module_name, new_config, it->second);
+ }
}
}
diff --git a/src/lib/config/ccsession.h b/src/lib/config/ccsession.h
index d8d1eeb..7dc34ba 100644
--- a/src/lib/config/ccsession.h
+++ b/src/lib/config/ccsession.h
@@ -135,6 +135,15 @@ public:
};
///
+/// \brief This exception is thrown if the constructor fails
+///
+class CCSessionInitError : public isc::Exception {
+public:
+ CCSessionInitError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+///
/// \brief This module keeps a connection to the command channel,
/// holds configuration information, and handles messages from
/// the command channel
@@ -152,11 +161,25 @@ public:
* configuration of the local module needs to be updated.
* This must refer to a valid object of a concrete derived class of
* AbstractSession without establishing the session.
+ *
* Note: the design decision on who is responsible for establishing the
* session is in flux, and may change in near future.
+ *
+ * \exception CCSessionInitError when the initialization fails,
+ * either because the file cannot be read or there is
+ * a communication problem with the config manager.
+ *
* @param command_handler A callback function pointer to be called when
* a control command from a remote agent needs to be performed on the
* local module.
+ * @param start_immediately If true (default), start listening to new commands
+ * and configuration changes asynchronously at the end of the constructor;
+ * if false, it will be delayed until the start() method is explicitly
+ * called. (This is a short term workaround for an initialization trouble.
+ * We'll need to develop a cleaner solution, and then remove this knob)
+ * @param handle_logging If true, the ModuleCCSession will automatically
+ * take care of logging configuration through the virtual Logging config
+ * module.
*/
ModuleCCSession(const std::string& spec_file_name,
isc::cc::AbstractSession& session,
@@ -164,9 +187,21 @@ public:
isc::data::ConstElementPtr new_config) = NULL,
isc::data::ConstElementPtr(*command_handler)(
const std::string& command,
- isc::data::ConstElementPtr args) = NULL
+ isc::data::ConstElementPtr args) = NULL,
+ bool start_immediately = true,
+ bool handle_logging = false
);
+ /// Start receiving new commands and configuration changes asynchronously.
+ ///
+ /// This method must be called only once, and only when the ModuleCCSession
+ /// was constructed with start_immediately being false. Otherwise
+ /// CCSessionError will be thrown.
+ ///
+ /// As noted in the constructor, this method should be considered a short
+ /// term workaround and will be removed in future.
+ void start();
+
/**
* Optional optimization for checkCommand loop; returns true
* if there are unhandled queued messages in the cc session.
@@ -220,24 +255,48 @@ public:
/**
* Gives access to the configuration values of a different module
* Once this function has been called with the name of the specification
- * file of the module you want the configuration of, you can use
+ * file or the module you want the configuration of, you can use
* \c getRemoteConfigValue() to get a specific setting.
- * Changes are automatically updated, but you cannot specify handlers
- * for those changes, must use \c getRemoteConfigValue() to get a value
- * This function will subscribe to the relevant module channel.
+ * Changes are automatically updated, and you can specify handlers
+ * for those changes. This function will subscribe to the relevant module
+ * channel.
+ *
+ * This method must be called before calling the \c start() method on the
+ * ModuleCCSession (it also implies the ModuleCCSession must have been
+ * constructed with start_immediately being false).
+ *
+ * \param spec_name This specifies the module to add. It is either a
+ * filename of the spec file to use or a name of module
+ * (in case it's a module name, the spec data is
+ * downloaded from the configuration manager, therefore
+ * the configuration manager must know it). If
+ * spec_is_filename is true (the default), then a
+ * filename is assumed, otherwise a module name.
+ * \param handler The handler function called whenever there's a change.
+ * Called once initally from this function. May be NULL
+ * if you don't want any handler to be called and you're
+ * fine with requesting the data through
+ * getRemoteConfigValue() each time.
*
- * \param spec_file_name The path to the specification file of
- * the module we want to have configuration
- * values from
+ * The handler should not throw, or it'll fall trough and
+ * the exception will get into strange places, probably
+ * aborting the application.
+ * \param spec_is_filename Says if spec_name is filename or module name.
* \return The name of the module specified in the given specification
* file
*/
- std::string addRemoteConfig(const std::string& spec_file_name);
+ std::string addRemoteConfig(const std::string& spec_name,
+ void (*handler)(const std::string& module_name,
+ isc::data::ConstElementPtr
+ update,
+ const ConfigData& config_data) = NULL,
+ bool spec_is_filename = true);
/**
* Removes the module with the given name from the remote config
* settings. If the module was not added with \c addRemoteConfig(),
- * nothing happens.
+ * nothing happens. If there was a handler for this config, it is
+ * removed as well.
*/
void removeRemoteConfig(const std::string& module_name);
@@ -260,7 +319,8 @@ public:
private:
ModuleSpec readModuleSpecification(const std::string& filename);
void startCheck();
-
+
+ bool started_;
std::string module_name_;
isc::cc::AbstractSession& session_;
ModuleSpec module_specification_;
@@ -282,13 +342,72 @@ private:
const std::string& command,
isc::data::ConstElementPtr args);
+ typedef void (*RemoteHandler)(const std::string&,
+ isc::data::ConstElementPtr,
+ const ConfigData&);
std::map<std::string, ConfigData> remote_module_configs_;
+ std::map<std::string, RemoteHandler> remote_module_handlers_;
+
void updateRemoteConfig(const std::string& module_name,
isc::data::ConstElementPtr new_config);
+
+ ModuleSpec fetchRemoteSpec(const std::string& module, bool is_filename);
};
-}
-}
+/// \brief Default handler for logging config updates
+///
+/// When CCSession is initialized with handle_logging set to true,
+/// this callback will be used to update the logger when a configuration
+/// change comes in.
+///
+/// This function updates the (global) loggers by initializing a
+/// LoggerManager and passing the settings as specified in the given
+/// configuration update.
+///
+/// \param module_name The name of the module
+/// \param new_config The modified configuration values
+/// \param config_data The full config data for the (remote) logging
+/// module.
+void
+default_logconfig_handler(const std::string& module_name,
+ isc::data::ConstElementPtr new_config,
+ const ConfigData& config_data);
+
+
+/// \brief Returns the loggers related to this module
+///
+/// This function does two things;
+/// - it drops the configuration parts for loggers for other modules
+/// - it replaces the '*' in the name of the loggers by the name of
+/// this module, but *only* if the expanded name is not configured
+/// explicitely
+///
+/// Examples: if this is the module b10-resolver,
+/// For the config names ['*', 'b10-auth']
+/// The '*' is replaced with 'b10-resolver', and this logger is used.
+/// 'b10-auth' is ignored (of course, it will not be in the b10-auth
+/// module).
+///
+/// For ['*', 'b10-resolver']
+/// The '*' is ignored, and only 'b10-resolver' is used.
+///
+/// For ['*.reslib', 'b10-resolver']
+/// Or ['b10-resolver.reslib', '*']
+/// Both are used, where the * will be expanded to b10-resolver
+///
+/// \note This is a public function at this time, but mostly for
+/// the purposes of testing. Once we can directly test what loggers
+/// are running, this function may be moved to the unnamed namespace
+///
+/// \param loggers the original 'loggers' config list
+/// \return ListElement containing only loggers relevant for this
+/// module, where * is replaced by the root logger name
+isc::data::ConstElementPtr
+getRelatedLoggers(isc::data::ConstElementPtr loggers);
+
+} // namespace config
+
+} // namespace isc
#endif // __CCSESSION_H
// Local Variables:
diff --git a/src/lib/config/config_data.cc b/src/lib/config/config_data.cc
index 1fa37c1..ebe51cc 100644
--- a/src/lib/config/config_data.cc
+++ b/src/lib/config/config_data.cc
@@ -21,6 +21,63 @@
using namespace isc::data;
+namespace {
+
+// Returns the '_spec' part of a list or map specification (recursively,
+// i.e. if it is a list of lists or maps, will return the spec of the
+// inner-most list or map).
+//
+// \param spec_part the list or map specification (part)
+// \return the value of spec_part's "list_item_spec" or "map_item_spec",
+// or the original spec_part, if it is not a MapElement or does
+// not contain "list_item_spec" or "map_item_spec"
+ConstElementPtr findListOrMapSubSpec(ConstElementPtr spec_part) {
+ while (spec_part->getType() == Element::map &&
+ (spec_part->contains("list_item_spec") ||
+ spec_part->contains("map_item_spec"))) {
+ if (spec_part->contains("list_item_spec")) {
+ spec_part = spec_part->get("list_item_spec");
+ } else {
+ spec_part = spec_part->get("map_item_spec");
+ }
+ }
+ return spec_part;
+}
+
+// Returns a specific Element in a given specification ListElement
+//
+// \exception DataNotFoundError if the given identifier does not
+// point to an existing element. Since we are dealing with the
+// specification here, and not the config data itself, this should
+// not happen, and is a code bug.
+//
+// \param spec_part ListElement to find the element in
+// \param id_part the name of the element to find (must match the value
+// "item_name" in the list item
+// \param id_full the full identifier id_part is a part of, this is
+// used to better report any errors
+ConstElementPtr findItemInSpecList(ConstElementPtr spec_part,
+ const std::string& id_part,
+ const std::string& id_full)
+{
+ bool found = false;
+ BOOST_FOREACH(ConstElementPtr list_el, spec_part->listValue()) {
+ if (list_el->getType() == Element::map &&
+ list_el->contains("item_name") &&
+ list_el->get("item_name")->stringValue() == id_part) {
+ spec_part = list_el;
+ found = true;
+ }
+ }
+ if (!found) {
+ isc_throw(isc::config::DataNotFoundError,
+ id_part + " in " + id_full + " not found");
+ }
+ return (spec_part);
+}
+
+} // anonymous namespace
+
namespace isc {
namespace config {
@@ -36,11 +93,10 @@ namespace config {
// validated and conforms to the specification.
static ConstElementPtr
find_spec_part(ConstElementPtr spec, const std::string& identifier) {
- //std::cout << "[XX] find_spec_part for " << identifier << std::endl;
if (!spec) {
isc_throw(DataNotFoundError, "Empty specification");
}
- //std::cout << "in: " << std::endl << spec << std::endl;
+
ConstElementPtr spec_part = spec;
if (identifier == "") {
isc_throw(DataNotFoundError, "Empty identifier");
@@ -49,59 +105,44 @@ find_spec_part(ConstElementPtr spec, const std::string& identifier) {
size_t sep = id.find('/');
while(sep != std::string::npos) {
std::string part = id.substr(0, sep);
- //std::cout << "[XX] id part: " << part << std::endl;
+
if (spec_part->getType() == Element::list) {
- bool found = false;
- BOOST_FOREACH(ConstElementPtr list_el, spec_part->listValue()) {
- if (list_el->getType() == Element::map &&
- list_el->contains("item_name") &&
- list_el->get("item_name")->stringValue() == part) {
- spec_part = list_el;
- found = true;
- }
- }
- if (!found) {
- isc_throw(DataNotFoundError, identifier);
- }
+ spec_part = findItemInSpecList(spec_part, part, identifier);
+ } else {
+ isc_throw(DataNotFoundError,
+ "Not a list of spec items: " + spec_part->str());
}
id = id.substr(sep + 1);
sep = id.find("/");
+
+ // As long as we are not in the 'final' element as specified
+ // by the identifier, we want to automatically traverse list
+ // and map specifications
+ if (id != "" && id != "/") {
+ spec_part = findListOrMapSubSpec(spec_part);
+ }
}
if (id != "" && id != "/") {
if (spec_part->getType() == Element::list) {
- bool found = false;
- BOOST_FOREACH(ConstElementPtr list_el, spec_part->listValue()) {
- if (list_el->getType() == Element::map &&
- list_el->contains("item_name") &&
- list_el->get("item_name")->stringValue() == id) {
- spec_part = list_el;
- found = true;
- }
- }
- if (!found) {
- isc_throw(DataNotFoundError, identifier);
- }
+ spec_part = findItemInSpecList(spec_part, id, identifier);
} else if (spec_part->getType() == Element::map) {
if (spec_part->contains("map_item_spec")) {
- bool found = false;
- BOOST_FOREACH(ConstElementPtr list_el,
- spec_part->get("map_item_spec")->listValue()) {
- if (list_el->getType() == Element::map &&
- list_el->contains("item_name") &&
- list_el->get("item_name")->stringValue() == id) {
- spec_part = list_el;
- found = true;
- }
- }
- if (!found) {
- isc_throw(DataNotFoundError, identifier);
- }
+ spec_part = findItemInSpecList(
+ spec_part->get("map_item_spec"),
+ id, identifier);
} else {
- isc_throw(DataNotFoundError, identifier);
+ // Either we already have the element we are looking
+ // for, or we are trying to reach something that does
+ // not exist (i.e. the code does not match the spec)
+ if (!spec_part->contains("item_name") ||
+ spec_part->get("item_name")->stringValue() != id) {
+ isc_throw(DataNotFoundError, "Element above " + id +
+ " in " + identifier +
+ " is not a map: " + spec_part->str());
+ }
}
}
}
- //std::cout << "[XX] found spec part: " << std::endl << spec_part << std::endl;
return (spec_part);
}
@@ -164,6 +205,17 @@ ConfigData::getValue(bool& is_default, const std::string& identifier) const {
return (value);
}
+ConstElementPtr
+ConfigData::getDefaultValue(const std::string& identifier) const {
+ ConstElementPtr spec_part =
+ find_spec_part(_module_spec.getConfigSpec(), identifier);
+ if (spec_part->contains("item_default")) {
+ return spec_part->get("item_default");
+ } else {
+ isc_throw(DataNotFoundError, "No default for " + identifier);
+ }
+}
+
/// Returns an ElementPtr pointing to a ListElement containing
/// StringElements with the names of the options at the given
/// identifier. If recurse is true, maps will be expanded as well
diff --git a/src/lib/config/config_data.h b/src/lib/config/config_data.h
index 29a5b5f..197d319 100644
--- a/src/lib/config/config_data.h
+++ b/src/lib/config/config_data.h
@@ -57,6 +57,16 @@ public:
/// value that is to be returned
isc::data::ConstElementPtr getValue(const std::string& identifier) const;
+ /// Returns the default value for the given identifier.
+ ///
+ /// \exception DataNotFoundError if the given identifier does not
+ /// exist, or if the given value has no specified default
+ ///
+ /// \param identifier The identifier pointing to the configuration
+ /// value for which the default is to be returned
+ /// \return ElementPtr containing the default value
+ isc::data::ConstElementPtr getDefaultValue(const std::string& identifier) const;
+
/// Returns the value currently set for the given identifier
/// If no value is set, the default value (as specified by the
/// .spec file) is returned. If there is no value and no default,
diff --git a/src/lib/config/config_log.cc b/src/lib/config/config_log.cc
new file mode 100644
index 0000000..672b9f1
--- /dev/null
+++ b/src/lib/config/config_log.cc
@@ -0,0 +1,26 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// Defines the logger used by the config lib
+
+#include "config/config_log.h"
+
+namespace isc {
+namespace config {
+
+isc::log::Logger config_logger("config");
+
+} // namespace nsas
+} // namespace isc
+
diff --git a/src/lib/config/config_log.h b/src/lib/config/config_log.h
new file mode 100644
index 0000000..0063855
--- /dev/null
+++ b/src/lib/config/config_log.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __CONFIG_LOG__H
+#define __CONFIG_LOG__H
+
+#include <log/macros.h>
+#include "config_messages.h"
+
+namespace isc {
+namespace config {
+
+/// \brief Config Logging
+///
+/// Defines logger object for config log messages
+
+/// \brief Config Logger
+///
+/// Define the logger used to log messages. We could define it in multiple
+/// modules, but defining in a single module and linking to it saves time and
+/// space.
+extern isc::log::Logger config_logger; // isc::config::config_logger is the CONFIG logger
+
+} // namespace config
+} // namespace isc
+
+#endif // __CONFIG_LOG__H
diff --git a/src/lib/config/config_messages.mes b/src/lib/config/config_messages.mes
new file mode 100644
index 0000000..660ab9a
--- /dev/null
+++ b/src/lib/config/config_messages.mes
@@ -0,0 +1,59 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+$NAMESPACE isc::config
+
+% CONFIG_CCSESSION_MSG error in CC session message: %1
+There was a problem with an incoming message on the command and control
+channel. The message does not appear to be a valid command, and is
+missing a required element or contains an unknown data format. This
+most likely means that another BIND10 module is sending a bad message.
+The message itself is ignored by this module.
+
+% CONFIG_CCSESSION_MSG_INTERNAL error handling CC session message: %1
+There was an internal problem handling an incoming message on the command
+and control channel. An unexpected exception was thrown, details of
+which are appended to the message. The module will continue to run,
+but will not send back an answer.
+
+The most likely cause of this error is a programming error. Please raise
+a bug report.
+
+% CONFIG_GET_FAIL error getting configuration from cfgmgr: %1
+The configuration manager returned an error when this module requested
+the configuration. The full error message answer from the configuration
+manager is appended to the log error. The most likely cause is that
+the module is of a different (command specification) version than the
+running configuration manager.
+
+% CONFIG_JSON_PARSE JSON parse error in %1: %2
+There was an error parsing the JSON file. The given file does not appear
+to be in valid JSON format. Please verify that the filename is correct
+and that the contents are valid JSON.
+
+% CONFIG_MOD_SPEC_FORMAT module specification error in %1: %2
+The given file does not appear to be a valid specification file: details
+are included in the message. Please verify that the filename is correct
+and that its contents are a valid BIND10 module specification.
+
+% CONFIG_MOD_SPEC_REJECT module specification rejected by cfgmgr: %1
+The specification file for this module was rejected by the configuration
+manager. The full error message answer from the configuration manager is
+appended to the log error. The most likely cause is that the module is of
+a different (specification file) version than the running configuration
+manager.
+
+% CONFIG_OPEN_FAIL error opening %1: %2
+There was an error opening the given file. The reason for the failure
+is included in the message.
diff --git a/src/lib/config/module_spec.cc b/src/lib/config/module_spec.cc
index fd07dde..1621fe3 100644
--- a/src/lib/config/module_spec.cc
+++ b/src/lib/config/module_spec.cc
@@ -372,15 +372,18 @@ ModuleSpec::validateSpecList(ConstElementPtr spec, ConstElementPtr data,
BOOST_FOREACH(maptype m, data->mapValue()) {
bool found = false;
- BOOST_FOREACH(ConstElementPtr cur_spec_el, spec->listValue()) {
- if (cur_spec_el->get("item_name")->stringValue().compare(m.first) == 0) {
- found = true;
+ // Ignore 'version' as a config element
+ if (m.first.compare("version") != 0) {
+ BOOST_FOREACH(ConstElementPtr cur_spec_el, spec->listValue()) {
+ if (cur_spec_el->get("item_name")->stringValue().compare(m.first) == 0) {
+ found = true;
+ }
}
- }
- if (!found) {
- validated = false;
- if (errors) {
- errors->add(Element::create("Unknown item " + m.first));
+ if (!found) {
+ validated = false;
+ if (errors) {
+ errors->add(Element::create("Unknown item " + m.first));
+ }
}
}
}
diff --git a/src/lib/config/tests/Makefile.am b/src/lib/config/tests/Makefile.am
index 0cebdbf..7153e09 100644
--- a/src/lib/config/tests/Makefile.am
+++ b/src/lib/config/tests/Makefile.am
@@ -22,10 +22,12 @@ run_unittests_SOURCES = ccsession_unittests.cc module_spec_unittests.cc config_d
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += libfake_session.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
diff --git a/src/lib/config/tests/ccsession_unittests.cc b/src/lib/config/tests/ccsession_unittests.cc
index 43663bd..e1a4f9d 100644
--- a/src/lib/config/tests/ccsession_unittests.cc
+++ b/src/lib/config/tests/ccsession_unittests.cc
@@ -24,6 +24,8 @@
#include <config/tests/data_def_unittests_config.h>
+#include <log/logger_name.h>
+
using namespace isc::data;
using namespace isc::config;
using namespace isc::cc;
@@ -160,6 +162,10 @@ TEST_F(CCSessionTest, session1) {
EXPECT_EQ("ConfigManager", group);
EXPECT_EQ("*", to);
EXPECT_EQ(0, session.getMsgQueue()->size());
+
+ // without explicit argument, the session should not automatically
+ // subscribe to logging config
+ EXPECT_FALSE(session.haveSubscription("Logging", "*"));
}
TEST_F(CCSessionTest, session2) {
@@ -264,10 +270,7 @@ TEST_F(CCSessionTest, checkCommand) {
session.addMessage(el("{ \"command\": \"bad_command\" }"), "Spec29", "*");
result = mccs.checkCommand();
- EXPECT_EQ(1, session.getMsgQueue()->size());
- msg = session.getFirstMessage(group, to);
- EXPECT_EQ("{ \"result\": [ 1, \"Command part in command message missing, empty, or not a list\" ] }", msg->str());
- EXPECT_EQ(0, result);
+ EXPECT_EQ(0, session.getMsgQueue()->size());
session.addMessage(el("{ \"command\": [ \"bad_command\" ] }"),
"Spec29", "*");
@@ -349,11 +352,25 @@ TEST_F(CCSessionTest, checkCommand2) {
EXPECT_EQ(2, mccs.getValue("item1")->intValue());
}
+std::string remote_module_name;
+int remote_item1(0);
+ConstElementPtr remote_config;
+ModuleCCSession *remote_mccs(NULL);
+
+void remoteHandler(const std::string& module_name,
+ ConstElementPtr config,
+ const ConfigData&) {
+ remote_module_name = module_name;
+ remote_item1 = remote_mccs->getRemoteConfigValue("Spec2", "item1")->
+ intValue();
+ remote_config = config;
+}
+
TEST_F(CCSessionTest, remoteConfig) {
std::string module_name;
int item1;
- ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL);
+ ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL, false);
EXPECT_TRUE(session.haveSubscription("Spec1", "*"));
// first simply connect, with no config values, and see we get
@@ -395,6 +412,112 @@ TEST_F(CCSessionTest, remoteConfig) {
session.getMessages()->add(createAnswer());
EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")), CCSessionError);
+
+ {
+ SCOPED_TRACE("With module name");
+ // Try adding it with downloading the spec from config manager
+ ModuleSpec spec(moduleSpecFromFile(ccspecfile("spec2.spec")));
+ session.getMessages()->add(createAnswer(0, spec.getFullSpec()));
+ session.getMessages()->add(createAnswer(0, el("{}")));
+
+ EXPECT_NO_THROW(module_name = mccs.addRemoteConfig("Spec2", NULL,
+ false));
+
+ const size_t qsize(session.getMsgQueue()->size());
+ EXPECT_TRUE(session.getMsgQueue()->get(qsize - 2)->equals(*el(
+ "[ \"ConfigManager\", \"*\", { \"command\": ["
+ "\"get_module_spec\", { \"module_name\": \"Spec2\" } ] } ]")));
+ EXPECT_TRUE(session.getMsgQueue()->get(qsize - 1)->equals(*el(
+ "[ \"ConfigManager\", \"*\", { \"command\": [ \"get_config\","
+ "{ \"module_name\": \"Spec2\" } ] } ]")));
+ EXPECT_EQ("Spec2", module_name);
+ // Since we returned an empty local config above, the default value
+ // for "item1", which is 1, should be used.
+ EXPECT_NO_THROW(item1 =
+ mccs.getRemoteConfigValue(module_name,
+ "item1")->intValue());
+ EXPECT_EQ(1, item1);
+
+ mccs.removeRemoteConfig(module_name);
+ }
+
+ {
+ SCOPED_TRACE("With bad module name");
+ // It is almost the same as above, but we supply wrong module name.
+ // It should fail.
+ // Try adding it with downloading the spec from config manager
+ ModuleSpec spec(moduleSpecFromFile(ccspecfile("spec2.spec")));
+ session.getMessages()->add(createAnswer(0, spec.getFullSpec()));
+
+ EXPECT_THROW(module_name = mccs.addRemoteConfig("Spec1", NULL, false),
+ CCSessionError);
+ }
+
+ {
+ // Try adding it with a handler.
+ // Pass non-default value to see the handler is called after
+ // downloading the configuration, not too soon.
+ SCOPED_TRACE("With handler");
+ session.getMessages()->add(createAnswer(0, el("{ \"item1\": 2 }")));
+ remote_mccs = &mccs;
+ module_name = mccs.addRemoteConfig(ccspecfile("spec2.spec"),
+ remoteHandler);
+ {
+ SCOPED_TRACE("Before update");
+ EXPECT_EQ("Spec2", module_name);
+ EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
+ // Now check the parameters the remote handler stored
+ // This also checks it was called
+ EXPECT_EQ("Spec2", remote_module_name);
+ remote_module_name = "";
+ EXPECT_EQ(2, remote_item1);
+ remote_item1 = 0;
+ if (remote_config) {
+ EXPECT_EQ(2, remote_config->get("item1")->intValue());
+ } else {
+ ADD_FAILURE() << "Remote config not set";
+ }
+ remote_config.reset();
+ // Make sure normal way still works
+ item1 = mccs.getRemoteConfigValue(module_name,
+ "item1")->intValue();
+ EXPECT_EQ(2, item1);
+ }
+
+ {
+ SCOPED_TRACE("After update");
+ session.addMessage(el("{ \"command\": [ \"config_update\", "
+ "{ \"item1\": 3 } ] }"), module_name, "*");
+ mccs.checkCommand();
+ EXPECT_EQ("Spec2", remote_module_name);
+ remote_module_name = "";
+ EXPECT_EQ(3, remote_item1);
+ remote_item1 = 0;
+ if (remote_config) {
+ EXPECT_EQ(3, remote_config->get("item1")->intValue());
+ } else {
+ ADD_FAILURE() << "Remote config not set";
+ }
+ remote_config.reset();
+ // Make sure normal way still works
+ item1 = mccs.getRemoteConfigValue(module_name,
+ "item1")->intValue();
+ EXPECT_EQ(3, item1);
+ }
+
+ remote_mccs = NULL;
+ mccs.removeRemoteConfig(module_name);
+
+ {
+ SCOPED_TRACE("When removed");
+ // Make sure nothing is called any more
+ session.addMessage(el("{ \"command\": [ \"config_update\", "
+ "{ \"item1\": 4 } ] }"), module_name, "*");
+ EXPECT_EQ("", remote_module_name);
+ EXPECT_EQ(0, remote_item1);
+ EXPECT_FALSE(remote_config);
+ }
+ }
}
TEST_F(CCSessionTest, ignoreRemoteConfigCommands) {
@@ -402,7 +525,8 @@ TEST_F(CCSessionTest, ignoreRemoteConfigCommands) {
session.getMessages()->add(createAnswer(0, el("{ }")));
EXPECT_FALSE(session.haveSubscription("Spec29", "*"));
- ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler, my_command_handler);
+ ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler,
+ my_command_handler, false);
EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
EXPECT_EQ(2, session.getMsgQueue()->size());
@@ -432,4 +556,142 @@ TEST_F(CCSessionTest, ignoreRemoteConfigCommands) {
EXPECT_EQ(0, session.getMsgQueue()->size());
}
+TEST_F(CCSessionTest, initializationFail) {
+ // bad specification
+ EXPECT_THROW(ModuleCCSession(ccspecfile("spec8.spec"), session,
+ NULL, NULL), CCSessionInitError);
+
+ // file that does not exist
+ EXPECT_THROW(ModuleCCSession(ccspecfile("does_not_exist_spec"),
+ session, NULL, NULL),
+ CCSessionInitError);
+
+
+ session.getMessages()->add(createAnswer(1, el("\"just an error\"")));
+
+ EXPECT_FALSE(session.haveSubscription("Spec29", "*"));
+ EXPECT_THROW(ModuleCCSession(ccspecfile("spec29.spec"), session,
+ my_config_handler, my_command_handler),
+ CCSessionInitError);
+ EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
+}
+
+// Test it throws when we try to start it twice (once from the constructor)
+TEST_F(CCSessionTest, doubleStartImplicit) {
+ ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL);
+ EXPECT_THROW(mccs.start(), CCSessionError);
+}
+
+// The same, but both starts are explicit
+TEST_F(CCSessionTest, doubleStartExplicit) {
+ ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL,
+ false);
+ mccs.start();
+ EXPECT_THROW(mccs.start(), CCSessionError);
+}
+
+// Test we can request synchronous receive before we start the session,
+// and check there's the mechanism if we do it after
+TEST_F(CCSessionTest, delayedStart) {
+ ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL, false);
+ session.getMessages()->add(createAnswer());
+ ConstElementPtr env, answer;
+ EXPECT_NO_THROW(session.group_recvmsg(env, answer, false, 3));
+ mccs.start();
+ session.getMessages()->add(createAnswer());
+ EXPECT_THROW(session.group_recvmsg(env, answer, false, 3),
+ FakeSession::DoubleRead);
+}
+
+TEST_F(CCSessionTest, loggingStart) {
+ // provide the logging module spec
+ ConstElementPtr log_spec = moduleSpecFromFile(LOG_SPEC_FILE).getFullSpec();
+ session.getMessages()->add(createAnswer(0, log_spec));
+ // just give an empty config
+ session.getMessages()->add(createAnswer(0, el("{}")));
+ ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL,
+ true, true);
+ EXPECT_TRUE(session.haveSubscription("Logging", "*"));
+}
+
+TEST_F(CCSessionTest, loggingStartBadSpec) {
+ // provide the logging module spec
+ session.getMessages()->add(createAnswer(0, el("{}")));
+ // just give an empty config
+ session.getMessages()->add(createAnswer(0, el("{}")));
+ EXPECT_THROW(new ModuleCCSession(ccspecfile("spec2.spec"), session,
+ NULL, NULL, true, true), ModuleSpecError);
+ EXPECT_FALSE(session.haveSubscription("Logging", "*"));
+}
+
+// Similar to the above, but more implicitly by calling addRemoteConfig().
+// We should construct ModuleCCSession with start_immediately being false
+// if we need to call addRemoteConfig().
+// The correct cases are covered in remoteConfig test.
+TEST_F(CCSessionTest, doubleStartWithAddRemoteConfig) {
+ ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL);
+ session.getMessages()->add(createAnswer(0, el("{}")));
+ EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")),
+ FakeSession::DoubleRead);
+}
+
+namespace {
+void doRelatedLoggersTest(const char* input, const char* expected) {
+ ConstElementPtr all_conf = isc::data::Element::fromJSON(input);
+ ConstElementPtr expected_conf = isc::data::Element::fromJSON(expected);
+ EXPECT_EQ(*expected_conf, *isc::config::getRelatedLoggers(all_conf));
+}
+} // end anonymous namespace
+
+TEST(LogConfigTest, relatedLoggersTest) {
+ // make sure logger configs for 'other' programs are ignored,
+ // and that * is substituted correctly
+ // The default root logger name is "bind10"
+ doRelatedLoggersTest("[{ \"name\": \"other_module\" }]",
+ "[]");
+ doRelatedLoggersTest("[{ \"name\": \"other_module.somelib\" }]",
+ "[]");
+ doRelatedLoggersTest("[{ \"name\": \"bind10_other\" }]",
+ "[]");
+ doRelatedLoggersTest("[{ \"name\": \"bind10_other.somelib\" }]",
+ "[]");
+ doRelatedLoggersTest("[ { \"name\": \"other_module\" },"
+ " { \"name\": \"bind10\" }]",
+ "[ { \"name\": \"bind10\" } ]");
+ doRelatedLoggersTest("[ { \"name\": \"bind10\" }]",
+ "[ { \"name\": \"bind10\" } ]");
+ doRelatedLoggersTest("[ { \"name\": \"bind10.somelib\" }]",
+ "[ { \"name\": \"bind10.somelib\" } ]");
+ doRelatedLoggersTest("[ { \"name\": \"other_module.somelib\" },"
+ " { \"name\": \"bind10.somelib\" }]",
+ "[ { \"name\": \"bind10.somelib\" } ]");
+ doRelatedLoggersTest("[ { \"name\": \"other_module.somelib\" },"
+ " { \"name\": \"bind10\" },"
+ " { \"name\": \"bind10.somelib\" }]",
+ "[ { \"name\": \"bind10\" },"
+ " { \"name\": \"bind10.somelib\" } ]");
+ doRelatedLoggersTest("[ { \"name\": \"*\" }]",
+ "[ { \"name\": \"bind10\" } ]");
+ doRelatedLoggersTest("[ { \"name\": \"*.somelib\" }]",
+ "[ { \"name\": \"bind10.somelib\" } ]");
+ doRelatedLoggersTest("[ { \"name\": \"*\", \"severity\": \"DEBUG\" },"
+ " { \"name\": \"bind10\", \"severity\": \"WARN\"}]",
+ "[ { \"name\": \"bind10\", \"severity\": \"WARN\"} ]");
+ doRelatedLoggersTest("[ { \"name\": \"*\", \"severity\": \"DEBUG\" },"
+ " { \"name\": \"some_module\", \"severity\": \"WARN\"}]",
+ "[ { \"name\": \"bind10\", \"severity\": \"DEBUG\"} ]");
+
+ // make sure 'bad' things like '*foo.x' or '*lib' are ignored
+ // (cfgmgr should have already caught it in the logconfig plugin
+ // check, and is responsible for reporting the error)
+ doRelatedLoggersTest("[ { \"name\": \"*foo\" }]",
+ "[ ]");
+ doRelatedLoggersTest("[ { \"name\": \"*foo.bar\" }]",
+ "[ ]");
+ doRelatedLoggersTest("[ { \"name\": \"*foo\" },"
+ " { \"name\": \"*foo.lib\" },"
+ " { \"name\": \"bind10\" } ]",
+ "[ { \"name\": \"bind10\" } ]");
+}
+
}
diff --git a/src/lib/config/tests/config_data_unittests.cc b/src/lib/config/tests/config_data_unittests.cc
index 974812d..26a3fc6 100644
--- a/src/lib/config/tests/config_data_unittests.cc
+++ b/src/lib/config/tests/config_data_unittests.cc
@@ -64,21 +64,35 @@ TEST(ConfigData, getValue) {
EXPECT_EQ("{ }", cd.getValue(is_default, "value6/")->str());
EXPECT_TRUE(is_default);
EXPECT_EQ("[ ]", cd.getValue("value8")->str());
+ EXPECT_EQ("[ ]", cd.getDefaultValue("value8")->str());
+ EXPECT_EQ("empty", cd.getValue("value8/a")->stringValue());
EXPECT_THROW(cd.getValue("")->str(), DataNotFoundError);
EXPECT_THROW(cd.getValue("/")->str(), DataNotFoundError);
EXPECT_THROW(cd.getValue("no_such_item")->str(), DataNotFoundError);
EXPECT_THROW(cd.getValue("value6/a")->str(), DataNotFoundError);
EXPECT_THROW(cd.getValue("value6/no_such_item")->str(), DataNotFoundError);
- EXPECT_THROW(cd.getValue("value8/a")->str(), DataNotFoundError);
- EXPECT_THROW(cd.getValue("value8/a")->str(), DataNotFoundError);
- EXPECT_THROW(cd.getValue("value8/a")->str(), DataNotFoundError);
+ EXPECT_THROW(cd.getValue("value8/b")->str(), DataNotFoundError);
ModuleSpec spec1 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec1.spec");
ConfigData cd1 = ConfigData(spec1);
EXPECT_THROW(cd1.getValue("anything")->str(), DataNotFoundError);
}
+TEST(ConfigData, getDefaultValue) {
+ ModuleSpec spec31 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec31.spec");
+ ConfigData cd = ConfigData(spec31);
+ EXPECT_EQ("[ ]", cd.getDefaultValue("first_list_items")->str());
+ EXPECT_EQ("\"foo\"", cd.getDefaultValue("first_list_items/foo")->str());
+ EXPECT_EQ("{ }", cd.getDefaultValue("first_list_items/second_list_items/map_element")->str());
+ EXPECT_EQ("[ ]", cd.getDefaultValue("first_list_items/second_list_items/map_element/list1")->str());
+ EXPECT_EQ("1", cd.getDefaultValue("first_list_items/second_list_items/map_element/list1/number")->str());
+
+ EXPECT_THROW(cd.getDefaultValue("doesnotexist")->str(), DataNotFoundError);
+ EXPECT_THROW(cd.getDefaultValue("first_list_items/second_list_items/map_element/list1/doesnotexist")->str(), DataNotFoundError);
+}
+
+
TEST(ConfigData, setLocalConfig) {
ModuleSpec spec2 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec2.spec");
ConfigData cd = ConfigData(spec2);
diff --git a/src/lib/config/tests/data_def_unittests_config.h.in b/src/lib/config/tests/data_def_unittests_config.h.in
index 80e9cfa..f9662f0 100644
--- a/src/lib/config/tests/data_def_unittests_config.h.in
+++ b/src/lib/config/tests/data_def_unittests_config.h.in
@@ -13,3 +13,4 @@
// PERFORMANCE OF THIS SOFTWARE.
#define TEST_DATA_PATH "@abs_srcdir@/testdata"
+#define LOG_SPEC_FILE "@abs_top_srcdir@/src/bin/cfgmgr/plugins/logging.spec"
diff --git a/src/lib/config/tests/fake_session.cc b/src/lib/config/tests/fake_session.cc
index 29744c7..2b216e7 100644
--- a/src/lib/config/tests/fake_session.cc
+++ b/src/lib/config/tests/fake_session.cc
@@ -52,16 +52,14 @@ listContains(ConstElementPtr list, ConstElementPtr el) {
void
listRemove(ElementPtr list, ConstElementPtr el) {
- int i = -1;
+ int i = 0;
BOOST_FOREACH(ConstElementPtr s_el, list->listValue()) {
if (*el == *s_el) {
- i = 0;
+ list->remove(i);
+ return;
}
i++;
}
- if (i >= 0) {
- list->remove(i);
- }
}
// endwant
@@ -73,7 +71,8 @@ FakeSession::FakeSession(isc::data::ElementPtr initial_messages,
isc::data::ElementPtr msg_queue) :
messages_(initial_messages),
subscriptions_(subscriptions),
- msg_queue_(msg_queue)
+ msg_queue_(msg_queue),
+ started_(false)
{
}
@@ -86,6 +85,7 @@ FakeSession::disconnect() {
void
FakeSession::startRead(boost::function<void()>) {
+ started_ = true;
}
void
@@ -93,7 +93,13 @@ FakeSession::establish(const char*) {
}
bool
-FakeSession::recvmsg(ConstElementPtr& msg, bool, int) {
+FakeSession::recvmsg(ConstElementPtr& msg, bool nonblock, int) {
+ if (started_ && !nonblock) {
+ // This would schedule another read for length, leading to
+ // corputed data
+ isc_throw(DoubleRead, "Second read scheduled from recvmsg");
+ }
+
//cout << "[XX] client asks for message " << endl;
if (messages_ &&
messages_->getType() == Element::list &&
@@ -107,7 +113,15 @@ FakeSession::recvmsg(ConstElementPtr& msg, bool, int) {
}
bool
-FakeSession::recvmsg(ConstElementPtr& env, ConstElementPtr& msg, bool, int) {
+FakeSession::recvmsg(ConstElementPtr& env, ConstElementPtr& msg, bool nonblock,
+ int)
+{
+ if (started_ && !nonblock) {
+ // This would schedule another read for length, leading to
+ // corputed data
+ isc_throw(DoubleRead, "Second read scheduled from recvmsg");
+ }
+
//cout << "[XX] client asks for message and env" << endl;
env = ElementPtr();
if (messages_ &&
diff --git a/src/lib/config/tests/fake_session.h b/src/lib/config/tests/fake_session.h
index ac8e291..85e47d5 100644
--- a/src/lib/config/tests/fake_session.h
+++ b/src/lib/config/tests/fake_session.h
@@ -42,6 +42,14 @@ public:
isc::data::ElementPtr msg_queue);
virtual ~FakeSession();
+ // This is thrown if two reads for length at once are scheduled at once.
+ // Such thing does bad things currently (see discussion in ticket #931).
+ class DoubleRead : public Exception {
+ public:
+ DoubleRead(const char* file, size_t line, const char* what) :
+ Exception(file, line, what) {}
+ };
+
virtual void startRead(boost::function<void()> read_callback);
virtual void establish(const char* socket_file = NULL);
@@ -89,6 +97,7 @@ private:
const isc::data::ElementPtr messages_;
isc::data::ElementPtr subscriptions_;
isc::data::ElementPtr msg_queue_;
+ bool started_;
};
} // namespace cc
} // namespace isc
diff --git a/src/lib/config/tests/module_spec_unittests.cc b/src/lib/config/tests/module_spec_unittests.cc
index 59f5459..1b43350 100644
--- a/src/lib/config/tests/module_spec_unittests.cc
+++ b/src/lib/config/tests/module_spec_unittests.cc
@@ -162,6 +162,10 @@ TEST(ModuleSpec, DataValidation) {
EXPECT_FALSE(dataTest(dd, "data22_8.data"));
EXPECT_FALSE(dataTest(dd, "data22_9.data"));
+ // Test if "version" is allowed in config data
+ // (same data as 22_7, but added "version")
+ EXPECT_TRUE(dataTest(dd, "data22_10.data"));
+
ElementPtr errors = Element::createList();
EXPECT_FALSE(dataTestWithErrors(dd, "data22_8.data", errors));
EXPECT_EQ("[ \"Type mismatch\" ]", errors->str());
diff --git a/src/lib/config/tests/run_unittests.cc b/src/lib/config/tests/run_unittests.cc
index 0908071..19d2be1 100644
--- a/src/lib/config/tests/run_unittests.cc
+++ b/src/lib/config/tests/run_unittests.cc
@@ -13,9 +13,12 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+#include <log/logger_support.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+ isc::log::initLogger();
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/config/tests/testdata/Makefile.am b/src/lib/config/tests/testdata/Makefile.am
index a16b106..57d1ed3 100644
--- a/src/lib/config/tests/testdata/Makefile.am
+++ b/src/lib/config/tests/testdata/Makefile.am
@@ -21,6 +21,7 @@ EXTRA_DIST += data22_6.data
EXTRA_DIST += data22_7.data
EXTRA_DIST += data22_8.data
EXTRA_DIST += data22_9.data
+EXTRA_DIST += data22_10.data
EXTRA_DIST += spec1.spec
EXTRA_DIST += spec2.spec
EXTRA_DIST += spec3.spec
@@ -50,3 +51,5 @@ EXTRA_DIST += spec26.spec
EXTRA_DIST += spec27.spec
EXTRA_DIST += spec28.spec
EXTRA_DIST += spec29.spec
+EXTRA_DIST += spec30.spec
+EXTRA_DIST += spec31.spec
diff --git a/src/lib/config/tests/testdata/data22_10.data b/src/lib/config/tests/testdata/data22_10.data
new file mode 100644
index 0000000..fed4001
--- /dev/null
+++ b/src/lib/config/tests/testdata/data22_10.data
@@ -0,0 +1,11 @@
+{
+ "version": 123,
+ "value1": 1,
+ "value2": 2.3,
+ "value3": true,
+ "value4": "foo",
+ "value5": [ 1, 2, 3 ],
+ "value6": { "v61": "bar", "v62": true },
+ "value8": [ { "a": "d" }, { "a": "e" } ],
+ "value9": { "v91": "hi", "v92": { "v92a": "Hi", "v92b": 3 } }
+}
diff --git a/src/lib/config/tests/testdata/spec30.spec b/src/lib/config/tests/testdata/spec30.spec
new file mode 100644
index 0000000..a9e00ad
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec30.spec
@@ -0,0 +1,45 @@
+{
+ "module_spec": {
+ "module_name": "lists",
+ "module_description": "Logging options",
+ "config_data": [
+ {
+ "item_name": "first_list_items",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "first_list_item",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "foo",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "foo"
+ },
+ { "item_name": "second_list_items",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "second_list_item",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "final_element",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "hello"
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/src/lib/config/tests/testdata/spec31.spec b/src/lib/config/tests/testdata/spec31.spec
new file mode 100644
index 0000000..9eebfd1
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec31.spec
@@ -0,0 +1,63 @@
+{
+ "module_spec": {
+ "module_name": "lists",
+ "module_description": "Logging options",
+ "config_data": [
+ {
+ "item_name": "first_list_items",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "first_list_item",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "foo",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "foo"
+ },
+ { "item_name": "second_list_items",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec": {
+ "item_name": "second_list_item",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "map_element",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "list1",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec":
+ { "item_name": "list2",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec":
+ { "item_name": "number",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 1
+ }
+ }
+ }]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/src/lib/cryptolink/Makefile.am b/src/lib/cryptolink/Makefile.am
new file mode 100644
index 0000000..93f3443
--- /dev/null
+++ b/src/lib/cryptolink/Makefile.am
@@ -0,0 +1,14 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(BOTAN_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+CLEANFILES = *.gcno *.gcda
+
+lib_LTLIBRARIES = libcryptolink.la
+
+libcryptolink_la_SOURCES = cryptolink.h cryptolink.cc
+libcryptolink_la_SOURCES += crypto_hmac.h crypto_hmac.cc
+
+libcryptolink_la_LIBADD = ${BOTAN_LDFLAGS} ${BOTAN_RPATH}
diff --git a/src/lib/cryptolink/crypto_hmac.cc b/src/lib/cryptolink/crypto_hmac.cc
new file mode 100644
index 0000000..277b036
--- /dev/null
+++ b/src/lib/cryptolink/crypto_hmac.cc
@@ -0,0 +1,281 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cryptolink.h>
+#include <cryptolink/crypto_hmac.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <botan/version.h>
+#include <botan/botan.h>
+#include <botan/hmac.h>
+#include <botan/hash.h>
+#include <botan/types.h>
+
+namespace {
+const char*
+getBotanHashAlgorithmName(isc::cryptolink::HashAlgorithm algorithm) {
+ switch (algorithm) {
+ case isc::cryptolink::MD5:
+ return ("MD5");
+ break;
+ case isc::cryptolink::SHA1:
+ return ("SHA-1");
+ break;
+ case isc::cryptolink::SHA256:
+ return ("SHA-256");
+ break;
+ case isc::cryptolink::SHA224:
+ return ("SHA-224");
+ break;
+ case isc::cryptolink::SHA384:
+ return ("SHA-384");
+ break;
+ case isc::cryptolink::SHA512:
+ return ("SHA-512");
+ break;
+ case isc::cryptolink::UNKNOWN_HASH:
+ return ("Unknown");
+ break;
+ }
+ // compiler should have prevented us to reach this, since we have
+ // no default. But we need a return value anyway
+ return ("Unknown");
+}
+
+} // local namespace
+
+
+namespace isc {
+namespace cryptolink {
+
+class HMACImpl {
+public:
+ explicit HMACImpl(const void* secret, size_t secret_len,
+ const HashAlgorithm hash_algorithm) {
+ Botan::HashFunction* hash;
+ try {
+ hash = Botan::get_hash(
+ getBotanHashAlgorithmName(hash_algorithm));
+ } catch (const Botan::Algorithm_Not_Found&) {
+ isc_throw(isc::cryptolink::UnsupportedAlgorithm,
+ "Unknown hash algorithm: " <<
+ static_cast<int>(hash_algorithm));
+ } catch (const Botan::Exception& exc) {
+ isc_throw(isc::cryptolink::LibraryError, exc.what());
+ }
+
+ hmac_.reset(new Botan::HMAC(hash));
+
+ // If the key length is larger than the block size, we hash the
+ // key itself first.
+ try {
+ // use a temp var so we don't have blocks spanning
+ // preprocessor directives
+#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,0)
+ size_t block_length = hash->hash_block_size();
+#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,8,0)
+ size_t block_length = hash->HASH_BLOCK_SIZE;
+#else
+#error "Unsupported Botan version (need 1.8 or higher)"
+ // added to suppress irrelevant compiler errors
+ size_t block_length = 0;
+#endif
+ if (secret_len > block_length) {
+ Botan::SecureVector<Botan::byte> hashed_key =
+ hash->process(static_cast<const Botan::byte*>(secret),
+ secret_len);
+ hmac_->set_key(hashed_key.begin(), hashed_key.size());
+ } else {
+ // Botan 1.8 considers len 0 a bad key. 1.9 does not,
+ // but we won't accept it anyway, and fail early
+ if (secret_len == 0) {
+ isc_throw(BadKey, "Bad HMAC secret length: 0");
+ }
+ hmac_->set_key(static_cast<const Botan::byte*>(secret),
+ secret_len);
+ }
+ } catch (const Botan::Invalid_Key_Length& ikl) {
+ isc_throw(BadKey, ikl.what());
+ } catch (const Botan::Exception& exc) {
+ isc_throw(isc::cryptolink::LibraryError, exc.what());
+ }
+ }
+
+ ~HMACImpl() { }
+
+ size_t getOutputLength() const {
+#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,0)
+ return (hmac_->output_length());
+#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,8,0)
+ return (hmac_->OUTPUT_LENGTH);
+#else
+#error "Unsupported Botan version (need 1.8 or higher)"
+ // added to suppress irrelevant compiler errors
+ return 0;
+#endif
+ }
+
+ void update(const void* data, const size_t len) {
+ try {
+ hmac_->update(static_cast<const Botan::byte*>(data), len);
+ } catch (const Botan::Exception& exc) {
+ isc_throw(isc::cryptolink::LibraryError, exc.what());
+ }
+ }
+
+ void sign(isc::util::OutputBuffer& result, size_t len) {
+ try {
+ Botan::SecureVector<Botan::byte> b_result(hmac_->final());
+
+ if (len == 0 || len > b_result.size()) {
+ len = b_result.size();
+ }
+ result.writeData(b_result.begin(), len);
+ } catch (const Botan::Exception& exc) {
+ isc_throw(isc::cryptolink::LibraryError, exc.what());
+ }
+ }
+
+ void sign(void* result, size_t len) {
+ try {
+ Botan::SecureVector<Botan::byte> b_result(hmac_->final());
+ size_t output_size = getOutputLength();
+ if (output_size > len) {
+ output_size = len;
+ }
+ memcpy(result, b_result.begin(), output_size);
+ } catch (const Botan::Exception& exc) {
+ isc_throw(isc::cryptolink::LibraryError, exc.what());
+ }
+ }
+
+ std::vector<uint8_t> sign(size_t len) {
+ try {
+ Botan::SecureVector<Botan::byte> b_result(hmac_->final());
+ if (len == 0 || len > b_result.size()) {
+ return (std::vector<uint8_t>(b_result.begin(), b_result.end()));
+ } else {
+ return (std::vector<uint8_t>(b_result.begin(), &b_result[len]));
+ }
+ } catch (const Botan::Exception& exc) {
+ isc_throw(isc::cryptolink::LibraryError, exc.what());
+ }
+ }
+
+
+ bool verify(const void* sig, size_t len) {
+ // Botan's verify_mac checks if len matches the output_length,
+ // which causes it to fail for truncated signatures, so we do
+ // the check ourselves
+ // SEE BELOW FOR TEMPORARY CHANGE
+ try {
+ Botan::SecureVector<Botan::byte> our_mac = hmac_->final();
+ if (len < getOutputLength()) {
+ // Currently we don't support truncated signature in TSIG (see
+ // #920). To avoid validating too short signature accidently,
+ // we enforce the standard signature size for the moment.
+ // Once we support truncation correctly, this if-clause should
+ // (and the capitalized comment above) be removed.
+ return (false);
+ }
+ if (len == 0 || len > getOutputLength()) {
+ len = getOutputLength();
+ }
+ return (Botan::same_mem(&our_mac[0],
+ static_cast<const unsigned char*>(sig),
+ len));
+ } catch (const Botan::Exception& exc) {
+ isc_throw(isc::cryptolink::LibraryError, exc.what());
+ }
+ }
+
+private:
+ boost::scoped_ptr<Botan::HMAC> hmac_;
+};
+
+HMAC::HMAC(const void* secret, size_t secret_length,
+ const HashAlgorithm hash_algorithm)
+{
+ impl_ = new HMACImpl(secret, secret_length, hash_algorithm);
+}
+
+HMAC::~HMAC() {
+ delete impl_;
+}
+
+size_t
+HMAC::getOutputLength() const {
+ return (impl_->getOutputLength());
+}
+
+void
+HMAC::update(const void* data, const size_t len) {
+ impl_->update(data, len);
+}
+
+void
+HMAC::sign(isc::util::OutputBuffer& result, size_t len) {
+ impl_->sign(result, len);
+}
+
+void
+HMAC::sign(void* result, size_t len) {
+ impl_->sign(result, len);
+}
+
+std::vector<uint8_t>
+HMAC::sign(size_t len) {
+ return impl_->sign(len);
+}
+
+bool
+HMAC::verify(const void* sig, const size_t len) {
+ return (impl_->verify(sig, len));
+}
+
+void
+signHMAC(const void* data, const size_t data_len, const void* secret,
+ size_t secret_len, const HashAlgorithm hash_algorithm,
+ isc::util::OutputBuffer& result, size_t len)
+{
+ boost::scoped_ptr<HMAC> hmac(
+ CryptoLink::getCryptoLink().createHMAC(secret,
+ secret_len,
+ hash_algorithm));
+ hmac->update(data, data_len);
+ hmac->sign(result, len);
+}
+
+
+bool
+verifyHMAC(const void* data, const size_t data_len, const void* secret,
+ size_t secret_len, const HashAlgorithm hash_algorithm,
+ const void* sig, const size_t sig_len)
+{
+ boost::scoped_ptr<HMAC> hmac(
+ CryptoLink::getCryptoLink().createHMAC(secret,
+ secret_len,
+ hash_algorithm));
+ hmac->update(data, data_len);
+ return (hmac->verify(sig, sig_len));
+}
+
+void
+deleteHMAC(HMAC* hmac) {
+ delete hmac;
+}
+
+} // namespace cryptolink
+} // namespace isc
diff --git a/src/lib/cryptolink/crypto_hmac.h b/src/lib/cryptolink/crypto_hmac.h
new file mode 100644
index 0000000..2eb0d0e
--- /dev/null
+++ b/src/lib/cryptolink/crypto_hmac.h
@@ -0,0 +1,209 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/buffer.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <cryptolink/cryptolink.h>
+
+#ifndef _ISC_CRYPTO_HMAC_H
+#define _ISC_CRYPTO_HMAC_H
+
+namespace isc {
+namespace cryptolink {
+
+/// Forward declaration, pimpl style
+class HMACImpl;
+
+/// \brief HMAC support
+///
+/// This class is used to create and verify HMAC signatures. Instances
+/// can be created with CryptoLink::createHMAC()
+///
+class HMAC : private boost::noncopyable {
+private:
+ /// \brief Constructor from a secret and a hash algorithm
+ ///
+ /// \exception UnsupportedAlgorithmException if the given algorithm
+ /// is unknown or not supported by the underlying library
+ /// \exception InvalidKeyLength if the given key secret_len is bad
+ /// \exception LibraryError if there was any unexpected exception
+ /// in the underlying library
+ ///
+ /// Notes: if the secret is longer than the block size of its
+ /// algorithm, the constructor will run it through the hash
+ /// algorithm, and use the digest as the secret for this HMAC
+ /// operation
+ ///
+ /// \param secret The secret to sign with
+ /// \param len The length of the secret
+ /// \param hash_algorithm The hash algorithm
+ HMAC(const void* secret, size_t secret_len,
+ const HashAlgorithm hash_algorithm);
+
+ friend HMAC* CryptoLink::createHMAC(const void*, size_t,
+ const HashAlgorithm);
+
+public:
+ /// \brief Destructor
+ ~HMAC();
+
+ /// \brief Returns the output size of the digest
+ ///
+ /// \return output size of the digest
+ size_t getOutputLength() const;
+
+ /// \brief Add data to digest
+ ///
+ /// \exception LibraryError if there was any unexpected exception
+ /// in the underlying library
+ ///
+ /// \param data The data to add
+ /// \param len The size of the data
+ void update(const void* data, const size_t len);
+
+ /// \brief Calculate the final signature
+ ///
+ /// The result will be appended to the given outputbuffer
+ ///
+ /// \exception LibraryError if there was any unexpected exception
+ /// in the underlying library
+ ///
+ /// \param result The OutputBuffer to append the result to
+ /// \param len The number of bytes from the result to copy. If this
+ /// value is smaller than the algorithms output size, the
+ /// result will be truncated. If this value is larger, or 0
+ /// (the default), it will be ignored
+ void sign(isc::util::OutputBuffer& result, size_t len = 0);
+
+ /// \brief Calculate the final signature
+ ///
+ /// len bytes of data from the result will be copied to *result
+ /// If len is larger than the output size, only output_size bytes
+ /// will be copied. If it is smaller, the output will be truncated
+ ///
+ /// \exception LibraryError if there was any unexpected exception
+ /// in the underlying library
+ ///
+ /// At least len bytes of data must be available for writing at
+ /// result
+ void sign(void* result, size_t len);
+
+ /// \brief Calculate the final signatre
+ ///
+ /// The result will be returned as a std::vector<uint8_t>
+ ///
+ /// \exception LibraryError if there was any unexpected exception
+ /// in the underlying library
+ ///
+ /// \param len The number of bytes from the result to copy. If this
+ /// value is smaller than the algorithms output size, the
+ /// result will be truncated. If this value is larger, or 0
+ /// (the default), it will be ignored
+ /// \return a vector containing the signature
+ std::vector<uint8_t> sign(size_t len = 0);
+
+ /// \brief Verify an existing signature
+ ///
+ /// \exception LibraryError if there was any unexpected exception
+ /// in the underlying library
+ ///
+ /// \param sig The signature to verify
+ /// \param len The length of the signature. If this is non-zero,
+ /// and smaller than the output length of the algorithm,
+ /// only len bytes will be checked
+ /// \return true if the signature is correct, false otherwise
+ bool verify(const void* sig, size_t len);
+
+private:
+ HMACImpl* impl_;
+};
+
+/// \brief Create an HMAC signature for the given data
+///
+/// This is a convenience function that calculates the hmac signature,
+/// given a fixed amount of data. Internally it does the same as
+/// creating an HMAC object, feeding it the data, and calculating the
+/// resulting signature.
+///
+/// \exception UnsupportedAlgorithm if the given algorithm is unknown
+/// or not supported by the underlying library
+/// \exception BadKey if the given key secret_len is bad
+/// \exception LibraryError if there was any unexpected exception
+/// in the underlying library
+///
+/// Notes: if the secret is longer than the block size of its
+/// algorithm, the constructor will run it through the hash
+/// algorithm, and use the digest as the secret for this HMAC
+/// operation
+///
+/// \param data The data to sign
+/// \param data_len The length of the data
+/// \param secret The secret to sign with
+/// \param secret_len The length of the secret
+/// \param hash_algorithm The hash algorithm
+/// \param result The signature will be appended to this buffer
+/// \param len If this is non-zero and less than the output size,
+/// the result will be truncated to len bytes
+void signHMAC(const void* data,
+ const size_t data_len,
+ const void* secret,
+ size_t secret_len,
+ const HashAlgorithm hash_algorithm,
+ isc::util::OutputBuffer& result,
+ size_t len = 0);
+
+/// \brief Verify an HMAC signature for the given data
+///
+/// This is a convenience function that verifies an hmac signature,
+/// given a fixed amount of data. Internally it does the same as
+/// creating an HMAC object, feeding it the data, and checking the
+/// resulting signature.
+///
+/// \exception UnsupportedAlgorithm if the given algorithm is unknown
+/// or not supported by the underlying library
+/// \exception BadKey if the given key secret_len is bad
+/// \exception LibraryError if there was any unexpected exception
+/// in the underlying library
+///
+/// Notes: if the secret is longer than the block size of its
+/// algorithm, the constructor will run it through the hash
+/// algorithm, and use the digest as the secret for this HMAC
+/// operation
+///
+/// \param data The data to verify
+/// \param data_len The length of the data
+/// \param secret The secret to sign with
+/// \param secret_len The length of the secret
+/// \param hash_algorithm The hash algorithm
+/// \param sig The signature to verify
+/// \param sig_len The length of the signature
+/// \return True if the signature verifies, false if not
+bool verifyHMAC(const void* data,
+ const size_t data_len,
+ const void* secret,
+ size_t secret_len,
+ const HashAlgorithm hash_algorithm,
+ const void* sig,
+ const size_t sig_len);
+
+/// \brief Delete an HMAC object
+void deleteHMAC(HMAC* hmac);
+
+} // namespace cryptolink
+} // namespace isc
+
+#endif // __ISC_CRYPTO_HMAC
+
diff --git a/src/lib/cryptolink/cryptolink.cc b/src/lib/cryptolink/cryptolink.cc
new file mode 100644
index 0000000..d1c375d
--- /dev/null
+++ b/src/lib/cryptolink/cryptolink.cc
@@ -0,0 +1,69 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cryptolink/cryptolink.h>
+#include <cryptolink/crypto_hmac.h>
+
+#include <botan/botan.h>
+
+namespace isc {
+namespace cryptolink {
+
+// For Botan, we use the CryptoLink class object in RAII style
+class CryptoLinkImpl {
+private:
+ Botan::LibraryInitializer botan_init_;
+};
+
+CryptoLink::~CryptoLink() {
+ delete impl_;
+}
+
+CryptoLink&
+CryptoLink::getCryptoLink() {
+ CryptoLink& c = getCryptoLinkInternal();
+ if (c.impl_ == NULL) {
+ c.initialize();
+ }
+ return (c);
+}
+
+CryptoLink&
+CryptoLink::getCryptoLinkInternal() {
+ static CryptoLink instance;
+ return (instance);
+}
+
+void
+CryptoLink::initialize() {
+ CryptoLink& c = getCryptoLinkInternal();
+ if (c.impl_ == NULL) {
+ try {
+ c.impl_ = new CryptoLinkImpl();
+ } catch (const Botan::Exception& ex) {
+ isc_throw(InitializationError, ex.what());
+ }
+ }
+}
+
+HMAC*
+CryptoLink::createHMAC(const void* secret, size_t secret_len,
+ const HashAlgorithm hash_algorithm)
+{
+ return (new HMAC(secret, secret_len, hash_algorithm));
+}
+
+} // namespace cryptolink
+} // namespace isc
+
diff --git a/src/lib/cryptolink/cryptolink.h b/src/lib/cryptolink/cryptolink.h
new file mode 100644
index 0000000..d0f7d38
--- /dev/null
+++ b/src/lib/cryptolink/cryptolink.h
@@ -0,0 +1,208 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef _ISC_CRYPTO_H
+#define _ISC_CRYPTO_H
+
+#include <string>
+#include <util/buffer.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <memory>
+
+namespace isc {
+namespace cryptolink {
+
+/// \brief Hash algorithm identifiers
+enum HashAlgorithm {
+ UNKNOWN_HASH = 0, ///< This value can be used in conversion
+ /// functions, to be returned when the
+ /// input is unknown (but a value MUST be
+ /// returned), for instance when the input
+ /// is a Name or a string, and the return
+ /// value is a HashAlgorithm.
+ MD5 = 1, ///< MD5
+ SHA1 = 2, ///< SHA-1
+ SHA256 = 3, ///< SHA-256
+ SHA224 = 4, ///< SHA-224
+ SHA384 = 5, ///< SHA-384
+ SHA512 = 6 ///< SHA-512
+
+};
+
+// Forward declaration for createHMAC()
+class HMAC;
+
+/// General exception class that is the base for all crypto-related
+/// exceptions
+class CryptoLinkError : public Exception {
+public:
+ CryptoLinkError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// This exception is thrown if there was a problem initializing the
+/// crypto library
+class InitializationError : public CryptoLinkError {
+public:
+ InitializationError(const char* file, size_t line, const char* what) :
+ CryptoLinkError(file, line, what) {}
+};
+
+/// This exception is thrown when a cryptographic action is requested
+/// for an algorithm that is not supported by the underlying library.
+class UnsupportedAlgorithm : public CryptoLinkError {
+public:
+ UnsupportedAlgorithm(const char* file, size_t line, const char* what) :
+ CryptoLinkError(file, line, what) {}
+};
+
+/// This exception is thrown when the underlying library could not
+/// handle the key data.
+class BadKey : public CryptoLinkError {
+public:
+ BadKey(const char* file, size_t line, const char* what) :
+ CryptoLinkError(file, line, what) {}
+};
+
+/// This exception is raised when a general error that was not
+/// specifically caught is thrown by the underlying library. It
+/// is replaced by this one so as not have 'external' exceptions
+/// bubbling up
+class LibraryError : public CryptoLinkError {
+public:
+ LibraryError(const char* file, size_t line, const char* what) :
+ CryptoLinkError(file, line, what) {}
+};
+
+/// Forward declaration for pimpl
+class CryptoLinkImpl;
+
+/// \brief Singleton entry point and factory class
+///
+/// This is a singleton class that serves as the entry point to
+/// the underlying cryptography library, and as a factory for objects
+/// within the cryptolink library.
+///
+/// There is only one way to access it, through getCryptoLink(), which
+/// returns a reference to the initialized library. On the first call,
+/// it will be initialized automatically. You can however initialize it
+/// manually through a call to the initalize(), before your first call
+/// to getCryptoLink. Any subsequent call to initialize() will be a
+/// noop.
+///
+/// In order for the CryptoLink library to be sure that the underlying
+/// library has been initialized, and because we do not want to add
+/// such a check to every class and function within it, we have made
+/// the constructors of all classes within cryptolink private. This way
+/// a caller cannot instantiate an object before the library is
+/// initialized, but must use CryptoLink's create method (e.g.
+/// createHMAC()), which enforces (automatic) initialization.
+///
+/// In order for the CryptoLink class to be able to create objects that
+/// have private constructors, it is declared a friend class of these
+/// classes.
+///
+/// Since these factory functions return bare pointers, we also provide
+/// deleter functions for them (e.g. deleteHMAC()), so that a caller
+/// can use that to make sure it uses the correct delete operator (the
+/// one defined at compilation time of this library). A way to make
+/// sure you do not forget this, is to place the result of the create
+/// functions in a shared_ptr with the corresponding deleter function.
+///
+/// \note All other classes within cryptolink should have private
+/// constructors as well, and should have a factory function from
+/// CryptoLink, and a deleter function.
+///
+// Internal note: we can use this class later to initialize and manage
+// dynamic (PKCS#11) libs
+class CryptoLink : private boost::noncopyable {
+public:
+ /// \brief Returns a reference to the singleton instance
+ ///
+ /// If the library has not been initialized yet, it will be
+ /// initialized with some default values.
+ ///
+ /// Since this class is noncopyable, you must use the return
+ /// value directly, or store it in a reference variable.
+ ///
+ /// \exception InitializationError if initialization fails
+ ///
+ /// \return Reference to the singleton instance
+ static CryptoLink& getCryptoLink();
+
+ /// \brief Initialize the library manually
+ ///
+ /// If the library has already been initialized (either by a call
+ /// to initialize() or automatically in getCryptoLink()), this
+ /// function does nothing.
+ ///
+ /// \note A call to initialize() is not strictly necessary with
+ /// the current implementation.
+ ///
+ /// \exception InitializationError if initialization fails
+ ///
+ static void initialize();
+
+ /// \brief Factory function for HMAC objects
+ ///
+ /// CryptoLink objects cannot be constructed directly. This
+ /// function creates a new HMAC object usable for signing or
+ /// verification.
+ ///
+ /// The caller is responsible for deleting the object, and it is
+ /// therefore highly recommended to place the return value of this
+ /// function in a scoped_ptr or shared_ptr.
+ ///
+ /// Notes: if the secret is longer than the block size of its
+ /// algorithm, the constructor will run it through the hash
+ /// algorithm, and use the digest as the secret for this HMAC
+ /// operation
+ ///
+ /// If you want to safely delete objects created with this method,
+ /// you can use the function deleteHMAC() as defined in
+ /// crypto_hmac.h
+ ///
+ /// \exception UnsupportedAlgorithmException if the given algorithm
+ /// is unknown or not supported by the underlying library
+ /// \exception InvalidKeyLength if the given key secret_len is bad
+ /// \exception LibraryError if there was any unexpected exception
+ /// in the underlying library
+ ///
+ /// \param secret The secret to sign with
+ /// \param secret_len The length of the secret
+ /// \param hash_algorithm The hash algorithm
+ HMAC* createHMAC(const void* secret, size_t secret_len,
+ const HashAlgorithm hash_algorithm);
+
+private:
+ // To enable us to use an optional explicit initialization call,
+ // the 'real' instance getter is private
+ static CryptoLink& getCryptoLinkInternal();
+
+ // To prevent people constructing their own, we make the constructor
+ // private too.
+ CryptoLink() : impl_(NULL) {}
+ ~CryptoLink();
+
+ CryptoLinkImpl* impl_;
+};
+
+} // namespace cryptolink
+} // namespace isc
+
+#endif // _ISC_CRYPTO_H
diff --git a/src/lib/cryptolink/tests/Makefile.am b/src/lib/cryptolink/tests/Makefile.am
new file mode 100644
index 0000000..fbdd13f
--- /dev/null
+++ b/src/lib/cryptolink/tests/Makefile.am
@@ -0,0 +1,27 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += crypto_unittests.cc
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = ${BOTAN_LDFLAGS} $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libcryptolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/cryptolink/tests/crypto_unittests.cc b/src/lib/cryptolink/tests/crypto_unittests.cc
new file mode 100644
index 0000000..4abeb87
--- /dev/null
+++ b/src/lib/cryptolink/tests/crypto_unittests.cc
@@ -0,0 +1,610 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <string>
+#include <vector>
+
+#include <boost/lexical_cast.hpp>
+
+#include <gtest/gtest.h>
+
+#include <util/encode/hex.h>
+
+#include <cryptolink/cryptolink.h>
+#include <cryptolink/crypto_hmac.h>
+
+#include <util/buffer.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+
+using namespace boost;
+using namespace isc::util;
+using namespace isc::util::encode;
+using namespace isc::cryptolink;
+
+namespace {
+ void checkData(const uint8_t* data, const uint8_t* expected,
+ size_t len) {
+ for (size_t i = 0; i < len; ++i) {
+ ASSERT_EQ(expected[i], data[i]);
+ }
+ }
+
+ void checkBuffer(const OutputBuffer& buf, const uint8_t* expected,
+ size_t len)
+ {
+ ASSERT_EQ(len, buf.getLength());
+ checkData(static_cast<const uint8_t*>(buf.getData()), expected,
+ len);
+ }
+
+ // Sign and verify with the convenience functions
+ void doHMACTestConv(const std::string& data,
+ const void* secret,
+ size_t secret_len,
+ const HashAlgorithm hash_algorithm,
+ const uint8_t* expected_hmac,
+ size_t hmac_len) {
+ OutputBuffer data_buf(data.size());
+ data_buf.writeData(data.c_str(), data.size());
+ OutputBuffer hmac_sig(0);
+
+ // Sign it
+ signHMAC(data_buf.getData(), data_buf.getLength(),
+ secret, secret_len, hash_algorithm, hmac_sig, hmac_len);
+
+ // Check if the signature is what we expect
+ checkBuffer(hmac_sig, expected_hmac, hmac_len);
+
+ // Check whether we can verify it ourselves
+ EXPECT_TRUE(verifyHMAC(data_buf.getData(), data_buf.getLength(),
+ secret, secret_len, hash_algorithm,
+ hmac_sig.getData(),
+ hmac_sig.getLength()));
+
+ // Change the sig by flipping the first octet, and check
+ // whether verification fails then
+ hmac_sig.writeUint8At(~hmac_sig[0], 0);
+ EXPECT_FALSE(verifyHMAC(data_buf.getData(), data_buf.getLength(),
+ secret, secret_len, hash_algorithm,
+ hmac_sig.getData(),
+ hmac_sig.getLength()));
+ }
+
+ // Sign and verify with an instantiation of an HMAC object
+ void doHMACTestDirect(const std::string& data,
+ const void* secret,
+ size_t secret_len,
+ const HashAlgorithm hash_algorithm,
+ const uint8_t* expected_hmac,
+ size_t hmac_len) {
+ OutputBuffer data_buf(data.size());
+ data_buf.writeData(data.c_str(), data.size());
+ OutputBuffer hmac_sig(1);
+ CryptoLink& crypto = CryptoLink::getCryptoLink();
+
+ // Sign it
+ boost::shared_ptr<HMAC> hmac_sign(crypto.createHMAC(secret,
+ secret_len,
+ hash_algorithm),
+ deleteHMAC);
+ hmac_sign->update(data_buf.getData(), data_buf.getLength());
+ hmac_sign->sign(hmac_sig, hmac_len);
+
+ // Check if the signature is what we expect
+ checkBuffer(hmac_sig, expected_hmac, hmac_len);
+
+ // Check whether we can verify it ourselves
+ boost::shared_ptr<HMAC> hmac_verify(crypto.createHMAC(secret,
+ secret_len,
+ hash_algorithm),
+ deleteHMAC);
+ hmac_verify->update(data_buf.getData(), data_buf.getLength());
+ EXPECT_TRUE(hmac_verify->verify(hmac_sig.getData(),
+ hmac_sig.getLength()));
+
+ // Change the sig by flipping the first octet, and check
+ // whether verification fails then
+ hmac_sig.writeUint8At(~hmac_sig[0], 0);
+ EXPECT_FALSE(hmac_verify->verify(hmac_sig.getData(),
+ hmac_sig.getLength()));
+ }
+
+ void doHMACTestVector(const std::string& data,
+ const void* secret,
+ size_t secret_len,
+ const HashAlgorithm hash_algorithm,
+ const uint8_t* expected_hmac,
+ size_t hmac_len) {
+ CryptoLink& crypto = CryptoLink::getCryptoLink();
+ boost::shared_ptr<HMAC> hmac_sign(crypto.createHMAC(secret,
+ secret_len,
+ hash_algorithm),
+ deleteHMAC);
+ hmac_sign->update(data.c_str(), data.size());
+ std::vector<uint8_t> sig = hmac_sign->sign(hmac_len);
+ ASSERT_EQ(hmac_len, sig.size());
+ checkData(&sig[0], expected_hmac, hmac_len);
+
+ boost::shared_ptr<HMAC> hmac_verify(crypto.createHMAC(secret,
+ secret_len,
+ hash_algorithm),
+ deleteHMAC);
+ hmac_verify->update(data.c_str(), data.size());
+ EXPECT_TRUE(hmac_verify->verify(&sig[0], sig.size()));
+
+ sig[0] = ~sig[0];
+ EXPECT_FALSE(hmac_verify->verify(&sig[0], sig.size()));
+ }
+
+ void doHMACTestArray(const std::string& data,
+ const void* secret,
+ size_t secret_len,
+ const HashAlgorithm hash_algorithm,
+ const uint8_t* expected_hmac,
+ size_t hmac_len) {
+ CryptoLink& crypto = CryptoLink::getCryptoLink();
+ boost::shared_ptr<HMAC> hmac_sign(crypto.createHMAC(secret,
+ secret_len,
+ hash_algorithm),
+ deleteHMAC);
+ hmac_sign->update(data.c_str(), data.size());
+
+ // note: this is not exception-safe, and can leak, but
+ // if there is an unexpected exception in the code below we
+ // have more important things to fix.
+ uint8_t* sig = new uint8_t[hmac_len];
+
+ hmac_sign->sign(sig, hmac_len);
+ checkData(sig, expected_hmac, hmac_len);
+
+ boost::shared_ptr<HMAC> hmac_verify(crypto.createHMAC(secret,
+ secret_len,
+ hash_algorithm),
+ deleteHMAC);
+ hmac_verify->update(data.c_str(), data.size());
+ EXPECT_TRUE(hmac_verify->verify(sig, hmac_len));
+
+ sig[0] = ~sig[0];
+ EXPECT_FALSE(hmac_verify->verify(sig, hmac_len));
+
+ delete[] sig;
+ }
+
+ void doHMACTest(const std::string& data,
+ const void* secret,
+ size_t secret_len,
+ const HashAlgorithm hash_algorithm,
+ const uint8_t* expected_hmac,
+ size_t hmac_len) {
+ doHMACTestConv(data, secret, secret_len, hash_algorithm,
+ expected_hmac, hmac_len);
+ doHMACTestDirect(data, secret, secret_len, hash_algorithm,
+ expected_hmac, hmac_len);
+ doHMACTestVector(data, secret, secret_len, hash_algorithm,
+ expected_hmac, hmac_len);
+ doHMACTestArray(data, secret, secret_len, hash_algorithm,
+ expected_hmac, hmac_len);
+ }
+}
+
+//
+// Test values taken from RFC 2202
+//
+TEST(CryptoLinkTest, HMAC_MD5_RFC2202_SIGN) {
+ const uint8_t secret[] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b };
+ const uint8_t hmac_expected[] = { 0x92, 0x94, 0x72, 0x7a, 0x36,
+ 0x38, 0xbb, 0x1c, 0x13, 0xf4,
+ 0x8e, 0xf8, 0x15, 0x8b, 0xfc,
+ 0x9d };
+ doHMACTest("Hi There", secret, 16, MD5, hmac_expected, 16);
+
+ const uint8_t hmac_expected2[] = { 0x75, 0x0c, 0x78, 0x3e, 0x6a,
+ 0xb0, 0xb5, 0x03, 0xea, 0xa8,
+ 0x6e, 0x31, 0x0a, 0x5d, 0xb7,
+ 0x38 };
+ doHMACTest("what do ya want for nothing?", "Jefe", 4, MD5,
+ hmac_expected2, 16);
+
+ const uint8_t secret3[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa };
+ const uint8_t hmac_expected3[] = { 0x56, 0xbe, 0x34, 0x52, 0x1d,
+ 0x14, 0x4c, 0x88, 0xdb, 0xb8,
+ 0xc7, 0x33, 0xf0, 0xe8, 0xb3,
+ 0xf6};
+ doHMACTest(std::string(50, 0xdd), secret3, 16, MD5, hmac_expected3, 16);
+
+ const std::string data4(50, 0xcd);
+ const uint8_t secret4[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+ 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+ 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19 };
+ const uint8_t hmac_expected4[] = { 0x69, 0x7e, 0xaf, 0x0a, 0xca,
+ 0x3a, 0x3a, 0xea, 0x3a, 0x75,
+ 0x16, 0x47, 0x46, 0xff, 0xaa,
+ 0x79 };
+ doHMACTest(data4, secret4, 25, MD5, hmac_expected4, 16);
+
+ const uint8_t hmac_expected6[] = { 0x6b, 0x1a, 0xb7, 0xfe, 0x4b,
+ 0xd7, 0xbf, 0x8f, 0x0b, 0x62,
+ 0xe6, 0xce, 0x61, 0xb9, 0xd0,
+ 0xcd };
+ doHMACTest("Test Using Larger Than Block-Size Key - Hash Key First",
+ std::string(80, 0xaa).c_str(), 80, MD5, hmac_expected6, 16);
+
+ const uint8_t hmac_expected7[] = { 0x6f, 0x63, 0x0f, 0xad, 0x67,
+ 0xcd, 0xa0, 0xee, 0x1f, 0xb1,
+ 0xf5, 0x62, 0xdb, 0x3a, 0xa5,
+ 0x3e };
+ doHMACTest("Test Using Larger Than Block-Size Key and Larger Than "
+ "One Block-Size Data",
+ std::string(80, 0xaa).c_str(), 80, MD5, hmac_expected7, 16);
+}
+
+// Temporarily disabled
+TEST(CryptoLinkTest, DISABLED_HMAC_MD5_RFC2202_SIGN_TRUNCATED) {
+ const uint8_t secret5[] = { 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c };
+ const uint8_t hmac_expected5[] = { 0x56, 0x46, 0x1e, 0xf2, 0x34,
+ 0x2e, 0xdc, 0x00, 0xf9, 0xba,
+ 0xb9, 0x95, 0x69, 0x0e, 0xfd,
+ 0x4c };
+ doHMACTest("Test With Truncation", secret5, 16, MD5,
+ hmac_expected5, 16);
+ doHMACTest("Test With Truncation", secret5, 16, MD5,
+ hmac_expected5, 12);
+}
+
+//
+// Test values taken from RFC 2202
+//
+TEST(CryptoLinkTest, HMAC_SHA1_RFC2202_SIGN) {
+ const uint8_t secret[] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b };
+ const uint8_t hmac_expected[] = { 0xb6, 0x17, 0x31, 0x86, 0x55,
+ 0x05, 0x72, 0x64, 0xe2, 0x8b,
+ 0xc0, 0xb6, 0xfb, 0x37, 0x8c,
+ 0x8e, 0xf1, 0x46, 0xbe, 0x00 };
+ doHMACTest("Hi There", secret, 20, SHA1, hmac_expected, 20);
+
+ const uint8_t hmac_expected2[] = { 0xef, 0xfc, 0xdf, 0x6a, 0xe5,
+ 0xeb, 0x2f, 0xa2, 0xd2, 0x74,
+ 0x16, 0xd5, 0xf1, 0x84, 0xdf,
+ 0x9c, 0x25, 0x9a, 0x7c, 0x79 };
+ doHMACTest("what do ya want for nothing?", "Jefe", 4, SHA1,
+ hmac_expected2, 20);
+
+ const uint8_t secret3[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa };
+ const uint8_t hmac_expected3[] = { 0x12, 0x5d, 0x73, 0x42, 0xb9,
+ 0xac, 0x11, 0xcd, 0x91, 0xa3,
+ 0x9a, 0xf4, 0x8a, 0xa1, 0x7b,
+ 0x4f, 0x63, 0xf1, 0x75, 0xd3 };
+ doHMACTest(std::string(50, 0xdd), secret3, 20, SHA1, hmac_expected3, 20);
+
+ const uint8_t secret4[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+ 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+ 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19 };
+ const uint8_t hmac_expected4[] = { 0x4c, 0x90, 0x07, 0xf4, 0x02,
+ 0x62, 0x50, 0xc6, 0xbc, 0x84,
+ 0x14, 0xf9, 0xbf, 0x50, 0xc8,
+ 0x6c, 0x2d, 0x72, 0x35, 0xda };
+ doHMACTest(std::string(50, 0xcd), secret4, 25, SHA1, hmac_expected4, 20);
+
+ const uint8_t hmac_expected6[] = { 0xaa, 0x4a, 0xe5, 0xe1, 0x52,
+ 0x72, 0xd0, 0x0e, 0x95, 0x70,
+ 0x56, 0x37, 0xce, 0x8a, 0x3b,
+ 0x55, 0xed, 0x40, 0x21, 0x12 };
+ doHMACTest("Test Using Larger Than Block-Size Key - Hash Key First",
+ std::string(80, 0xaa).c_str(), 80, SHA1, hmac_expected6, 20);
+
+ const uint8_t hmac_expected7[] = { 0xe8, 0xe9, 0x9d, 0x0f, 0x45,
+ 0x23, 0x7d, 0x78, 0x6d, 0x6b,
+ 0xba, 0xa7, 0x96, 0x5c, 0x78,
+ 0x08, 0xbb, 0xff, 0x1a, 0x91 };
+ doHMACTest("Test Using Larger Than Block-Size Key and Larger Than "
+ "One Block-Size Data",
+ std::string(80, 0xaa).c_str(), 80, SHA1, hmac_expected7, 20);
+}
+
+// Temporarily disabled
+TEST(CryptoLinkTest, DISABLED_HMAC_SHA1_RFC2202_SIGN_TRUNCATED) {
+ const uint8_t secret5[] = { 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c };
+ const uint8_t hmac_expected5[] = { 0x4c, 0x1a, 0x03, 0x42, 0x4b,
+ 0x55, 0xe0, 0x7f, 0xe7, 0xf2,
+ 0x7b, 0xe1, 0xd5, 0x8b, 0xb9,
+ 0x32, 0x4a, 0x9a, 0x5a, 0x04 };
+ doHMACTest("Test With Truncation", secret5, 20, SHA1,
+ hmac_expected5, 20);
+ doHMACTest("Test With Truncation", secret5, 20, SHA1,
+ hmac_expected5, 12);
+}
+
+//
+// Test values taken from RFC 4231
+//
+// Test data from RFC4231, including secret key
+// and source data, they are common for sha224/256/384/512
+// so put them together within the separate function.
+void
+doRFC4231Tests(HashAlgorithm hash_algorithm,
+ const std::vector<std::vector<uint8_t> >& hmac_list)
+{
+ std::vector<std::string> data_list;
+ std::vector<std::string> secret_list;
+
+ data_list.push_back("Hi There");
+ data_list.push_back("what do ya want for nothing?");
+ data_list.push_back(std::string(50, 0xdd));
+ data_list.push_back(std::string(50, 0xcd));
+ data_list.push_back("Test With Truncation");
+ data_list.push_back("Test Using Larger Than Block-Size Key - "
+ "Hash Key First");
+ data_list.push_back("This is a test using a larger than block-size "
+ "key and a larger than block-size data. The key "
+ "needs to be hashed before being used by the HMAC "
+ "algorithm.");
+
+ secret_list.push_back(std::string(20, 0x0b));
+ secret_list.push_back("Jefe");
+ secret_list.push_back(std::string(20, 0xaa));
+ const uint8_t secret_array[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+ 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+ 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19
+ };
+ secret_list.push_back(std::string(secret_array,
+ secret_array + sizeof(secret_array)));
+ secret_list.push_back(std::string(20, 0x0c));
+ secret_list.push_back(std::string(131, 0xaa));
+ secret_list.push_back(std::string(131, 0xaa));
+
+ // Make sure we provide a consistent size of test data
+ ASSERT_EQ(secret_list.size(), data_list.size());
+ ASSERT_EQ(secret_list.size(), hmac_list.size());
+
+ for (int i = 0; i < data_list.size(); ++i) {
+ SCOPED_TRACE("RFC4231 HMAC test for algorithm ID: " +
+ lexical_cast<std::string>(hash_algorithm) +
+ ", data ID: " + lexical_cast<std::string>(i));
+ // Until #920 is resolved we have to skip truncation cases.
+ if (data_list[i] == "Test With Truncation") {
+ continue;
+ }
+ doHMACTest(data_list[i], secret_list[i].c_str(), secret_list[i].size(),
+ hash_algorithm, &hmac_list[i][0], hmac_list[i].size());
+ }
+}
+
+TEST(CryptoLinkTest, HMAC_SHA256_RFC4231_SIGN) {
+ std::vector<std::vector<uint8_t> > hmac_expected_list(7);
+
+ int i = 0;
+ decodeHex(
+ "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7",
+ hmac_expected_list[i++]);
+ decodeHex(
+ "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843",
+ hmac_expected_list[i++]);
+ decodeHex(
+ "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe",
+ hmac_expected_list[i++]);
+ decodeHex(
+ "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b",
+ hmac_expected_list[i++]);
+ decodeHex("a3b6167473100ee06e0c796c2955552b", hmac_expected_list[i++]);
+ decodeHex(
+ "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54",
+ hmac_expected_list[i++]);
+ decodeHex(
+ "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2",
+ hmac_expected_list[i++]);
+
+ doRFC4231Tests(SHA256, hmac_expected_list);
+}
+
+//
+// Test values taken from RFC 4231, test optional algorithm 224,384,512
+//
+TEST(CryptoLinkTest, HMAC_SHA224_RFC4231_SIGN) {
+ std::vector<std::vector<uint8_t> > hmac_expected_list(7);
+
+ int i = 0;
+ decodeHex("896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22",
+ hmac_expected_list[i++]);
+ decodeHex("a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44",
+ hmac_expected_list[i++]);
+ decodeHex("7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea",
+ hmac_expected_list[i++]);
+ decodeHex("6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a",
+ hmac_expected_list[i++]);
+ decodeHex("0e2aea68a90c8d37c988bcdb9fca6fa8", hmac_expected_list[i++]);
+ decodeHex("95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e",
+ hmac_expected_list[i++]);
+ decodeHex("3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1",
+ hmac_expected_list[i++]);
+
+ doRFC4231Tests(SHA224, hmac_expected_list);
+}
+
+TEST(CryptoLinkTest, HMAC_SHA384_RFC4231_SIGN) {
+ std::vector<std::vector<uint8_t> > hmac_expected_list(7);
+
+ int i = 0;
+ decodeHex("afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc5"
+ "9cfaea9ea9076ede7f4af152e8b2fa9cb6", hmac_expected_list[i++]);
+ decodeHex("af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec373632244"
+ "5e8e2240ca5e69e2c78b3239ecfab21649", hmac_expected_list[i++]);
+ decodeHex("88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e5596614"
+ "4b2a5ab39dc13814b94e3ab6e101a34f27", hmac_expected_list[i++]);
+ decodeHex("3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b"
+ "4e6801dd23c4a7d679ccf8a386c674cffb", hmac_expected_list[i++]);
+ decodeHex("3abf34c3503b2a23a46efc619baef897", hmac_expected_list[i++]);
+ decodeHex("4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4"
+ "c60c2ef6ab4030fe8296248df163f44952", hmac_expected_list[i++]);
+ decodeHex("6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99"
+ "c5a678cc31e799176d3860e6110c46523e", hmac_expected_list[i++]);
+
+ doRFC4231Tests(SHA384, hmac_expected_list);
+}
+
+TEST(CryptoLinkTest, HMAC_SHA512_RFC4231_SIGN) {
+ std::vector<std::vector<uint8_t> > hmac_expected_list(7);
+
+ int i = 0;
+ decodeHex("87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17c"
+ "dedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a12"
+ "6854", hmac_expected_list[i++]);
+ decodeHex("164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505"
+ "549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bc"
+ "e737", hmac_expected_list[i++]);
+ decodeHex("fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d"
+ "39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e132"
+ "92fb", hmac_expected_list[i++]);
+ decodeHex("b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3"
+ "dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a2"
+ "98dd", hmac_expected_list[i++]);
+ decodeHex("415fad6271580a531d4179bc891d87a6", hmac_expected_list[i++]);
+ decodeHex("80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3"
+ "526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d78"
+ "6598", hmac_expected_list[i++]);
+ decodeHex("e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc9"
+ "44b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c"
+ "6a58", hmac_expected_list[i++]);
+
+ doRFC4231Tests(SHA512, hmac_expected_list);
+}
+
+TEST(CryptoLinkTest, DISABLED_HMAC_SHA256_RFC2202_SIGN_TRUNCATED) {
+ const uint8_t secret5[] = { 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c };
+ const uint8_t hmac_expected5[] = { 0xa3, 0xb6, 0x16, 0x74, 0x73,
+ 0x10, 0x0e, 0xe0, 0x6e, 0x0c,
+ 0x79, 0x6c, 0x29, 0x55, 0x55,
+ 0x2b };
+ doHMACTest("Test With Truncation", secret5, 20, SHA256,
+ hmac_expected5, 16);
+}
+
+namespace {
+ size_t
+ sigVectorLength(HashAlgorithm alg, size_t len) {
+ boost::shared_ptr<HMAC> hmac_sign(
+ CryptoLink::getCryptoLink().createHMAC("asdf", 4, alg),
+ deleteHMAC);
+ hmac_sign->update("asdf", 4);
+ const std::vector<uint8_t> sig = hmac_sign->sign(len);
+ return (sig.size());
+ }
+
+ size_t
+ sigBufferLength(HashAlgorithm alg, size_t len) {
+ boost::shared_ptr<HMAC> hmac_sign(
+ CryptoLink::getCryptoLink().createHMAC("asdf", 4, alg),
+ deleteHMAC);
+ hmac_sign->update("asdf", 4);
+ OutputBuffer sig(0);
+ hmac_sign->sign(sig, len);
+ return (sig.getLength());
+ }
+}
+
+TEST(CryptoLinkTest, HMACSigLengthArgument) {
+ std::vector<uint8_t> sig;
+
+ EXPECT_EQ(16, sigVectorLength(MD5, 0));
+ EXPECT_EQ(8, sigVectorLength(MD5, 8));
+ EXPECT_EQ(16, sigVectorLength(MD5, 16));
+ EXPECT_EQ(16, sigVectorLength(MD5, 40));
+ EXPECT_EQ(16, sigVectorLength(MD5, 2000));
+
+ EXPECT_EQ(20, sigBufferLength(SHA1, 0));
+ EXPECT_EQ(8, sigBufferLength(SHA1, 8));
+ EXPECT_EQ(20, sigBufferLength(SHA1, 20));
+ EXPECT_EQ(20, sigBufferLength(SHA1, 40));
+ EXPECT_EQ(20, sigBufferLength(SHA1, 2000));
+
+ EXPECT_EQ(32, sigBufferLength(SHA256, 0));
+ EXPECT_EQ(8, sigBufferLength(SHA256, 8));
+ EXPECT_EQ(32, sigBufferLength(SHA256, 32));
+ EXPECT_EQ(32, sigBufferLength(SHA256, 40));
+ EXPECT_EQ(32, sigBufferLength(SHA256, 3200));
+
+ EXPECT_EQ(16, sigBufferLength(MD5, 0));
+ EXPECT_EQ(8, sigBufferLength(MD5, 8));
+ EXPECT_EQ(16, sigBufferLength(MD5, 16));
+ EXPECT_EQ(16, sigBufferLength(MD5, 40));
+ EXPECT_EQ(16, sigBufferLength(MD5, 2000));
+
+ EXPECT_EQ(20, sigBufferLength(SHA1, 0));
+ EXPECT_EQ(8, sigBufferLength(SHA1, 8));
+ EXPECT_EQ(20, sigBufferLength(SHA1, 20));
+ EXPECT_EQ(20, sigBufferLength(SHA1, 40));
+ EXPECT_EQ(20, sigBufferLength(SHA1, 2000));
+
+ EXPECT_EQ(32, sigBufferLength(SHA256, 0));
+ EXPECT_EQ(8, sigBufferLength(SHA256, 8));
+ EXPECT_EQ(32, sigBufferLength(SHA256, 32));
+ EXPECT_EQ(32, sigBufferLength(SHA256, 40));
+ EXPECT_EQ(32, sigBufferLength(SHA256, 3200));
+}
+
+TEST(CryptoLinkTest, BadKey) {
+ OutputBuffer data_buf(0);
+ OutputBuffer hmac_sig(0);
+ CryptoLink& crypto = CryptoLink::getCryptoLink();
+
+ EXPECT_THROW(crypto.createHMAC(NULL, 0, MD5), BadKey);
+ EXPECT_THROW(crypto.createHMAC(NULL, 0, UNKNOWN_HASH), UnsupportedAlgorithm);
+
+ EXPECT_THROW(signHMAC(data_buf.getData(), data_buf.getLength(),
+ NULL, 0, MD5, hmac_sig), BadKey);
+ EXPECT_THROW(signHMAC(data_buf.getData(), data_buf.getLength(),
+ NULL, 0, UNKNOWN_HASH, hmac_sig),
+ UnsupportedAlgorithm);
+
+ EXPECT_THROW(verifyHMAC(data_buf.getData(), data_buf.getLength(),
+ NULL, 0, MD5, hmac_sig.getData(),
+ hmac_sig.getLength()), BadKey);
+ EXPECT_THROW(verifyHMAC(data_buf.getData(), data_buf.getLength(),
+ NULL, 0, UNKNOWN_HASH, hmac_sig.getData(),
+ hmac_sig.getLength()),
+ UnsupportedAlgorithm);
+}
+
+TEST(CryptoLinkTest, Singleton) {
+ const CryptoLink& c1 = CryptoLink::getCryptoLink();
+ const CryptoLink& c2 = CryptoLink::getCryptoLink();
+ ASSERT_EQ(&c1, &c2);
+}
diff --git a/src/lib/cryptolink/tests/run_unittests.cc b/src/lib/cryptolink/tests/run_unittests.cc
new file mode 100644
index 0000000..a2181cf
--- /dev/null
+++ b/src/lib/cryptolink/tests/run_unittests.cc
@@ -0,0 +1,23 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return (isc::util::unittests::run_all());
+}
diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am
index adb1d41..457d5b0 100644
--- a/src/lib/datasrc/Makefile.am
+++ b/src/lib/datasrc/Makefile.am
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(SQLITE_CFLAGS)
AM_CXXFLAGS = $(B10_CXXFLAGS)
-CLEANFILES = *.gcno *.gcda
+CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
lib_LTLIBRARIES = libdatasrc.la
libdatasrc_la_SOURCES = data_source.h data_source.cc
@@ -20,3 +20,16 @@ libdatasrc_la_SOURCES += zonetable.h zonetable.cc
libdatasrc_la_SOURCES += memory_datasrc.h memory_datasrc.cc
libdatasrc_la_SOURCES += zone.h
libdatasrc_la_SOURCES += result.h
+libdatasrc_la_SOURCES += logger.h logger.cc
+nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc
+
+libdatasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
+libdatasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libdns++.la
+libdatasrc_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la
+libdatasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la
+
+BUILT_SOURCES = datasrc_messages.h datasrc_messages.cc
+datasrc_messages.h datasrc_messages.cc: Makefile datasrc_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/datasrc/datasrc_messages.mes
+
+EXTRA_DIST = datasrc_messages.mes
diff --git a/src/lib/datasrc/cache.cc b/src/lib/datasrc/cache.cc
index 6fff754..9082a6b 100644
--- a/src/lib/datasrc/cache.cc
+++ b/src/lib/datasrc/cache.cc
@@ -24,6 +24,7 @@
#include <list>
#include <datasrc/cache.h>
+#include <datasrc/logger.h>
using namespace std;
using namespace isc::dns;
@@ -99,6 +100,19 @@ public:
/// \return \c RRsetPtr
RRsetPtr getRRset() const { return (entry->rrset); }
+ /// \brief Returns name associated with cached node
+ ///
+ /// This is the name associated with the RRset if it is a positive
+ /// entry, and the associated question name if the RRSet is NULL
+ /// and this is a negative entry (together with an indication that
+ /// this is a negative entry).
+ string getNodeName() const {
+ if (getRRset()) {
+ return (getRRset()->getName().toText());
+ }
+ return (std::string("negative entry for ") + question.toText());
+ }
+
/// \brief Returns the query response flags associated with the data.
///
/// \return \c uint32_t
@@ -204,16 +218,21 @@ public:
// HotCacheImpl constructor
HotCacheImpl::HotCacheImpl(int slots, bool enabled) :
enabled_(enabled), slots_(slots), count_(0)
-{}
+{
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_CACHE_CREATE);
+}
// Insert a cache node into the cache
inline void
HotCacheImpl::insert(const CacheNodePtr node) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_INSERT).
+ arg(node->getNodeName());
std::map<Question, CacheNodePtr>::const_iterator iter;
iter = map_.find(node->question);
if (iter != map_.end()) {
CacheNodePtr old = iter->second;
if (old && old->isValid()) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_OLD_FOUND);
remove(old);
}
}
@@ -225,6 +244,7 @@ HotCacheImpl::insert(const CacheNodePtr node) {
++count_;
if (slots_ != 0 && count_ > slots_) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_FULL);
remove(lru_.back());
}
}
@@ -245,6 +265,8 @@ HotCacheImpl::promote(CacheNodePtr node) {
// Remove a node from the LRU list and the map
void
HotCacheImpl::remove(ConstCacheNodePtr node) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_REMOVE).
+ arg(node->getNodeName());
lru_.erase(node->lru_entry_);
map_.erase(node->question);
--count_;
@@ -257,6 +279,7 @@ HotCache::HotCache(const int slots) {
// HotCache destructor
HotCache::~HotCache() {
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_CACHE_DESTROY);
delete impl_;
}
@@ -303,18 +326,21 @@ HotCache::retrieve(const Name& n, const RRClass& c, const RRType& t,
std::map<Question, CacheNodePtr>::const_iterator iter;
iter = impl_->map_.find(Question(n, c, t));
if (iter == impl_->map_.end()) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_NOT_FOUND).arg(n);
return (false);
}
CacheNodePtr node = iter->second;
if (node->isValid()) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_FOUND).arg(n);
impl_->promote(node);
rrset = node->getRRset();
flags = node->getFlags();
return (true);
}
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_EXPIRED).arg(n);
impl_->remove(node);
return (false);
}
@@ -328,6 +354,9 @@ HotCache::setSlots(const int slots) {
return;
}
+ logger.info(DATASRC_CACHE_SLOTS).arg(slots).arg(max(0, impl_->count_ -
+ slots));
+
while (impl_->slots_ != 0 && impl_->count_ > impl_->slots_) {
impl_->remove(impl_->lru_.back());
}
@@ -343,6 +372,11 @@ HotCache::getSlots() const {
void
HotCache::setEnabled(const bool e) {
impl_->enabled_ = e;
+ if (e) {
+ logger.info(DATASRC_CACHE_ENABLE);
+ } else {
+ logger.info(DATASRC_CACHE_DISABLE);
+ }
}
/// Indicate whether the cache is enabled
diff --git a/src/lib/datasrc/data_source.cc b/src/lib/datasrc/data_source.cc
index 0ee656f..b57a967 100644
--- a/src/lib/datasrc/data_source.cc
+++ b/src/lib/datasrc/data_source.cc
@@ -25,16 +25,18 @@
#include <datasrc/cache.h>
#include <datasrc/data_source.h>
#include <datasrc/query.h>
+#include <datasrc/logger.h>
+
+#include <util/encode/base32hex.h>
+#include <util/hash/sha1.h>
+#include <util/buffer.h>
-#include <dns/util/base32hex.h>
-#include <dns/buffer.h>
#include <dns/message.h>
#include <dns/name.h>
#include <dns/rcode.h>
#include <dns/rdataclass.h>
#include <dns/rrset.h>
#include <dns/rrsetlist.h>
-#include <dns/util/sha1.h>
#include <cc/data.h>
@@ -45,9 +47,34 @@
} while (0)
using namespace std;
+using namespace isc::util;
+using namespace isc::util::encode;
+using namespace isc::util::hash;
using namespace isc::dns;
using namespace isc::dns::rdata;
+namespace {
+
+struct MatchRRsetForType {
+ MatchRRsetForType(const RRType rrtype) : rrtype_(rrtype) {}
+ bool operator()(RRsetPtr rrset) {
+ return (rrset->getType() == rrtype_);
+ }
+ const RRType rrtype_;
+};
+
+// This is a helper to retrieve a specified RR type of RRset from RRsetList.
+// In our case the data source search logic should ensure that the class is
+// valid. We use this find logic of our own so that we can support both
+// specific RR class queries (normal case) and class ANY queries.
+RRsetPtr
+findRRsetFromList(RRsetList& list, const RRType rrtype) {
+ RRsetList::iterator it(find_if(list.begin(), list.end(),
+ MatchRRsetForType(rrtype)));
+ return (it != list.end() ? *it : RRsetPtr());
+}
+}
+
namespace isc {
namespace datasrc {
@@ -57,7 +84,7 @@ class ZoneInfo {
public:
ZoneInfo(DataSrc* ts,
const isc::dns::Name& n,
- const isc::dns::RRClass& c,
+ const isc::dns::RRClass& c,
const isc::dns::RRType& t = isc::dns::RRType::ANY()) :
top_source_(ts),
dsm_(((t == RRType::DS() && n.getLabelCount() != 1)
@@ -97,6 +124,8 @@ getAdditional(Query& q, ConstRRsetPtr rrset) {
const Rdata& rd(it->getCurrent());
if (rrset->getType() == RRType::NS()) {
const generic::NS& ns = dynamic_cast<const generic::NS&>(rd);
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_GET_NS_ADDITIONAL).
+ arg(ns.getNSName()).arg(rrset->getName());
q.tasks().push(QueryTaskPtr(
new QueryTask(q, ns.getNSName(),
Message::SECTION_ADDITIONAL,
@@ -104,6 +133,8 @@ getAdditional(Query& q, ConstRRsetPtr rrset) {
QueryTask::GETADDITIONAL)));
} else if (rrset->getType() == RRType::MX()) {
const generic::MX& mx = dynamic_cast<const generic::MX&>(rd);
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_GET_MX_ADDITIONAL).
+ arg(mx.getMXName()).arg(rrset->getName());
q.tasks().push(QueryTaskPtr(
new QueryTask(q, mx.getMXName(),
Message::SECTION_ADDITIONAL,
@@ -117,11 +148,14 @@ getAdditional(Query& q, ConstRRsetPtr rrset) {
// understand DNAME
void
synthesizeCname(QueryTaskPtr task, RRsetPtr rrset, RRsetList& target) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_SYNTH_CNAME).
+ arg(rrset->getName());
RdataIteratorPtr it = rrset->getRdataIterator();
// More than one DNAME RR in the RRset is illegal, so we only have
// to process the first one.
if (it->isLast()) {
+ logger.error(DATASRC_QUERY_EMPTY_DNAME).arg(rrset->getName());
return;
}
@@ -129,7 +163,7 @@ synthesizeCname(QueryTaskPtr task, RRsetPtr rrset, RRsetList& target) {
const generic::DNAME& dname = dynamic_cast<const generic::DNAME&>(rd);
const Name& dname_target(dname.getDname());
- RRsetPtr cname(new RRset(task->qname, task->qclass, RRType::CNAME(),
+ RRsetPtr cname(new RRset(task->qname, rrset->getClass(), RRType::CNAME(),
rrset->getTTL()));
const int qnlen = task->qname.getLabelCount();
@@ -145,16 +179,20 @@ synthesizeCname(QueryTaskPtr task, RRsetPtr rrset, RRsetList& target) {
// to by a CNAME record
void
chaseCname(Query& q, QueryTaskPtr task, RRsetPtr rrset) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_FOLLOW_CNAME).
+ arg(rrset->getName());
RdataIteratorPtr it = rrset->getRdataIterator();
// More than one CNAME RR in the RRset is illegal, so we only have
// to process the first one.
if (it->isLast()) {
+ logger.error(DATASRC_QUERY_EMPTY_CNAME).arg(rrset->getName());
return;
}
// Stop chasing CNAMES after 16 lookups, to prevent loops
if (q.tooMany()) {
+ logger.error(DATASRC_QUERY_TOO_MANY_CNAMES).arg(rrset->getName());
return;
}
@@ -168,6 +206,8 @@ chaseCname(Query& q, QueryTaskPtr task, RRsetPtr rrset) {
// Check the cache for data which can answer the current query task.
bool
checkCache(QueryTask& task, RRsetList& target) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_CHECK_CACHE).
+ arg(task.qname).arg(task.qtype);
HotCache& cache = task.q.getCache();
RRsetList rrsets;
RRsetPtr rrset;
@@ -180,6 +220,9 @@ checkCache(QueryTask& task, RRsetList& target) {
// ANY queries must be handled by the low-level data source,
// or the results won't be guaranteed to be complete
if (task.qtype == RRType::ANY() || task.qclass == RRClass::ANY()) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA,
+ DATASRC_QUERY_NO_CACHE_ANY_SIMPLE).arg(task.qname).
+ arg(task.qtype).arg(task.qclass);
break;
}
@@ -209,6 +252,8 @@ checkCache(QueryTask& task, RRsetList& target) {
case QueryTask::AUTH_QUERY: // Find exact RRset or CNAME
if (task.qtype == RRType::ANY() || task.qclass == RRClass::ANY()) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_NO_CACHE_ANY_AUTH).
+ arg(task.qname).arg(task.qtype).arg(task.qclass);
break;
}
@@ -327,30 +372,43 @@ DataSrc::Result
doQueryTask(QueryTask& task, ZoneInfo& zoneinfo, RRsetList& target) {
HotCache& cache = task.q.getCache();
RRsetPtr rrset;
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_DO_QUERY).arg(task.qname).
+ arg(task.qtype);
+
+ // First off, make sure at least we have a matching zone in some data
+ // source. We must do this before checking the cache, because it can
+ // happen that the matching zone has been removed after an RRset of that
+ // zone is cached. Such inconsistency will cause various problems,
+ // including a crash.
+ const DataSrc* ds = zoneinfo.getDataSource();
+ const Name* const zonename = zoneinfo.getEnclosingZone();
+ if (ds == NULL) {
+ task.flags |= DataSrc::NO_SUCH_ZONE;
+ logger.info(DATASRC_QUERY_NO_ZONE).arg(task.qname).arg(task.qclass);
+ return (DataSrc::SUCCESS);
+ }
- // First, check the cache for matching data
+ // Then check the cache for matching data
if (checkCache(task, target)) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_CACHED).
+ arg(task.qname).arg(task.qtype);
return (DataSrc::SUCCESS);
}
// Requested data weren't in the cache (or were, but had expired),
// so now we proceed with the low-level data source lookup, and cache
// whatever we find.
- const DataSrc* ds = zoneinfo.getDataSource();
- const Name* const zonename = zoneinfo.getEnclosingZone();
-
- if (ds == NULL) {
- task.flags |= DataSrc::NO_SUCH_ZONE;
- return (DataSrc::SUCCESS);
- }
DataSrc::Result result;
switch (task.op) {
case QueryTask::SIMPLE_QUERY:
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_IS_SIMPLE).
+ arg(task.qname).arg(task.qtype);
result = ds->findExactRRset(task.qname, task.qclass, task.qtype,
target, task.flags, zonename);
if (result != DataSrc::SUCCESS) {
+ logger.error(DATASRC_QUERY_SIMPLE_FAIL).arg(result);
return (result);
}
@@ -372,10 +430,13 @@ doQueryTask(QueryTask& task, ZoneInfo& zoneinfo, RRsetList& target) {
return (result);
case QueryTask::AUTH_QUERY:
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_IS_AUTH).
+ arg(task.qname).arg(task.qtype);
result = ds->findRRset(task.qname, task.qclass, task.qtype,
target, task.flags, zonename);
if (result != DataSrc::SUCCESS) {
+ logger.error(DATASRC_QUERY_AUTH_FAIL).arg(result);
return (result);
}
@@ -408,10 +469,16 @@ doQueryTask(QueryTask& task, ZoneInfo& zoneinfo, RRsetList& target) {
case QueryTask::GLUE_QUERY:
case QueryTask::NOGLUE_QUERY:
+ LOG_DEBUG(logger, DBG_TRACE_DATA, task.op == QueryTask::GLUE_QUERY ?
+ DATASRC_QUERY_IS_GLUE : DATASRC_QUERY_IS_NOGLUE).
+ arg(task.qname).arg(task.qtype);
result = ds->findAddrs(task.qname, task.qclass, target,
task.flags, zonename);
if (result != DataSrc::SUCCESS) {
+ logger.error(task.op == QueryTask::GLUE_QUERY ?
+ DATASRC_QUERY_GLUE_FAIL : DATASRC_QUERY_NOGLUE_FAIL).
+ arg(result);
return (result);
}
@@ -437,10 +504,13 @@ doQueryTask(QueryTask& task, ZoneInfo& zoneinfo, RRsetList& target) {
return (result);
case QueryTask::REF_QUERY:
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_IS_REF).
+ arg(task.qname).arg(task.qtype);
result = ds->findReferral(task.qname, task.qclass, target,
task.flags, zonename);
if (result != DataSrc::SUCCESS) {
+ logger.error(DATASRC_QUERY_REF_FAIL).arg(result);
return (result);
}
@@ -474,6 +544,7 @@ doQueryTask(QueryTask& task, ZoneInfo& zoneinfo, RRsetList& target) {
}
// Not reached
+ logger.error(DATASRC_QUERY_INVALID_OP);
return (DataSrc::ERROR);
}
@@ -485,6 +556,8 @@ inline void
addToMessage(Query& q, const Message::Section sect, RRsetPtr rrset,
bool no_dnssec = false)
{
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_ADD_RRSET).
+ arg(rrset->getName()).arg(rrset->getType());
Message& m = q.message();
if (no_dnssec) {
if (rrset->getType() == RRType::RRSIG() ||
@@ -503,6 +576,7 @@ addToMessage(Query& q, const Message::Section sect, RRsetPtr rrset,
// Copy referral information into the authority section of a message
inline void
copyAuth(Query& q, RRsetList& auth) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_COPY_AUTH);
BOOST_FOREACH(RRsetPtr rrset, auth) {
if (rrset->getType() == RRType::DNAME()) {
continue;
@@ -540,6 +614,9 @@ refQuery(const Query& q, const Name& name, ZoneInfo& zoneinfo,
// they'll be handled in a normal lookup in the zone.
inline bool
hasDelegation(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_DELEGATION).
+ arg(task->qname);
+
const Name* const zonename = zoneinfo.getEnclosingZone();
if (zonename == NULL) {
if (task->state == QueryTask::GETANSWER) {
@@ -569,17 +646,17 @@ hasDelegation(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo) {
// Found a referral while getting answer data;
// send a delegation.
if (found) {
- RRsetPtr r = ref.findRRset(RRType::DNAME(), q.qclass());
+ RRsetPtr r = findRRsetFromList(ref, RRType::DNAME());
if (r != NULL) {
RRsetList syn;
addToMessage(q, Message::SECTION_ANSWER, r);
q.message().setHeaderFlag(Message::HEADERFLAG_AA);
synthesizeCname(task, r, syn);
if (syn.size() == 1) {
- addToMessage(q, Message::SECTION_ANSWER,
- syn.findRRset(RRType::CNAME(), q.qclass()));
- chaseCname(q, task, syn.findRRset(RRType::CNAME(),
- q.qclass()));
+ RRsetPtr cname_rrset = findRRsetFromList(syn,
+ RRType::CNAME());
+ addToMessage(q, Message::SECTION_ANSWER, cname_rrset);
+ chaseCname(q, task, cname_rrset);
return (true);
}
}
@@ -605,6 +682,7 @@ addSOA(Query& q, ZoneInfo& zoneinfo) {
RRsetList soa;
const Name* const zonename = zoneinfo.getEnclosingZone();
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_ADD_SOA).arg(*zonename);
QueryTask newtask(q, *zonename, RRType::SOA(), QueryTask::SIMPLE_QUERY);
RETERR(doQueryTask(newtask, zoneinfo, soa));
if (newtask.flags != 0) {
@@ -612,19 +690,20 @@ addSOA(Query& q, ZoneInfo& zoneinfo) {
}
addToMessage(q, Message::SECTION_AUTHORITY,
- soa.findRRset(RRType::SOA(), q.qclass()));
+ findRRsetFromList(soa, RRType::SOA()));
return (DataSrc::SUCCESS);
}
inline DataSrc::Result
addNSEC(Query& q, const Name& name, ZoneInfo& zoneinfo) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_ADD_NSEC).arg(name);
RRsetList nsec;
QueryTask newtask(q, name, RRType::NSEC(), QueryTask::SIMPLE_QUERY);
RETERR(doQueryTask(newtask, zoneinfo, nsec));
if (newtask.flags == 0) {
addToMessage(q, Message::SECTION_AUTHORITY,
- nsec.findRRset(RRType::NSEC(), q.qclass()));
+ findRRsetFromList(nsec, RRType::NSEC()));
}
return (DataSrc::SUCCESS);
@@ -634,9 +713,11 @@ inline DataSrc::Result
getNsec3(Query& q, ZoneInfo& zoneinfo, string& hash, RRsetPtr& target) {
const DataSrc* ds = zoneinfo.getDataSource();
const Name* const zonename = zoneinfo.getEnclosingZone();
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_ADD_NSEC3).arg(*zonename);
if (ds == NULL) {
q.message().setRcode(Rcode::SERVFAIL());
+ logger.error(DATASRC_QUERY_NO_DS_NSEC3).arg(*zonename);
return (DataSrc::ERROR);
}
@@ -739,6 +820,7 @@ proveNX(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, const bool wildcard) {
const DataSrc* ds = zoneinfo.getDataSource();
if (ds == NULL) {
m.setRcode(Rcode::SERVFAIL());
+ logger.error(DATASRC_QUERY_NO_DS_NSEC).arg(*zonename);
return (DataSrc::ERROR);
}
ds->findPreviousName(task->qname, nsecname, zonename);
@@ -767,6 +849,7 @@ proveNX(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, const bool wildcard) {
// Attempt a wildcard lookup
inline DataSrc::Result
tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_WILDCARD).arg(task->qname);
Message& m = q.message();
DataSrc::Result result;
found = false;
@@ -820,6 +903,8 @@ tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) {
result = proveNX(q, task, zoneinfo, true);
if (result != DataSrc::SUCCESS) {
m.setRcode(Rcode::SERVFAIL());
+ logger.error(DATASRC_QUERY_WILDCARD_PROVENX_FAIL).
+ arg(task->qname).arg(result);
return (DataSrc::ERROR);
}
}
@@ -828,7 +913,7 @@ tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) {
// match the qname), and then continue as if this were a normal
// answer: if a CNAME, chase the target, otherwise add authority.
if (cname) {
- RRsetPtr rrset = wild.findRRset(RRType::CNAME(), q.qclass());
+ RRsetPtr rrset = findRRsetFromList(wild, RRType::CNAME());
if (rrset != NULL) {
rrset->setName(task->qname);
addToMessage(q, Message::SECTION_ANSWER, rrset);
@@ -842,6 +927,8 @@ tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) {
RRsetList auth;
if (!refQuery(q, *zonename, zoneinfo, auth)) {
+ logger.error(DATASRC_QUERY_WILDCARD_REFERRAL).arg(task->qname).
+ arg(result);
return (DataSrc::ERROR);
}
@@ -857,6 +944,8 @@ tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) {
//
void
DataSrc::doQuery(Query& q) {
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_QUERY_PROCESS).arg(q.qname()).
+ arg(q.qtype()).arg(q.qclass());
Message& m = q.message();
vector<RRsetPtr> additional;
@@ -874,6 +963,7 @@ DataSrc::doQuery(Query& q) {
// Can't query directly for RRSIG.
if (task->qtype == RRType::RRSIG()) {
m.setRcode(Rcode::REFUSED());
+ logger.warn(DATASRC_QUERY_RRSIG).arg(task->qname);
return;
}
@@ -881,6 +971,7 @@ DataSrc::doQuery(Query& q) {
if (task->op == QueryTask::SIMPLE_QUERY ||
task->op == QueryTask::REF_QUERY) {
m.setRcode(Rcode::SERVFAIL());
+ logger.error(DATASRC_QUERY_MISPLACED_TASK);
return;
}
@@ -900,6 +991,7 @@ DataSrc::doQuery(Query& q) {
result = doQueryTask(*task, zoneinfo, data);
if (result != SUCCESS) {
m.setRcode(Rcode::SERVFAIL());
+ logger.error(DATASRC_QUERY_TASK_FAIL).arg(result);
return;
}
@@ -909,6 +1001,7 @@ DataSrc::doQuery(Query& q) {
if (task->flags == NO_SUCH_ZONE) {
if (task->state == QueryTask::GETANSWER) {
m.setRcode(Rcode::REFUSED());
+ // No need to log it here, it was already logged in doQueryTask
return;
}
continue;
@@ -923,7 +1016,7 @@ DataSrc::doQuery(Query& q) {
((task->qtype == RRType::NSEC() ||
task->qtype == RRType::DS() ||
task->qtype == RRType::DNAME()) &&
- data.findRRset(task->qtype, task->qclass)))) {
+ findRRsetFromList(data, task->qtype)))) {
task->flags &= ~REFERRAL;
}
@@ -948,9 +1041,9 @@ DataSrc::doQuery(Query& q) {
// Add the NS records for the enclosing zone to
// the authority section.
RRsetList auth;
- const DataSrc* ds = zoneinfo.getDataSource();
- if (!refQuery(q, Name(*zonename), zoneinfo, auth) ||
- !auth.findRRset(RRType::NS(), ds->getClass())) {
+ if (!refQuery(q, Name(*zonename), zoneinfo, auth) ||
+ !findRRsetFromList(auth, RRType::NS())) {
+ logger.error(DATASRC_QUERY_MISSING_NS).arg(*zonename);
isc_throw(DataSourceError,
"NS RR not found in " << *zonename << "/" <<
q.qclass());
@@ -975,15 +1068,17 @@ DataSrc::doQuery(Query& q) {
continue;
default:
+ logger.error(DATASRC_UNEXPECTED_QUERY_STATE);
isc_throw (Unexpected, "unexpected query state");
}
} else if (result == ERROR || result == NOT_IMPLEMENTED) {
m.setRcode(Rcode::SERVFAIL());
+ logger.error(DATASRC_QUERY_FAIL);
return;
} else if ((task->flags & CNAME_FOUND) != 0) {
// The qname node contains a CNAME. Add a new task to the
// queue to look up its target.
- RRsetPtr rrset = data.findRRset(RRType::CNAME(), q.qclass());
+ RRsetPtr rrset = findRRsetFromList(data, RRType::CNAME());
if (rrset != NULL) {
addToMessage(q, task->section, rrset);
chaseCname(q, task, rrset);
@@ -996,6 +1091,7 @@ DataSrc::doQuery(Query& q) {
m.setHeaderFlag(Message::HEADERFLAG_AA, false);
if (!refQuery(q, task->qname, zoneinfo, auth)) {
m.setRcode(Rcode::SERVFAIL());
+ logger.error(DATASRC_QUERY_BAD_REFERRAL).arg(task->qname);
return;
}
BOOST_FOREACH (RRsetPtr rrset, auth) {
@@ -1013,6 +1109,13 @@ DataSrc::doQuery(Query& q) {
continue;
} else if ((task->flags & (NAME_NOT_FOUND|TYPE_NOT_FOUND)) != 0) {
// No data found at this qname/qtype.
+
+ // If we were looking for additional data, we should simply
+ // ignore this result.
+ if (task->state == QueryTask::GETADDITIONAL) {
+ continue;
+ }
+
// If we were looking for answer data, not additional,
// and the name was not found, we need to find out whether
// there are any relevant wildcards.
@@ -1020,6 +1123,7 @@ DataSrc::doQuery(Query& q) {
result = tryWildcard(q, task, zoneinfo, wildcard_found);
if (result != SUCCESS) {
m.setRcode(Rcode::SERVFAIL());
+ logger.error(DATASRC_QUERY_WILDCARD_FAIL).arg(task->qname);
return;
}
@@ -1041,6 +1145,7 @@ DataSrc::doQuery(Query& q) {
result = addSOA(q, zoneinfo);
if (result != SUCCESS) {
+ logger.error(DATASRC_QUERY_MISSING_SOA).arg(*zonename);
isc_throw(DataSourceError,
"SOA RR not found in " << *zonename <<
"/" << q.qclass());
@@ -1057,6 +1162,7 @@ DataSrc::doQuery(Query& q) {
result = proveNX(q, task, zoneinfo, false);
if (result != DataSrc::SUCCESS) {
m.setRcode(Rcode::SERVFAIL());
+ logger.error(DATASRC_QUERY_PROVENX_FAIL).arg(task->qname);
return;
}
}
@@ -1065,6 +1171,7 @@ DataSrc::doQuery(Query& q) {
} else {
// Should never be reached!
m.setRcode(Rcode::SERVFAIL());
+ logger.error(DATASRC_QUERY_UNKNOWN_RESULT);
return;
}
}
@@ -1160,7 +1267,10 @@ DataSrc::findReferral(const Name& qname, const RRClass& qclass,
void
MetaDataSrc::addDataSrc(ConstDataSrcPtr data_src) {
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_META_ADD);
if (getClass() != RRClass::ANY() && data_src->getClass() != getClass()) {
+ logger.error(DATASRC_META_ADD_CLASS_MISMATCH).
+ arg(data_src->getClass()).arg(getClass());
isc_throw(Unexpected, "class mismatch");
}
@@ -1169,6 +1279,7 @@ MetaDataSrc::addDataSrc(ConstDataSrcPtr data_src) {
void
MetaDataSrc::removeDataSrc(ConstDataSrcPtr data_src) {
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_META_REMOVE);
std::vector<ConstDataSrcPtr>::iterator it, itr;
for (it = data_sources.begin(); it != data_sources.end(); ++it) {
if (*it == data_src) {
diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes
new file mode 100644
index 0000000..c692364
--- /dev/null
+++ b/src/lib/datasrc/datasrc_messages.mes
@@ -0,0 +1,493 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+$NAMESPACE isc::datasrc
+
+# \brief Messages for the data source library
+
+% DATASRC_CACHE_CREATE creating the hotspot cache
+Debug information that the hotspot cache was created at startup.
+
+% DATASRC_CACHE_DESTROY destroying the hotspot cache
+Debug information. The hotspot cache is being destroyed.
+
+% DATASRC_CACHE_DISABLE disabling the cache
+The hotspot cache is disabled from now on. It is not going to store
+information or return anything.
+
+% DATASRC_CACHE_ENABLE enabling the cache
+The hotspot cache is enabled from now on.
+
+% DATASRC_CACHE_EXPIRED the item '%1' is expired
+Debug information. There was an attempt to look up an item in the hotspot
+cache. And the item was actually there, but it was too old, so it was removed
+instead and nothing is reported (the external behaviour is the same as with
+CACHE_NOT_FOUND).
+
+% DATASRC_CACHE_FOUND the item '%1' was found
+Debug information. An item was successfully looked up in the hotspot cache.
+
+% DATASRC_CACHE_FULL cache is full, dropping oldest
+Debug information. After inserting an item into the hotspot cache, the
+maximum number of items was exceeded, so the least recently used item will
+be dropped. This should be directly followed by CACHE_REMOVE.
+
+% DATASRC_CACHE_INSERT inserting item '%1' into the cache
+Debug information. It means a new item is being inserted into the hotspot
+cache.
+
+% DATASRC_CACHE_NOT_FOUND the item '%1' was not found
+Debug information. It was attempted to look up an item in the hotspot cache,
+but it is not there.
+
+% DATASRC_CACHE_OLD_FOUND older instance of cache item found, replacing
+Debug information. While inserting an item into the hotspot cache, an older
+instance of an item with the same name was found. The old instance will be
+removed. This should be directly followed by CACHE_REMOVE.
+
+% DATASRC_CACHE_REMOVE removing '%1' from the cache
+Debug information. An item is being removed from the hotspot cache.
+
+% DATASRC_CACHE_SLOTS setting the cache size to '%1', dropping '%2' items
+The maximum allowed number of items of the hotspot cache is set to the given
+number. If there are too many, some of them will be dropped. The size of 0
+means no limit.
+
+% DATASRC_DO_QUERY handling query for '%1/%2'
+Debug information. We're processing some internal query for given name and
+type.
+
+% DATASRC_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3'
+Debug information. An RRset is being added to the in-memory data source.
+
+% DATASRC_MEM_ADD_WILDCARD adding wildcards for '%1'
+Debug information. Some special marks above each * in wildcard name are needed.
+They are being added now for this name.
+
+% DATASRC_MEM_ADD_ZONE adding zone '%1/%2'
+Debug information. A zone is being added into the in-memory data source.
+
+% DATASRC_MEM_ANY_SUCCESS ANY query for '%1' successful
+Debug information. The domain was found and an ANY type query is being answered
+by providing everything found inside the domain.
+
+% DATASRC_MEM_CNAME CNAME at the domain '%1'
+Debug information. The requested domain is an alias to a different domain,
+returning the CNAME instead.
+
+% DATASRC_MEM_CNAME_COEXIST can't add data to CNAME in domain '%1'
+This is the same problem as in MEM_CNAME_TO_NONEMPTY, but it happened the
+other way around -- adding some other data to CNAME.
+
+% DATASRC_MEM_CNAME_TO_NONEMPTY can't add CNAME to domain with other data in '%1'
+Someone or something tried to add a CNAME into a domain that already contains
+some other data. But the protocol forbids coexistence of CNAME with anything
+(RFC 1034, section 3.6.2). This indicates a problem with provided data.
+
+% DATASRC_MEM_CREATE creating zone '%1' in '%2' class
+Debug information. A representation of a zone for the in-memory data source is
+being created.
+
+% DATASRC_MEM_DELEG_FOUND delegation found at '%1'
+Debug information. A delegation point was found above the requested record.
+
+% DATASRC_MEM_DESTROY destroying zone '%1' in '%2' class
+Debug information. A zone from in-memory data source is being destroyed.
+
+% DATASRC_MEM_DNAME_ENCOUNTERED encountered a DNAME
+Debug information. While searching for the requested domain, a DNAME was
+encountered on the way. This may lead to redirection to a different domain and
+stop the search.
+
+% DATASRC_MEM_DNAME_FOUND DNAME found at '%1'
+Debug information. A DNAME was found instead of the requested information.
+
+% DATASRC_MEM_DNAME_NS DNAME and NS can't coexist in non-apex domain '%1'
+It was requested for DNAME and NS records to be put into the same domain
+which is not the apex (the top of the zone). This is forbidden by RFC
+2672, section 3. This indicates a problem with provided data.
+
+% DATASRC_MEM_DOMAIN_EMPTY requested domain '%1' is empty
+Debug information. The requested domain exists in the tree of domains, but
+it is empty. Therefore it doesn't contain the requested resource type.
+
+% DATASRC_MEM_DUP_RRSET duplicate RRset '%1/%2'
+An RRset is being inserted into in-memory data source for a second time. The
+original version must be removed first. Note that loading master files where an
+RRset is split into multiple locations is not supported yet.
+
+% DATASRC_MEM_EXACT_DELEGATION delegation at the exact domain '%1'
+Debug information. There's a NS record at the requested domain. This means
+this zone is not authoritative for the requested domain, but a delegation
+should be followed. The requested domain is an apex of some zone.
+
+% DATASRC_MEM_FIND find '%1/%2'
+Debug information. A search for the requested RRset is being started.
+
+% DATASRC_MEM_FIND_ZONE looking for zone '%1'
+Debug information. A zone object for this zone is being searched for in the
+in-memory data source.
+
+% DATASRC_MEM_LOAD loading zone '%1' from file '%2'
+Debug information. The content of master file is being loaded into the memory.
+
+% DATASRC_MEM_NOTFOUND requested domain '%1' not found
+Debug information. The requested domain does not exist.
+
+% DATASRC_MEM_NS_ENCOUNTERED encountered a NS
+Debug information. While searching for the requested domain, a NS was
+encountered on the way (a delegation). This may lead to stop of the search.
+
+% DATASRC_MEM_NXRRSET no such type '%1' at '%2'
+Debug information. The domain exists, but it doesn't hold any record of the
+requested type.
+
+% DATASRC_MEM_OUT_OF_ZONE domain '%1' doesn't belong to zone '%2'
+It was attempted to add the domain into a zone that shouldn't have it
+(eg. the domain is not subdomain of the zone origin). This indicates a
+problem with provided data.
+
+% DATASRC_MEM_RENAME renaming RRset from '%1' to '%2'
+Debug information. A RRset is being generated from a different RRset (most
+probably a wildcard). So it must be renamed to whatever the user asked for. In
+fact, it's impossible to rename RRsets with our libraries, so a new one is
+created and all resource records are copied over.
+
+% DATASRC_MEM_SINGLETON trying to add multiple RRs for domain '%1' and type '%2'
+Some resource types are singletons -- only one is allowed in a domain
+(for example CNAME or SOA). This indicates a problem with provided data.
+
+% DATASRC_MEM_SUCCESS query for '%1/%2' successful
+Debug information. The requested record was found.
+
+% DATASRC_MEM_SUPER_STOP stopped at superdomain '%1', domain '%2' is empty
+Debug information. The search stopped at a superdomain of the requested
+domain. The domain is a empty nonterminal, therefore it is treated as NXRRSET
+case (eg. the domain exists, but it doesn't have the requested record type).
+
+% DATASRC_MEM_SWAP swapping contents of two zone representations ('%1' and '%2')
+Debug information. The contents of two in-memory zones are being exchanged.
+This is usual practice to do some manipulation in exception-safe manner -- the
+new data are prepared in a different zone object and when it works, they are
+swapped. The old one contains the new data and the other one can be safely
+destroyed.
+
+% DATASRC_MEM_WILDCARD_CANCEL wildcard match canceled for '%1'
+Debug information. A domain above wildcard was reached, but there's something
+below the requested domain. Therefore the wildcard doesn't apply here. This
+behaviour is specified by RFC 1034, section 4.3.3
+
+% DATASRC_MEM_WILDCARD_DNAME DNAME record in wildcard domain '%1'
+The software refuses to load DNAME records into a wildcard domain. It isn't
+explicitly forbidden, but the protocol is ambiguous about how this should
+behave and BIND 9 refuses that as well. Please describe your intention using
+different tools.
+
+% DATASRC_MEM_WILDCARD_NS NS record in wildcard domain '%1'
+The software refuses to load NS records into a wildcard domain. It isn't
+explicitly forbidden, but the protocol is ambiguous about how this should
+behave and BIND 9 refuses that as well. Please describe your intention using
+different tools.
+
+% DATASRC_META_ADD adding a data source into meta data source
+Debug information. Yet another data source is being added into the meta data
+source. (probably at startup or reconfiguration)
+
+% DATASRC_META_ADD_CLASS_MISMATCH mismatch between classes '%1' and '%2'
+It was attempted to add a data source into a meta data source. But their
+classes do not match.
+
+% DATASRC_META_REMOVE removing data source from meta data source
+Debug information. A data source is being removed from meta data source.
+
+% DATASRC_QUERY_ADD_NSEC adding NSEC record for '%1'
+Debug information. A NSEC record covering this zone is being added.
+
+% DATASRC_QUERY_ADD_NSEC3 adding NSEC3 record of zone '%1'
+Debug information. A NSEC3 record for the given zone is being added to the
+response message.
+
+% DATASRC_QUERY_ADD_RRSET adding RRset '%1/%2' to message
+Debug information. An RRset is being added to the response message.
+
+% DATASRC_QUERY_ADD_SOA adding SOA of '%1'
+Debug information. A SOA record of the given zone is being added to the
+authority section of the response message.
+
+% DATASRC_QUERY_AUTH_FAIL the underlying data source failed with %1
+The underlying data source failed to answer the authoritative query. 1 means
+some error, 2 is not implemented. The data source should have logged the
+specific error already.
+
+% DATASRC_QUERY_BAD_REFERRAL bad referral to '%1'
+The domain lives in another zone. But it is not possible to generate referral
+information for it.
+
+% DATASRC_QUERY_CACHED data for %1/%2 found in cache
+Debug information. The requested data were found in the hotspot cache, so
+no query is sent to the real data source.
+
+% DATASRC_QUERY_CHECK_CACHE checking cache for '%1/%2'
+Debug information. While processing a query, lookup to the hotspot cache
+is being made.
+
+% DATASRC_QUERY_COPY_AUTH copying authoritative section into message
+Debug information. The whole referral information is being copied into the
+response message.
+
+% DATASRC_QUERY_DELEGATION looking for delegation on the path to '%1'
+Debug information. The software is trying to identify delegation points on the
+way down to the given domain.
+
+% DATASRC_QUERY_EMPTY_CNAME CNAME at '%1' is empty
+There was an CNAME and it was being followed. But it contains no records,
+so there's nowhere to go. There will be no answer. This indicates a problem
+with supplied data.
+We tried to follow
+
+% DATASRC_QUERY_EMPTY_DNAME the DNAME on '%1' is empty
+During an attempt to synthesize CNAME from this DNAME it was discovered the
+DNAME is empty (it has no records). This indicates problem with supplied data.
+
+% DATASRC_QUERY_FAIL query failed
+Some subtask of query processing failed. The reason should have been reported
+already. We are returning SERVFAIL.
+
+% DATASRC_QUERY_FOLLOW_CNAME following CNAME at '%1'
+Debug information. The domain is a CNAME (or a DNAME and we created a CNAME
+for it already), so it's being followed.
+
+% DATASRC_QUERY_GET_MX_ADDITIONAL addition of A/AAAA for '%1' requested by MX '%2'
+Debug information. While processing a query, a MX record was met. It
+references the mentioned address, so A/AAAA records for it are looked up
+and put it into the additional section.
+
+% DATASRC_QUERY_GET_NS_ADDITIONAL addition of A/AAAA for '%1' requested by NS '%2'
+Debug information. While processing a query, a NS record was met. It
+references the mentioned address, so A/AAAA records for it are looked up
+and put it into the additional section.
+
+% DATASRC_QUERY_GLUE_FAIL the underlying data source failed with %1
+The underlying data source failed to answer the glue query. 1 means some error,
+2 is not implemented. The data source should have logged the specific error
+already.
+
+% DATASRC_QUERY_INVALID_OP invalid query operation requested
+This indicates a programmer error. The DO_QUERY was called with unknown
+operation code.
+
+% DATASRC_QUERY_IS_AUTH auth query (%1/%2)
+Debug information. The last DO_QUERY is an auth query.
+
+% DATASRC_QUERY_IS_GLUE glue query (%1/%2)
+Debug information. The last DO_QUERY is query for glue addresses.
+
+% DATASRC_QUERY_IS_NOGLUE query for non-glue addresses (%1/%2)
+Debug information. The last DO_QUERY is query for addresses that are not
+glue.
+
+% DATASRC_QUERY_IS_REF query for referral (%1/%2)
+Debug information. The last DO_QUERY is query for referral information.
+
+% DATASRC_QUERY_IS_SIMPLE simple query (%1/%2)
+Debug information. The last DO_QUERY is a simple query.
+
+% DATASRC_QUERY_MISPLACED_TASK task of this type should not be here
+This indicates a programming error. A task was found in the internal task
+queue, but this kind of task wasn't designed to be inside the queue (it should
+be handled right away, not queued).
+
+% DATASRC_QUERY_MISSING_NS missing NS records for '%1'
+NS records should have been put into the authority section. However, this zone
+has none. This indicates problem with provided data.
+
+% DATASRC_QUERY_MISSING_SOA the zone '%1' has no SOA
+The answer should have been a negative one (eg. of nonexistence of something).
+To do so, a SOA record should be put into the authority section, but the zone
+does not have one. This indicates problem with provided data.
+
+% DATASRC_QUERY_NOGLUE_FAIL the underlying data source failed with %1
+The underlying data source failed to answer the no-glue query. 1 means some
+error, 2 is not implemented. The data source should have logged the specific
+error already.
+
+% DATASRC_QUERY_NO_CACHE_ANY_AUTH ignoring cache for ANY query (%1/%2 in %3 class)
+Debug information. The hotspot cache is ignored for authoritative ANY queries
+for consistency reasons.
+
+% DATASRC_QUERY_NO_CACHE_ANY_SIMPLE ignoring cache for ANY query (%1/%2 in %3 class)
+Debug information. The hotspot cache is ignored for ANY queries for consistency
+reasons.
+
+% DATASRC_QUERY_NO_DS_NSEC there's no DS record in the '%1' zone
+An attempt to add a NSEC record into the message failed, because the zone does
+not have any DS record. This indicates problem with the provided data.
+
+% DATASRC_QUERY_NO_DS_NSEC3 there's no DS record in the '%1' zone
+An attempt to add a NSEC3 record into the message failed, because the zone does
+not have any DS record. This indicates problem with the provided data.
+
+% DATASRC_QUERY_NO_ZONE no zone containing '%1' in class '%2'
+Lookup of domain failed because the data have no zone that contain the
+domain. Maybe someone sent a query to the wrong server for some reason.
+
+% DATASRC_QUERY_PROCESS processing query '%1/%2' in the '%3' class
+Debug information. A sure query is being processed now.
+
+% DATASRC_QUERY_PROVENX_FAIL unable to prove nonexistence of '%1'
+The user wants DNSSEC and we discovered the entity doesn't exist (either
+domain or the record). But there was an error getting NSEC/NSEC3 record
+to prove the nonexistence.
+
+% DATASRC_QUERY_REF_FAIL the underlying data source failed with %1
+The underlying data source failed to answer the query for referral information.
+1 means some error, 2 is not implemented. The data source should have logged
+the specific error already.
+
+% DATASRC_QUERY_RRSIG unable to answer RRSIG query
+The server is unable to answer a direct query for RRSIG type, but was asked
+to do so.
+
+% DATASRC_QUERY_SIMPLE_FAIL the underlying data source failed with %1
+The underlying data source failed to answer the simple query. 1 means some
+error, 2 is not implemented. The data source should have logged the specific
+error already.
+
+% DATASRC_QUERY_SYNTH_CNAME synthesizing CNAME from DNAME on '%1'
+Debug information. While answering a query, a DNAME was met. The DNAME itself
+will be returned, but along with it a CNAME for clients which don't understand
+DNAMEs will be synthesized.
+
+% DATASRC_QUERY_TASK_FAIL task failed with %1
+The query subtask failed. The reason should have been reported by the subtask
+already. The code is 1 for error, 2 for not implemented.
+
+% DATASRC_QUERY_TOO_MANY_CNAMES CNAME chain limit exceeded at '%1'
+A CNAME led to another CNAME and it led to another, and so on. After 16
+CNAMEs, the software gave up. Long CNAME chains are discouraged, and this
+might possibly be a loop as well. Note that some of the CNAMEs might have
+been synthesized from DNAMEs. This indicates problem with supplied data.
+
+% DATASRC_QUERY_UNKNOWN_RESULT unknown result of subtask
+This indicates a programmer error. The answer of subtask doesn't look like
+anything known.
+
+% DATASRC_QUERY_WILDCARD looking for a wildcard covering '%1'
+Debug information. A direct match wasn't found, so a wildcard covering the
+domain is being looked for now.
+
+% DATASRC_QUERY_WILDCARD_FAIL error processing wildcard for '%1'
+During an attempt to cover the domain by a wildcard an error happened. The
+exact kind was hopefully already reported.
+
+% DATASRC_QUERY_WILDCARD_PROVENX_FAIL unable to prove nonexistence of '%1' (%2)
+While processing a wildcard, it wasn't possible to prove nonexistence of the
+given domain or record. The code is 1 for error and 2 for not implemented.
+
+% DATASRC_QUERY_WILDCARD_REFERRAL unable to find referral info for '%1' (%2)
+While processing a wildcard, a referral was met. But it wasn't possible to get
+enough information for it. The code is 1 for error, 2 for not implemented.
+
+% DATASRC_SQLITE_CLOSE closing SQLite database
+Debug information. The SQLite data source is closing the database file.
+% DATASRC_SQLITE_CREATE SQLite data source created
+Debug information. An instance of SQLite data source is being created.
+
+% DATASRC_SQLITE_DESTROY SQLite data source destroyed
+Debug information. An instance of SQLite data source is being destroyed.
+
+% DATASRC_SQLITE_ENCLOSURE looking for zone containing '%1'
+Debug information. The SQLite data source is trying to identify which zone
+should hold this domain.
+
+% DATASRC_SQLITE_ENCLOSURE_NOTFOUND no zone contains it
+Debug information. The last SQLITE_ENCLOSURE query was unsuccessful; there's
+no such zone in our data.
+
+% DATASRC_SQLITE_FIND looking for RRset '%1/%2'
+Debug information. The SQLite data source is looking up a resource record
+set.
+
+% DATASRC_SQLITE_FINDADDRS looking for A/AAAA addresses for '%1'
+Debug information. The data source is looking up the addresses for given
+domain name.
+
+% DATASRC_SQLITE_FINDADDRS_BAD_CLASS class mismatch looking for addresses ('%1' and '%2')
+The SQLite data source was looking up A/AAAA addresses, but the data source
+contains different class than the query was for.
+
+% DATASRC_SQLITE_FINDEXACT looking for exact RRset '%1/%2'
+Debug information. The SQLite data source is looking up an exact resource
+record.
+
+% DATASRC_SQLITE_FINDEXACT_BAD_CLASS class mismatch looking for an RRset ('%1' and '%2')
+The SQLite data source was looking up an exact RRset, but the data source
+contains different class than the query was for.
+
+% DATASRC_SQLITE_FINDREC looking for record '%1/%2'
+Debug information. The SQLite data source is looking up records of given name
+and type in the database.
+
+% DATASRC_SQLITE_FINDREF looking for referral at '%1'
+Debug information. The SQLite data source is identifying if this domain is
+a referral and where it goes.
+
+% DATASRC_SQLITE_FINDREF_BAD_CLASS class mismatch looking for referral ('%1' and '%2')
+The SQLite data source was trying to identify if there's a referral. But
+it contains different class than the query was for.
+
+% DATASRC_SQLITE_FIND_BAD_CLASS class mismatch looking for an RRset ('%1' and '%2')
+The SQLite data source was looking up an RRset, but the data source contains
+different class than the query was for.
+
+% DATASRC_SQLITE_FIND_NSEC3 looking for NSEC3 in zone '%1' for hash '%2'
+Debug information. We're trying to look up a NSEC3 record in the SQLite data
+source.
+
+% DATASRC_SQLITE_FIND_NSEC3_NO_ZONE no such zone '%1'
+The SQLite data source was asked to provide a NSEC3 record for given zone.
+But it doesn't contain that zone.
+
+% DATASRC_SQLITE_OPEN opening SQLite database '%1'
+Debug information. The SQLite data source is loading an SQLite database in
+the provided file.
+
+% DATASRC_SQLITE_PREVIOUS looking for name previous to '%1'
+Debug information. We're trying to look up name preceding the supplied one.
+
+% DATASRC_SQLITE_PREVIOUS_NO_ZONE no zone containing '%1'
+The SQLite data source tried to identify name preceding this one. But this
+one is not contained in any zone in the data source.
+
+% DATASRC_SQLITE_SETUP setting up SQLite database
+The database for SQLite data source was found empty. It is assumed this is the
+first run and it is being initialized with current schema. It'll still contain
+no data, but it will be ready for use.
+
+% DATASRC_STATIC_BAD_CLASS static data source can handle CH only
+For some reason, someone asked the static data source a query that is not in
+the CH class.
+
+% DATASRC_STATIC_CREATE creating the static datasource
+Debug information. The static data source (the one holding stuff like
+version.bind) is being created.
+
+% DATASRC_STATIC_FIND looking for '%1/%2'
+Debug information. This resource record set is being looked up in the static
+data source.
+
+% DATASRC_UNEXPECTED_QUERY_STATE unexpected query state
+This indicates a programming error. An internal task of unknown type was
+generated.
+
diff --git a/src/lib/datasrc/logger.cc b/src/lib/datasrc/logger.cc
new file mode 100644
index 0000000..846e43b
--- /dev/null
+++ b/src/lib/datasrc/logger.cc
@@ -0,0 +1,23 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/logger.h>
+
+namespace isc {
+namespace datasrc {
+
+isc::log::Logger logger("datasrc");
+
+}
+}
diff --git a/src/lib/datasrc/logger.h b/src/lib/datasrc/logger.h
new file mode 100644
index 0000000..ac5d50b
--- /dev/null
+++ b/src/lib/datasrc/logger.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __DATASRC_LOGGER_H
+#define __DATASRC_LOGGER_H
+
+#include <log/macros.h>
+#include <datasrc/datasrc_messages.h>
+
+/// \file logger.h
+/// \brief Data Source library global logger
+///
+/// This holds the logger for the data source library. It is a private header
+/// and should not be included in any publicly used header, only in local
+/// cc files.
+
+namespace isc {
+namespace datasrc {
+
+/// \brief The logger for this library
+extern isc::log::Logger logger;
+
+enum {
+ /// \brief Trace basic operations
+ DBG_TRACE_BASIC = 10,
+ /// \brief Trace data changes and lookups as well
+ DBG_TRACE_DATA = 20,
+ /// \brief Detailed even about how the lookups happen
+ DBG_TRACE_DETAILED = 50
+};
+
+}
+}
+
+#endif
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index 5230ced..b8019a2 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -24,6 +24,7 @@
#include <datasrc/memory_datasrc.h>
#include <datasrc/rbtree.h>
+#include <datasrc/logger.h>
using namespace std;
using namespace isc::dns;
@@ -94,6 +95,8 @@ struct MemoryZone::MemoryZoneImpl {
l > origin_labels;
--l, wname = wname.split(1)) {
if (wname.isWildcard()) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_WILDCARD).
+ arg(name);
// Ensure a separate level exists for the "wildcarding" name,
// and mark the node as "wild".
DomainNode* node;
@@ -130,10 +133,13 @@ struct MemoryZone::MemoryZoneImpl {
// (depending on how we support DNSSEC). We should revisit it
// at that point.
if (!domain->empty()) {
+ LOG_ERROR(logger, DATASRC_MEM_CNAME_TO_NONEMPTY).
+ arg(rrset->getName());
isc_throw(AddError, "CNAME can't be added with other data for "
<< rrset->getName());
}
} else if (domain->find(RRType::CNAME()) != domain->end()) {
+ LOG_ERROR(logger, DATASRC_MEM_CNAME_COEXIST).arg(rrset->getName());
isc_throw(AddError, "CNAME and " << rrset->getType() <<
" can't coexist for " << rrset->getName());
}
@@ -151,6 +157,7 @@ struct MemoryZone::MemoryZoneImpl {
(rrset->getType() == RRType::NS() &&
domain->find(RRType::DNAME()) != domain->end())))
{
+ LOG_ERROR(logger, DATASRC_MEM_DNAME_NS).arg(rrset->getName());
isc_throw(AddError, "DNAME can't coexist with NS in non-apex "
"domain " << rrset->getName());
}
@@ -172,6 +179,8 @@ struct MemoryZone::MemoryZoneImpl {
// XXX: this is not only for CNAME or DNAME. We should generalize
// this code for all other "singleton RR types" (such as SOA) in a
// separate task.
+ LOG_ERROR(logger, DATASRC_MEM_SINGLETON).arg(rrset->getName()).
+ arg(rrset->getType());
isc_throw(AddError, "multiple RRs of singleton type for "
<< rrset->getName());
}
@@ -180,6 +189,8 @@ struct MemoryZone::MemoryZoneImpl {
if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
compare.getRelation() != NameComparisonResult::EQUAL)
{
+ LOG_ERROR(logger, DATASRC_MEM_OUT_OF_ZONE).arg(rrset->getName()).
+ arg(origin_);
isc_throw(OutOfZone, "The name " << rrset->getName() <<
" is not contained in zone " << origin_);
}
@@ -194,10 +205,14 @@ struct MemoryZone::MemoryZoneImpl {
// behavior.
if (rrset->getName().isWildcard()) {
if (rrset->getType() == RRType::NS()) {
+ LOG_ERROR(logger, DATASRC_MEM_WILDCARD_NS).
+ arg(rrset->getName());
isc_throw(AddError, "Invalid NS owner name (wildcard): " <<
rrset->getName());
}
if (rrset->getType() == RRType::DNAME()) {
+ LOG_ERROR(logger, DATASRC_MEM_WILDCARD_DNAME).
+ arg(rrset->getName());
isc_throw(AddError, "Invalid DNAME owner name (wildcard): " <<
rrset->getName());
}
@@ -210,9 +225,14 @@ struct MemoryZone::MemoryZoneImpl {
*/
// Implementation of MemoryZone::add
result::Result add(const ConstRRsetPtr& rrset, DomainTree* domains) {
- // Sanitize input
+ // Sanitize input. This will cause an exception to be thrown
+ // if the input RRset is empty.
addValidation(rrset);
+ // OK, can add the RRset.
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_RRSET).
+ arg(rrset->getName()).arg(rrset->getType()).arg(origin_);
+
// Add wildcards possibly contained in the owner name to the domain
// tree.
// Note: this can throw an exception, breaking strong exception
@@ -271,6 +291,8 @@ struct MemoryZone::MemoryZoneImpl {
void addFromLoad(const ConstRRsetPtr& set, DomainTree* domains) {
switch (add(set, domains)) {
case result::EXIST:
+ LOG_ERROR(logger, DATASRC_MEM_DUP_RRSET).
+ arg(set->getName()).arg(set->getType());
isc_throw(dns::MasterLoadError, "Duplicate rrset: " <<
set->toText());
case result::SUCCESS:
@@ -307,6 +329,8 @@ struct MemoryZone::MemoryZoneImpl {
const Domain::const_iterator foundDNAME(node.getData()->find(
RRType::DNAME()));
if (foundDNAME != node.getData()->end()) {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED,
+ DATASRC_MEM_DNAME_ENCOUNTERED);
state->dname_node_ = &node;
state->rrset_ = foundDNAME->second;
// No more processing below the DNAME (RFC 2672, section 3
@@ -328,6 +352,8 @@ struct MemoryZone::MemoryZoneImpl {
return (false);
}
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_NS_ENCOUNTERED);
+
// BIND 9 checks if this node is not the origin. That's probably
// because it can support multiple versions for dynamic updates
// and IXFR, and it's possible that the callback is called at
@@ -363,6 +389,8 @@ struct MemoryZone::MemoryZoneImpl {
rrset, bool rename)
{
if (rename) {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_RENAME).
+ arg(rrset->getName()).arg(name);
/*
* We lose a signature here. But it would be wrong anyway, because
* the name changed. This might turn out to be unimportant in
@@ -385,6 +413,8 @@ struct MemoryZone::MemoryZoneImpl {
FindResult find(const Name& name, RRType type,
RRsetList* target, const FindOptions options) const
{
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FIND).arg(name).
+ arg(type);
// Get the node
DomainNode* node(NULL);
FindState state(options);
@@ -411,12 +441,16 @@ struct MemoryZone::MemoryZoneImpl {
* is NULL.
*/
if (state.dname_node_ != NULL) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DNAME_FOUND).
+ arg(state.rrset_->getName());
// We were traversing a DNAME node (and wanted to go
// lower below it), so return the DNAME
return (FindResult(DNAME, prepareRRset(name, state.rrset_,
rename)));
}
if (state.zonecut_node_ != NULL) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND).
+ arg(state.rrset_->getName());
return (FindResult(DELEGATION, prepareRRset(name,
state.rrset_, rename)));
}
@@ -426,6 +460,8 @@ struct MemoryZone::MemoryZoneImpl {
// the zone but is empty. Treat it as NXRRSET.
if (node_path.getLastComparisonResult().getRelation() ==
NameComparisonResult::SUPERDOMAIN) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).
+ arg(node_path.getAbsoluteName()).arg(name);
return (FindResult(NXRRSET, ConstRRsetPtr()));
}
@@ -463,6 +499,8 @@ struct MemoryZone::MemoryZoneImpl {
if (node_path.getLastComparisonResult().getRelation() ==
NameComparisonResult::COMMONANCESTOR && node_path.
getLastComparisonResult().getCommonLabels() > 1) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA,
+ DATASRC_MEM_WILDCARD_CANCEL).arg(name);
return (FindResult(NXDOMAIN, ConstRRsetPtr()));
}
Name wildcard(Name("*").concatenate(
@@ -485,6 +523,8 @@ struct MemoryZone::MemoryZoneImpl {
// fall through
case DomainTree::NOTFOUND:
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOTFOUND).
+ arg(name);
return (FindResult(NXDOMAIN, ConstRRsetPtr()));
case DomainTree::EXACTMATCH: // This one is OK, handle it
break;
@@ -496,6 +536,8 @@ struct MemoryZone::MemoryZoneImpl {
// If there is an exact match but the node is empty, it's equivalent
// to NXRRSET.
if (node->isEmpty()) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY).
+ arg(name);
return (FindResult(NXRRSET, ConstRRsetPtr()));
}
@@ -506,6 +548,8 @@ struct MemoryZone::MemoryZoneImpl {
if (node->getFlag(DomainNode::FLAG_CALLBACK) && node != origin_data_) {
found = node->getData()->find(RRType::NS());
if (found != node->getData()->end()) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA,
+ DATASRC_MEM_EXACT_DELEGATION).arg(name);
return (FindResult(DELEGATION, prepareRRset(name,
found->second, rename)));
}
@@ -521,23 +565,30 @@ struct MemoryZone::MemoryZoneImpl {
boost::const_pointer_cast<RRset>(prepareRRset(name,
found->second, rename)));
}
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
+ arg(name);
return (FindResult(SUCCESS, ConstRRsetPtr()));
}
found = node->getData()->find(type);
if (found != node->getData()->end()) {
// Good, it is here
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name).
+ arg(type);
return (FindResult(SUCCESS, prepareRRset(name, found->second,
rename)));
} else {
// Next, try CNAME.
found = node->getData()->find(RRType::CNAME());
if (found != node->getData()->end()) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
return (FindResult(CNAME, prepareRRset(name, found->second,
rename)));
}
}
// No exact match or CNAME. Return NXRRSET.
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NXRRSET).arg(type).
+ arg(name);
return (FindResult(NXRRSET, ConstRRsetPtr()));
}
};
@@ -545,9 +596,13 @@ struct MemoryZone::MemoryZoneImpl {
MemoryZone::MemoryZone(const RRClass& zone_class, const Name& origin) :
impl_(new MemoryZoneImpl(zone_class, origin))
{
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_CREATE).arg(origin).
+ arg(zone_class);
}
MemoryZone::~MemoryZone() {
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_DESTROY).arg(getOrigin()).
+ arg(getClass());
delete impl_;
}
@@ -576,6 +631,8 @@ MemoryZone::add(const ConstRRsetPtr& rrset) {
void
MemoryZone::load(const string& filename) {
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_LOAD).arg(getOrigin()).
+ arg(filename);
// Load it into a temporary tree
MemoryZoneImpl::DomainTree tmp;
masterLoad(filename.c_str(), getOrigin(), getClass(),
@@ -588,6 +645,8 @@ MemoryZone::load(const string& filename) {
void
MemoryZone::swap(MemoryZone& zone) {
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_SWAP).arg(getOrigin()).
+ arg(zone.getOrigin());
std::swap(impl_, zone.impl_);
}
@@ -628,6 +687,9 @@ MemoryDataSrc::addZone(ZonePtr zone) {
"Null pointer is passed to MemoryDataSrc::addZone()");
}
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_ADD_ZONE).
+ arg(zone->getOrigin()).arg(zone->getClass().toText());
+
const result::Result result = impl_->zone_table.addZone(zone);
if (result == result::SUCCESS) {
++impl_->zone_count;
@@ -637,6 +699,7 @@ MemoryDataSrc::addZone(ZonePtr zone) {
MemoryDataSrc::FindResult
MemoryDataSrc::findZone(const isc::dns::Name& name) const {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_FIND_ZONE).arg(name);
return (FindResult(impl_->zone_table.findZone(name).code,
impl_->zone_table.findZone(name).zone));
}
diff --git a/src/lib/datasrc/query.cc b/src/lib/datasrc/query.cc
index d3de5c7..a8d675a 100644
--- a/src/lib/datasrc/query.cc
+++ b/src/lib/datasrc/query.cc
@@ -12,7 +12,7 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/name.h>
#include <dns/rrset.h>
#include <dns/message.h>
diff --git a/src/lib/datasrc/result.h b/src/lib/datasrc/result.h
index 201cbcc..f7ca363 100644
--- a/src/lib/datasrc/result.h
+++ b/src/lib/datasrc/result.h
@@ -1,4 +1,3 @@
-// Copyright (C) 2010 CZ NIC
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
diff --git a/src/lib/datasrc/sqlite3_datasrc.cc b/src/lib/datasrc/sqlite3_datasrc.cc
index ab910ba..13d98ed 100644
--- a/src/lib/datasrc/sqlite3_datasrc.cc
+++ b/src/lib/datasrc/sqlite3_datasrc.cc
@@ -18,6 +18,7 @@
#include <sqlite3.h>
#include <datasrc/sqlite3_datasrc.h>
+#include <datasrc/logger.h>
#include <dns/rrttl.h>
#include <dns/rdata.h>
@@ -227,6 +228,8 @@ Sqlite3DataSrc::findRecords(const Name& name, const RRType& rdtype,
RRsetList& target, const Name* zonename,
const Mode mode, uint32_t& flags) const
{
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_SQLITE_FINDREC).arg(name).
+ arg(rdtype);
flags = 0;
int zone_id = (zonename == NULL) ? findClosest(name, NULL) :
findClosest(*zonename, NULL);
@@ -345,12 +348,15 @@ Sqlite3DataSrc::findClosest(const Name& name, unsigned int* position) const {
void
Sqlite3DataSrc::findClosestEnclosure(DataSrcMatch& match) const {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_ENCLOSURE).
+ arg(match.getName());
if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
return;
}
unsigned int position;
if (findClosest(match.getName(), &position) == -1) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_ENCLOSURE_NOTFOUND);
return;
}
@@ -362,9 +368,11 @@ Sqlite3DataSrc::findPreviousName(const Name& qname,
Name& target,
const Name* zonename) const
{
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_PREVIOUS).arg(qname);
const int zone_id = (zonename == NULL) ?
findClosest(qname, NULL) : findClosest(*zonename, NULL);
if (zone_id < 0) {
+ LOG_ERROR(logger, DATASRC_SQLITE_PREVIOUS_NO_ZONE).arg(qname.toText());
return (ERROR);
}
@@ -402,8 +410,11 @@ Sqlite3DataSrc::findCoveringNSEC3(const Name& zonename,
string& hashstr,
RRsetList& target) const
{
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FIND_NSEC3).
+ arg(zonename).arg(hashstr);
const int zone_id = findClosest(zonename, NULL);
if (zone_id < 0) {
+ LOG_ERROR(logger, DATASRC_SQLITE_FIND_NSEC3_NO_ZONE).arg(zonename);
return (ERROR);
}
@@ -484,7 +495,11 @@ Sqlite3DataSrc::findRRset(const Name& qname,
uint32_t& flags,
const Name* zonename) const
{
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FIND).arg(qname).
+ arg(qtype);
if (qclass != getClass() && qclass != RRClass::ANY()) {
+ LOG_ERROR(logger, DATASRC_SQLITE_FIND_BAD_CLASS).arg(getClass()).
+ arg(qclass);
return (ERROR);
}
findRecords(qname, qtype, target, zonename, NORMAL, flags);
@@ -499,7 +514,11 @@ Sqlite3DataSrc::findExactRRset(const Name& qname,
uint32_t& flags,
const Name* zonename) const
{
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FINDEXACT).arg(qname).
+ arg(qtype);
if (qclass != getClass() && qclass != RRClass::ANY()) {
+ LOG_ERROR(logger, DATASRC_SQLITE_FINDEXACT_BAD_CLASS).arg(getClass()).
+ arg(qclass);
return (ERROR);
}
findRecords(qname, qtype, target, zonename, NORMAL, flags);
@@ -523,7 +542,10 @@ Sqlite3DataSrc::findAddrs(const Name& qname,
uint32_t& flags,
const Name* zonename) const
{
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FINDADDRS).arg(qname);
if (qclass != getClass() && qclass != RRClass::ANY()) {
+ LOG_ERROR(logger, DATASRC_SQLITE_FINDADDRS_BAD_CLASS).arg(getClass()).
+ arg(qclass);
return (ERROR);
}
findRecords(qname, RRType::ANY(), target, zonename, ADDRESS, flags);
@@ -537,8 +559,11 @@ Sqlite3DataSrc::findReferral(const Name& qname,
uint32_t& flags,
const Name* zonename) const
{
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FINDREF).arg(qname);
if (qclass != getClass() && qclass != RRClass::ANY()) {
- return (ERROR);
+ LOG_ERROR(logger, DATASRC_SQLITE_FINDREF_BAD_CLASS).arg(getClass()).
+ arg(qclass);
+ return (ERROR);
}
findRecords(qname, RRType::ANY(), target, zonename, DELEGATION, flags);
return (SUCCESS);
@@ -546,9 +571,12 @@ Sqlite3DataSrc::findReferral(const Name& qname,
Sqlite3DataSrc::Sqlite3DataSrc() :
dbparameters(new Sqlite3Parameters)
-{}
+{
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CREATE);
+}
Sqlite3DataSrc::~Sqlite3DataSrc() {
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_DESTROY);
if (dbparameters->db_ != NULL) {
close();
}
@@ -635,6 +663,7 @@ checkAndSetupSchema(Sqlite3Initializer* initializer) {
initializer->params_.version_ = sqlite3_column_int(prepared, 0);
sqlite3_finalize(prepared);
} else {
+ logger.info(DATASRC_SQLITE_SETUP);
if (prepared != NULL) {
sqlite3_finalize(prepared);
}
@@ -664,6 +693,7 @@ checkAndSetupSchema(Sqlite3Initializer* initializer) {
//
void
Sqlite3DataSrc::open(const string& name) {
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_OPEN).arg(name);
if (dbparameters->db_ != NULL) {
isc_throw(DataSourceError, "Duplicate SQLite open with " << name);
}
@@ -683,6 +713,7 @@ Sqlite3DataSrc::open(const string& name) {
//
DataSrc::Result
Sqlite3DataSrc::close(void) {
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CLOSE);
if (dbparameters->db_ == NULL) {
isc_throw(DataSourceError,
"SQLite data source is being closed before open");
diff --git a/src/lib/datasrc/static_datasrc.cc b/src/lib/datasrc/static_datasrc.cc
index 025078a..dee14b9 100644
--- a/src/lib/datasrc/static_datasrc.cc
+++ b/src/lib/datasrc/static_datasrc.cc
@@ -26,6 +26,7 @@
#include <datasrc/data_source.h>
#include <datasrc/static_datasrc.h>
+#include <datasrc/logger.h>
using namespace std;
using namespace isc::dns;
@@ -112,6 +113,7 @@ StaticDataSrcImpl::StaticDataSrcImpl() :
}
StaticDataSrc::StaticDataSrc() {
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_STATIC_CREATE);
setClass(RRClass::CH());
impl_ = new StaticDataSrcImpl;
}
@@ -155,8 +157,11 @@ StaticDataSrc::findRRset(const Name& qname,
RRsetList& target, uint32_t& flags,
const Name* const zonename) const
{
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_STATIC_FIND).arg(qname).
+ arg(qtype);
flags = 0;
if (qclass != getClass() && qclass != RRClass::ANY()) {
+ LOG_ERROR(logger, DATASRC_STATIC_BAD_CLASS);
return (ERROR);
}
diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am
index f09b4b7..fbcf9c9 100644
--- a/src/lib/datasrc/tests/Makefile.am
+++ b/src/lib/datasrc/tests/Makefile.am
@@ -27,15 +27,20 @@ run_unittests_SOURCES += test_datasrc.h test_datasrc.cc
run_unittests_SOURCES += rbtree_unittest.cc
run_unittests_SOURCES += zonetable_unittest.cc
run_unittests_SOURCES += memory_datasrc_unittest.cc
+run_unittests_SOURCES += logger_unittest.cc
+
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(SQLITE_LIBS)
-run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/datasrc/tests/datasrc_unittest.cc b/src/lib/datasrc/tests/datasrc_unittest.cc
index ab5cd85..cd1a40c 100644
--- a/src/lib/datasrc/tests/datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/datasrc_unittest.cc
@@ -20,7 +20,8 @@
#include <gtest/gtest.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
+
#include <dns/message.h>
#include <dns/messagerenderer.h>
#include <dns/question.h>
@@ -44,6 +45,7 @@
using isc::UnitTestUtil;
using namespace std;
+using namespace isc::util;
using namespace isc::dns;
using namespace isc::dns::rdata;
using namespace isc::datasrc;
@@ -70,7 +72,7 @@ protected:
}
void QueryCommon(const RRClass& qclass);
void createAndProcessQuery(const Name& qname, const RRClass& qclass,
- const RRType& qtype);
+ const RRType& qtype, bool need_dnssec);
HotCache cache;
MetaDataSrc meta_source;
@@ -82,23 +84,26 @@ protected:
};
void
-performQuery(DataSrc& data_source, HotCache& cache, Message& message) {
+performQuery(DataSrc& data_source, HotCache& cache, Message& message,
+ bool need_dnssec = true)
+{
message.setHeaderFlag(Message::HEADERFLAG_AA);
message.setRcode(Rcode::NOERROR());
- Query q(message, cache, true);
+ Query q(message, cache, need_dnssec);
data_source.doQuery(q);
}
void
DataSrcTest::createAndProcessQuery(const Name& qname, const RRClass& qclass,
- const RRType& qtype)
+ const RRType& qtype,
+ bool need_dnssec = true)
{
msg.makeResponse();
msg.setOpcode(Opcode::QUERY());
msg.addQuestion(Question(qname, qclass, qtype));
msg.setHeaderFlag(Message::HEADERFLAG_RD);
qid = msg.getQid();
- performQuery(meta_source, cache, msg);
+ performQuery(meta_source, cache, msg, need_dnssec);
}
void
@@ -165,6 +170,59 @@ TEST_F(DataSrcTest, QueryClassAny) {
QueryCommon(RRClass::ANY());
}
+TEST_F(DataSrcTest, queryClassAnyNegative) {
+ // There was a bug where Class ANY query triggered a crash due to NULL
+ // pointer dereference. This test checks that condition.
+
+ // NXDOMAIN case
+ createAndProcessQuery(Name("notexistent.example.com"), RRClass::ANY(),
+ RRType::A());
+ headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
+ QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 6, 0);
+
+ // NXRRSET case
+ msg.clear(Message::PARSE);
+ createAndProcessQuery(Name("www.example.com"), RRClass::ANY(),
+ RRType::TXT());
+ headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+ QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 4, 0);
+}
+
+TEST_F(DataSrcTest, queryClassAnyDNAME) {
+ // Class ANY query that would match a DNAME. Everything including the
+ // synthesized CNAME should be the same as the response to class IN query.
+ createAndProcessQuery(Name("www.dname.example.com"), RRClass::ANY(),
+ RRType::A(), false);
+ headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+ QR_FLAG | AA_FLAG | RD_FLAG, 1, 3, 3, 3);
+ rrsetsCheck("dname.example.com. 3600 IN DNAME sql1.example.com.\n"
+ "www.dname.example.com. 3600 IN CNAME www.sql1.example.com.\n"
+ "www.sql1.example.com. 3600 IN A 192.0.2.2\n",
+ msg.beginSection(Message::SECTION_ANSWER),
+ msg.endSection(Message::SECTION_ANSWER));
+
+ // Also check the case of explicit DNAME query.
+ msg.clear(Message::PARSE);
+ createAndProcessQuery(Name("dname.example.com"), RRClass::ANY(),
+ RRType::DNAME(), false);
+ headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+ QR_FLAG | AA_FLAG | RD_FLAG, 1, 1, 3, 3);
+ rrsetsCheck("dname.example.com. 3600 IN DNAME sql1.example.com.\n",
+ msg.beginSection(Message::SECTION_ANSWER),
+ msg.endSection(Message::SECTION_ANSWER));
+}
+
+TEST_F(DataSrcTest, queryClassAnyCNAME) {
+ // Similar test for CNAME
+ createAndProcessQuery(Name("foo.example.com"), RRClass::ANY(),
+ RRType::A(), false);
+ headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+ QR_FLAG | AA_FLAG | RD_FLAG, 1, 1, 0, 0);
+ rrsetsCheck("foo.example.com. 3600 IN CNAME cnametest.example.net.\n",
+ msg.beginSection(Message::SECTION_ANSWER),
+ msg.endSection(Message::SECTION_ANSWER));
+}
+
TEST_F(DataSrcTest, NSQuery) {
createAndProcessQuery(Name("example.com"), RRClass::IN(),
RRType::NS());
@@ -416,68 +474,36 @@ TEST_F(DataSrcTest, DISABLED_WildcardAgainstMultiLabel) {
TEST_F(DataSrcTest, WildcardCname) {
// Check that wildcard answers containing CNAMES are followed
- // correctly
- createAndProcessQuery(Name("www.wild2.example.com"), RRClass::IN(),
- RRType::A());
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 6, 6);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("www.wild2.example.com"), rrset->getName());
- EXPECT_EQ(RRType::CNAME(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("www.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- ++rit;
- ++rit;
- rrset = *rit;
- EXPECT_EQ(Name("www.example.com"), rrset->getName());
- EXPECT_EQ(RRType::A(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- it = rrset->getRdataIterator();
- EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- rit = msg.beginSection(Message::SECTION_AUTHORITY);
- rrset = *rit;
- EXPECT_EQ(Name("*.wild2.example.com"), rrset->getName());
- EXPECT_EQ(RRType::NSEC(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
- ++rit;
- ++rit;
-
- rrset = *rit;
- EXPECT_EQ(Name("example.com"), rrset->getName());
- EXPECT_EQ(RRType::NS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- it = rrset->getRdataIterator();
- EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- rit = msg.beginSection(Message::SECTION_ADDITIONAL);
- rrset = *rit;
- EXPECT_EQ(Name("dns01.example.com"), rrset->getName());
- EXPECT_EQ(RRType::A(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- it = rrset->getRdataIterator();
- EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
+ // correctly. It should result in the same response for both
+ // class IN and ANY queries.
+ const RRClass classes[2] = { RRClass::IN(), RRClass::ANY() };
+
+ for (int i = 0; i < sizeof(classes) / sizeof(classes[0]); ++i) {
+ SCOPED_TRACE("Wildcard + CNAME test for class " + classes[i].toText());
+
+ msg.clear(Message::PARSE);
+
+ createAndProcessQuery(Name("www.wild2.example.com"), classes[i],
+ RRType::A(), false);
+
+ headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+ QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 3, 3);
+
+ rrsetsCheck("www.wild2.example.com. 3600 IN CNAME www.example.com\n"
+ "www.example.com. 3600 IN A 192.0.2.1\n",
+ msg.beginSection(Message::SECTION_ANSWER),
+ msg.endSection(Message::SECTION_ANSWER));
+ rrsetsCheck("example.com. 3600 IN NS dns01.example.com.\n"
+ "example.com. 3600 IN NS dns02.example.com.\n"
+ "example.com. 3600 IN NS dns03.example.com.",
+ msg.beginSection(Message::SECTION_AUTHORITY),
+ msg.endSection(Message::SECTION_AUTHORITY));
+ rrsetsCheck("dns01.example.com. 3600 IN A 192.0.2.1\n"
+ "dns02.example.com. 3600 IN A 192.0.2.2\n"
+ "dns03.example.com. 3600 IN A 192.0.2.3",
+ msg.beginSection(Message::SECTION_ADDITIONAL),
+ msg.endSection(Message::SECTION_ADDITIONAL));
+ }
}
TEST_F(DataSrcTest, WildcardCnameNodata) {
@@ -667,7 +693,7 @@ TEST_F(DataSrcTest, Cname) {
EXPECT_EQ(RRClass::IN(), rrset->getClass());
RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("cnametest.flame.org.", it->getCurrent().toText());
+ EXPECT_EQ("cnametest.example.net.", it->getCurrent().toText());
it->next();
EXPECT_TRUE(it->isLast());
}
@@ -1035,6 +1061,25 @@ TEST_F(DataSrcTest, apexCNAMEZone) {
DataSourceError);
}
+TEST_F(DataSrcTest, incompleteGlue) {
+ // One of the NS names belong to a different zone (which is still
+ // authoritative), and the glue is missing in that zone. We should
+ // still return the existent glue.
+ // (nons.example is also broken in that it doesn't have apex NS, but
+ // that doesn't matter for this test)
+ createAndProcessQuery(Name("www.incompletechild.nons.example"),
+ RRClass::IN(), RRType::A());
+ headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+ QR_FLAG | RD_FLAG, 1, 0, 2, 1);
+ rrsetsCheck("incompletechild.nons.example. 3600 IN NS ns.incompletechild.nons.example.\n"
+ "incompletechild.nons.example. 3600 IN NS nx.nosoa.example.",
+ msg.beginSection(Message::SECTION_AUTHORITY),
+ msg.endSection(Message::SECTION_AUTHORITY));
+ rrsetsCheck("ns.incompletechild.nons.example. 3600 IN A 192.0.2.1",
+ msg.beginSection(Message::SECTION_ADDITIONAL),
+ msg.endSection(Message::SECTION_ADDITIONAL));
+}
+
// currently fails
TEST_F(DataSrcTest, DISABLED_synthesizedCnameTooLong) {
// qname has the possible max length (255 octets). it matches a DNAME,
@@ -1047,6 +1092,22 @@ TEST_F(DataSrcTest, DISABLED_synthesizedCnameTooLong) {
RRClass::IN(), RRType::A());
}
+TEST_F(DataSrcTest, cacheDataInNonexistentZone) {
+ // This test emulates the situation where an RRset in some zone of some
+ // data source is cached and then the zone is removed from the data source.
+ // When there is such a substantial inconsistency between the cache and
+ // the real data source, we should honor the latter. More important,
+ // the inconsistency shouldn't cause any disruption such as a crash.
+
+ const Name qname("nosuchzone.example");
+ RRsetPtr rrset(new RRset(qname, RRClass::IN(), RRType::A(), RRTTL(0)));
+ cache.addPositive(rrset, DataSrc::REFERRAL);
+
+ createAndProcessQuery(qname, RRClass::IN(), RRType::A(), false);
+ headerCheck(msg, qid, Rcode::REFUSED(), opcodeval, QR_FLAG | RD_FLAG,
+ 1, 0, 0, 0);
+}
+
// Tests of the DataSrcMatch class start here
class DataSrcMatchTest : public ::testing::Test {
protected:
diff --git a/src/lib/datasrc/tests/logger_unittest.cc b/src/lib/datasrc/tests/logger_unittest.cc
new file mode 100644
index 0000000..df5a41c
--- /dev/null
+++ b/src/lib/datasrc/tests/logger_unittest.cc
@@ -0,0 +1,31 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+
+#include <datasrc/logger.h>
+
+using namespace isc::datasrc;
+
+namespace {
+
+TEST(CacheLogger, name) {
+ // This does not check the name only, but the fact the logger is created
+ // The dot is because of empty root logger
+ std::string name(logger.getName());
+ EXPECT_EQ(name.size() - 8, name.rfind(".datasrc")) <<
+ "Wrong logger name: " << name;
+}
+
+}
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index 16d749c..83fbb58 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -1,5 +1,4 @@
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-// Copyright (C) 2011 CZ NIC
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
diff --git a/src/lib/datasrc/tests/query_unittest.cc b/src/lib/datasrc/tests/query_unittest.cc
index fa7216b..7a20b86 100644
--- a/src/lib/datasrc/tests/query_unittest.cc
+++ b/src/lib/datasrc/tests/query_unittest.cc
@@ -14,7 +14,7 @@
#include <gtest/gtest.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/message.h>
#include <dns/name.h>
#include <dns/opcode.h>
diff --git a/src/lib/datasrc/tests/rbtree_unittest.cc b/src/lib/datasrc/tests/rbtree_unittest.cc
index dd1b7fe..b26a22b 100644
--- a/src/lib/datasrc/tests/rbtree_unittest.cc
+++ b/src/lib/datasrc/tests/rbtree_unittest.cc
@@ -398,7 +398,7 @@ TEST_F(RBTreeTest, getLastComparedNode) {
EXPECT_EQ(static_cast<void*>(NULL), chain.getLastComparedNode());
chain.clear();
- const RBNode<int>* expected_node;
+ const RBNode<int>* expected_node = NULL;
// Exact match case. The returned node should be last compared.
EXPECT_EQ(RBTree<int>::EXACTMATCH,
diff --git a/src/lib/datasrc/tests/run_unittests.cc b/src/lib/datasrc/tests/run_unittests.cc
index a35a646..ffef333 100644
--- a/src/lib/datasrc/tests/run_unittests.cc
+++ b/src/lib/datasrc/tests/run_unittests.cc
@@ -13,6 +13,8 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+#include <log/logger_support.h>
#include <dns/tests/unittest_util.h>
@@ -21,5 +23,7 @@ main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
- return (RUN_ALL_TESTS());
+ isc::log::initLogger();
+
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/datasrc/tests/test_datasrc.cc b/src/lib/datasrc/tests/test_datasrc.cc
index 9493e1a..d78e7db 100644
--- a/src/lib/datasrc/tests/test_datasrc.cc
+++ b/src/lib/datasrc/tests/test_datasrc.cc
@@ -23,7 +23,7 @@
#include <datasrc/data_source.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/name.h>
#include <dns/rdata.h>
@@ -154,7 +154,7 @@ const struct RRData example_com_records[] = {
{"*.wild3.example.com", "RRSIG", "NSEC 5 3 7200 20100410212307 20100311212307 33495 example.com. EuSzh6or8mbvwru2H7fyYeMpW6J8YZ528rabU38V/lMN0TdamghIuCneAvSNaZgwk2MSN1bWpZqB2kAipaM/ZI9/piLlTvVjjOQ8pjk0auwCEqT7Z7Qng3E92O9yVzO+WHT9QZn/fR6t60392In4IvcBGjZyjzQk8njIwbui xGA="},
// foo.example.com
- {"foo.example.com", "CNAME", "cnametest.flame.org"},
+ {"foo.example.com", "CNAME", "cnametest.example.net"},
{"foo.example.com", "RRSIG", "CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. DSqkLnsh0gCeCPVW/Q8viy9GNP+KHmFGfWqyVG1S6koBtGN/VQQ16M4PHZ9Zssmf/JcDVJNIhAChHPE2WJiaPCNGTprsaUshf1Q2vMPVnkrJKgDY8SVRYMptmT8eaT0gGri4KhqRoFpMT5OYfesybwDgfhFSQQAh6ps3bIUsy4o="},
{"foo.example.com", "NSEC", "mail.example.com. CNAME RRSIG NSEC"},
{"foo.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. RTQwlSqui6StUYye1KCSOEr1d3irndWFqHBpwP7g7n+w8EDXJ8I7lYgwzHvlQt6BLAxe5fUDi7ct8M5hXvsm7FoWPZ5wXH+2/eJUCYxIw4vezKMkMwBP6M/YkJ2CMqY8DppYf60QaLDONQAr7AcK/naSyioeI5h6eaoVitUDMso="},
@@ -199,6 +199,7 @@ const struct RRData example_com_records[] = {
{NULL, NULL, NULL}
};
+
const struct RRData example_com_glue_records[] = {
{"ns1.subzone.example.com", "A", "192.0.2.1"},
{"ns2.subzone.example.com", "A", "192.0.2.2"},
@@ -247,6 +248,20 @@ const struct RRData nons_example_records[] = {
"1234 3600 1800 2419200 7200"},
{"www.nons.example", "A", "192.0.2.1"},
{"ns.nons.example", "A", "192.0.2.2"},
+
+ // One of the NS names is intentionally non existent in the zone it belongs
+ // to. This delegation is used to see if we still return the NS and the
+ // existent glue.
+ // (These are not relevant to test the case for the "no NS" case. We use
+ // this zone to minimize the number of test zones)
+ {"incompletechild.nons.example", "NS", "ns.incompletechild.nons.example"},
+ {"incompletechild.nons.example", "NS", "nx.nosoa.example"},
+
+ {NULL, NULL, NULL}
+};
+
+const struct RRData nons_example_glue_records[] = {
+ {"ns.incompletechild.nons.example", "A", "192.0.2.1"},
{NULL, NULL, NULL}
};
@@ -298,7 +313,7 @@ const struct ZoneData zone_data[] = {
{ "example.com", "IN", example_com_records, example_com_glue_records },
{ "sql1.example.com", "IN", sql1_example_com_records, empty_records },
{ "loop.example", "IN", loop_example_records, empty_records },
- { "nons.example", "IN", nons_example_records, empty_records },
+ { "nons.example", "IN", nons_example_records, nons_example_glue_records },
{ "nons-dname.example", "IN", nonsdname_example_records, empty_records },
{ "nosoa.example", "IN", nosoa_example_records, empty_records },
{ "apexcname.example", "IN", nosoa_example_records, empty_records }
diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h
index f70274f..1252c94 100644
--- a/src/lib/datasrc/zone.h
+++ b/src/lib/datasrc/zone.h
@@ -1,4 +1,3 @@
-// Copyright (C) 2010 CZ NIC
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index c5c5cd1..887ac09 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -1,6 +1,8 @@
-SUBDIRS = . tests python
+SUBDIRS = . tests python benchmarks
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -15,60 +17,58 @@ EXTRA_DIST += rrtype-placeholder.h
# NOTE: when an rdata file is added, please also add to this list:
EXTRA_DIST += rdata/any_255/tsig_250.cc
EXTRA_DIST += rdata/any_255/tsig_250.h
-EXTRA_DIST += rdata/in_1/aaaa_28.cc
-EXTRA_DIST += rdata/in_1/aaaa_28.h
-EXTRA_DIST += rdata/in_1/a_1.cc
-EXTRA_DIST += rdata/in_1/a_1.h
EXTRA_DIST += rdata/ch_3/a_1.cc
EXTRA_DIST += rdata/ch_3/a_1.h
-EXTRA_DIST += rdata/generic/mx_15.h
-EXTRA_DIST += rdata/generic/rrsig_46.cc
+EXTRA_DIST += rdata/generic/cname_5.cc
+EXTRA_DIST += rdata/generic/cname_5.h
+EXTRA_DIST += rdata/generic/detail/nsec_bitmap.cc
+EXTRA_DIST += rdata/generic/detail/nsec_bitmap.h
EXTRA_DIST += rdata/generic/dname_39.cc
-EXTRA_DIST += rdata/generic/rrsig_46.h
EXTRA_DIST += rdata/generic/dname_39.h
-EXTRA_DIST += rdata/generic/ns_2.cc
-EXTRA_DIST += rdata/generic/nsec_47.cc
-EXTRA_DIST += rdata/generic/ns_2.h
-EXTRA_DIST += rdata/generic/nsec_47.h
-EXTRA_DIST += rdata/generic/opt_41.cc
-EXTRA_DIST += rdata/generic/soa_6.cc
-EXTRA_DIST += rdata/generic/cname_5.cc
EXTRA_DIST += rdata/generic/dnskey_48.cc
-EXTRA_DIST += rdata/generic/opt_41.h
-EXTRA_DIST += rdata/generic/soa_6.h
-EXTRA_DIST += rdata/generic/cname_5.h
EXTRA_DIST += rdata/generic/dnskey_48.h
EXTRA_DIST += rdata/generic/ds_43.cc
EXTRA_DIST += rdata/generic/ds_43.h
-EXTRA_DIST += rdata/generic/txt_16.cc
-EXTRA_DIST += rdata/generic/txt_16.h
EXTRA_DIST += rdata/generic/mx_15.cc
-EXTRA_DIST += rdata/generic/nsec3param_51.h
-EXTRA_DIST += rdata/generic/nsec3param_51.cc
+EXTRA_DIST += rdata/generic/mx_15.h
+EXTRA_DIST += rdata/generic/ns_2.cc
+EXTRA_DIST += rdata/generic/ns_2.h
EXTRA_DIST += rdata/generic/nsec3_50.cc
EXTRA_DIST += rdata/generic/nsec3_50.h
+EXTRA_DIST += rdata/generic/nsec3param_51.cc
+EXTRA_DIST += rdata/generic/nsec3param_51.h
+EXTRA_DIST += rdata/generic/nsec_47.cc
+EXTRA_DIST += rdata/generic/nsec_47.h
+EXTRA_DIST += rdata/generic/opt_41.cc
+EXTRA_DIST += rdata/generic/opt_41.h
EXTRA_DIST += rdata/generic/ptr_12.cc
EXTRA_DIST += rdata/generic/ptr_12.h
+EXTRA_DIST += rdata/generic/rp_17.cc
+EXTRA_DIST += rdata/generic/rp_17.h
+EXTRA_DIST += rdata/generic/rrsig_46.cc
+EXTRA_DIST += rdata/generic/rrsig_46.h
+EXTRA_DIST += rdata/generic/soa_6.cc
+EXTRA_DIST += rdata/generic/soa_6.h
+EXTRA_DIST += rdata/generic/txt_16.cc
+EXTRA_DIST += rdata/generic/txt_16.h
EXTRA_DIST += rdata/hs_4/a_1.cc
EXTRA_DIST += rdata/hs_4/a_1.h
+EXTRA_DIST += rdata/in_1/a_1.cc
+EXTRA_DIST += rdata/in_1/a_1.h
+EXTRA_DIST += rdata/in_1/aaaa_28.cc
+EXTRA_DIST += rdata/in_1/aaaa_28.h
#EXTRA_DIST += rdata/template.cc
#EXTRA_DIST += rdata/template.h
# auto-generate by gen-rdatacode.py:
BUILT_SOURCES = rrclass.h rrtype.h rrparamregistry.cc
-#TODO: check this###BUILT_SOURCES = rdataclass.h rdataclass.cc
+BUILT_SOURCES += rdataclass.h rdataclass.cc
lib_LTLIBRARIES = libdns++.la
-libdns___la_SOURCES = util/base32hex.h util/base64.h util/base_n.cc
-libdns___la_SOURCES += util/base32hex_from_binary.h
-libdns___la_SOURCES += util/binary_from_base32hex.h
-libdns___la_SOURCES += util/base16_from_binary.h util/binary_from_base16.h
-libdns___la_SOURCES += buffer.h
-libdns___la_SOURCES += dnssectime.h dnssectime.cc
+libdns___la_SOURCES =
libdns___la_SOURCES += edns.h edns.cc
libdns___la_SOURCES += exceptions.h exceptions.cc
-libdns___la_SOURCES += util/hex.h
libdns___la_SOURCES += masterload.h masterload.cc
libdns___la_SOURCES += message.h message.cc
libdns___la_SOURCES += messagerenderer.h messagerenderer.cc
@@ -76,6 +76,7 @@ libdns___la_SOURCES += name.h name.cc
libdns___la_SOURCES += opcode.h opcode.cc
libdns___la_SOURCES += rcode.h rcode.cc
libdns___la_SOURCES += rdata.h rdata.cc
+libdns___la_SOURCES += rdatafields.h rdatafields.cc
libdns___la_SOURCES += rrclass.cc
libdns___la_SOURCES += rrparamregistry.h
libdns___la_SOURCES += rrset.h rrset.cc
@@ -83,13 +84,22 @@ libdns___la_SOURCES += rrsetlist.h rrsetlist.cc
libdns___la_SOURCES += rrttl.h rrttl.cc
libdns___la_SOURCES += rrtype.cc
libdns___la_SOURCES += question.h question.cc
-libdns___la_SOURCES += util/sha1.h util/sha1.cc
+libdns___la_SOURCES += tsig.h tsig.cc
+libdns___la_SOURCES += tsigerror.h tsigerror.cc
libdns___la_SOURCES += tsigkey.h tsigkey.cc
+libdns___la_SOURCES += tsigrecord.h tsigrecord.cc
libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h
libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc
-nodist_libdns___la_SOURCES = rdataclass.cc rrclass.h rrtype.h
-nodist_libdns___la_SOURCES += rrparamregistry.cc
+libdns___la_CPPFLAGS = $(AM_CPPFLAGS)
+# Most applications of libdns++ will only implicitly rely on libcryptolink,
+# so we add the dependency here so that the applications don't have to link
+# libcryptolink explicitly.
+libdns___la_LIBADD = $(top_builddir)/src/lib/cryptolink/libcryptolink.la
+libdns___la_LIBADD += $(top_builddir)/src/lib/util/libutil.la
+
+nodist_libdns___include_HEADERS = rdataclass.h rrclass.h rrtype.h
+nodist_libdns___la_SOURCES = rdataclass.cc rrparamregistry.cc
rrclass.h: rrclass-placeholder.h
rrtype.h: rrtype-placeholder.h
@@ -99,8 +109,6 @@ rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc: Makefile
libdns___includedir = $(includedir)/dns
libdns___include_HEADERS = \
- buffer.h \
- dnssectime.h \
edns.h \
exceptions.h \
message.h \
@@ -109,13 +117,10 @@ libdns___include_HEADERS = \
question.h \
rcode.h \
rdata.h \
- rdataclass.h \
- rrclass.h \
rrparamregistry.h \
rrset.h \
rrsetlist.h \
rrttl.h \
- rrtype.h \
tsigkey.h
# Purposely not installing these headers:
# util/*.h: used only internally, and not actually DNS specific
diff --git a/src/lib/dns/benchmarks/Makefile.am b/src/lib/dns/benchmarks/Makefile.am
new file mode 100644
index 0000000..8645385
--- /dev/null
+++ b/src/lib/dns/benchmarks/Makefile.am
@@ -0,0 +1,17 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+noinst_PROGRAMS = rdatarender_bench
+rdatarender_bench_SOURCES = rdatarender_bench.cc
+
+rdatarender_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
+rdatarender_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+rdatarender_bench_LDADD += $(SQLITE_LIBS)
diff --git a/src/lib/dns/benchmarks/README b/src/lib/dns/benchmarks/README
new file mode 100644
index 0000000..7119c13
--- /dev/null
+++ b/src/lib/dns/benchmarks/README
@@ -0,0 +1,10 @@
+- rdatarender_bench
+
+ This is a benchmark for RDATA rendering performance comparing the basic
+ Rdata objects and RdataField objects. It takes a command line argument
+ that specifies an input data file. Each line of the data file should
+ specify a single RDATA with its RR class and type, e.g.
+ IN A 192.0.2.1
+ IN NS ns.example.com.
+ Lines beginning with '#' and empty lines will be ignored. Sample input
+ files can be found in benchmarkdata/rdatarender_*.
diff --git a/src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_com b/src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_com
new file mode 100644
index 0000000..12155a6
--- /dev/null
+++ b/src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_com
@@ -0,0 +1,32 @@
+# This is sample input data for rdatarender_bench.
+# These are RDATA in the authority and additional sections in a response from
+# a root server for a query domain under COM.
+
+IN NS g.gtld-servers.net.
+IN NS a.gtld-servers.net.
+IN NS k.gtld-servers.net.
+IN NS c.gtld-servers.net.
+IN NS d.gtld-servers.net.
+IN NS m.gtld-servers.net.
+IN NS h.gtld-servers.net.
+IN NS b.gtld-servers.net.
+IN NS j.gtld-servers.net.
+IN NS l.gtld-servers.net.
+IN NS e.gtld-servers.net.
+IN NS f.gtld-servers.net.
+IN NS i.gtld-servers.net.
+IN A 192.5.6.30
+IN A 192.33.14.30
+IN A 192.26.92.30
+IN A 192.31.80.30
+IN A 192.12.94.30
+IN A 192.35.51.30
+IN A 192.42.93.30
+IN A 192.54.112.30
+IN A 192.43.172.30
+IN A 192.48.79.30
+IN A 192.52.178.30
+IN A 192.41.162.30
+IN A 192.55.83.30
+IN AAAA 2001:503:a83e::2:30
+IN AAAA 2001:503:231d::2:30
diff --git a/src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_nxdomain b/src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_nxdomain
new file mode 100644
index 0000000..eb14b5f
--- /dev/null
+++ b/src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_nxdomain
@@ -0,0 +1,10 @@
+# This is sample input data for rdatarender_bench.
+# These are RDATA in the authority section in a response from
+# a root server for a non existent query domain (with DNSSEC).
+
+IN SOA a.root-servers.net. nstld.verisign-grs.com. 2010110301 1800 900 604800 86400
+IN RRSIG SOA 8 0 86400 20101110000000 20101102230000 40288 . WtvYyX2nIsaqjWqkIG1WHFE5PnJ6eno0KqF6azU/MFJ/t1JpKWQ1P4rA 61rnoq0p252fg7wT4XzEz9UDxmpB5pvF2VApe2w9LvSWxsWIIOg8ue5u e9NAAYdzjd0rsYObQQ6msf7WchyAUbnmrqKvf8/CK6+s1xFihXp5DpYL 6K0=
+IN NSEC ac. NS SOA RRSIG NSEC DNSKEY
+IN RRSIG NSEC 8 0 86400 20101110000000 20101102230000 40288 . rWfgg4YUDFAjhiUOT+niJy/qbaIbydqoXg5oB/5j//ZjNFy4hqU8DvdM xJr9UybQpEvu7pvmKQ0jRYO98Fw/UTlY5KiKbhVBJ1t8AE93cbU+s5gX d3Q6+wRcFX5MjZyIe+f30llKrYOZHjRyEFALCkLt4XEmr0xsua+ztAFY 65k=
+IN NSEC np. NS RRSIG NSEC
+IN RRSIG NSEC 8 1 86400 20101110000000 20101102230000 40288 . G32LGynsGA2fyDnesyeCtBCoM3ERMgGS4touDUuoBYW1NrZub76kz5fc z93p8VZfoYWAW7LuC8vJ1jl2sUgBNns4zN4RsfFeopcYjjFnGbGuoZnO NmTU+NKO53Ub7uIcCSeqV+COAaL8XqDfyk1FmVdQvtrBaOW/PWpRahVq 7E8=
diff --git a/src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_org b/src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_org
new file mode 100644
index 0000000..17b80d2
--- /dev/null
+++ b/src/lib/dns/benchmarks/benchmarkdata/rdatarender_data_org
@@ -0,0 +1,22 @@
+# This is sample input data for rdatarender_bench.
+# These are RDATA in the authority and additional sections in a response from
+# a root server for a query domain under ORG.
+
+IN NS b0.org.afilias-nst.org.
+IN NS a2.org.afilias-nst.info.
+IN NS a0.org.afilias-nst.info.
+IN NS c0.org.afilias-nst.info.
+IN NS d0.org.afilias-nst.org.
+IN NS b2.org.afilias-nst.org.
+IN A 199.19.56.1
+IN A 199.249.112.1
+IN A 199.19.54.1
+IN A 199.249.120.1
+IN A 199.19.53.1
+IN A 199.19.57.1
+IN AAAA 2001:500:e::1
+IN AAAA 2001:500:40::1
+IN AAAA 2001:500:c::1
+IN AAAA 2001:500:48::1
+IN AAAA 2001:500:b::1
+IN AAAA 2001:500:f::1
diff --git a/src/lib/dns/benchmarks/rdatarender_bench.cc b/src/lib/dns/benchmarks/rdatarender_bench.cc
new file mode 100644
index 0000000..d1fb0f2
--- /dev/null
+++ b/src/lib/dns/benchmarks/rdatarender_bench.cc
@@ -0,0 +1,188 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include <bench/benchmark.h>
+
+#include <util/buffer.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdatafields.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+using namespace std;
+using namespace isc::bench;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using isc::util::OutputBuffer;
+
+namespace {
+// This templated benchmark class is constructed with a vector of Rdata-like
+// (pointer) objects which should have a "toWire()" method. In its run(),
+// it calls toWire() for each element of the vector.
+template <typename T>
+class RdataRenderBenchMark {
+public:
+ RdataRenderBenchMark(const vector<T>& dataset) :
+ dataset_(dataset), buffer_(4096), renderer_(buffer_)
+ {}
+ unsigned int run() {
+ typename vector<T>::const_iterator data;
+ typename vector<T>::const_iterator data_end = dataset_.end();
+ for (data = dataset_.begin(); data != data_end; ++data) {
+ renderer_.clear();
+ (*data)->toWire(renderer_);
+ }
+ return (dataset_.size());
+ }
+private:
+ const vector<T>& dataset_;
+ OutputBuffer buffer_;
+ MessageRenderer renderer_;
+};
+
+// This supplemental class emulates an RRset like class that internally
+// uses RdataFields. On construction it stores RDATA information in the
+// form of RdataFields fields. Its toWire() method restores the data as
+// an RdataFields object for the rendering.
+class RdataFieldsStore {
+public:
+ RdataFieldsStore(ConstRdataPtr rdata) {
+ const RdataFields fields(*rdata);
+
+ spec_size_ = fields.getFieldSpecDataSize();
+ spec_store_.resize(spec_size_);
+ void* cp_spec = &spec_store_[0];
+ memcpy(cp_spec, fields.getFieldSpecData(), spec_store_.size());
+ spec_ptr_ = cp_spec;
+
+ data_length_ = fields.getDataLength();
+ data_store_.resize(data_length_);
+ void* cp_data = &data_store_[0];
+ memcpy(cp_data, fields.getData(), data_store_.size());
+ // Vector guarantees that the elements are stored in continuous array
+ // in memory, so this is actually correct by the standard
+ data_ptr_ = cp_data;
+ }
+ void toWire(MessageRenderer& renderer) const {
+ RdataFields(spec_ptr_, spec_size_,
+ data_ptr_, data_length_).toWire(renderer);
+ }
+private:
+ vector<unsigned char> spec_store_;
+ vector<unsigned char> data_store_;
+ const void* spec_ptr_;
+ const void* data_ptr_;
+ unsigned int spec_size_;
+ size_t data_length_;
+};
+
+// We wouldn't necessarily have to use a shared pointer, but it's easier
+// to use pointer-like values to adjust them with the RdataRenderBenchMark
+// template.
+typedef boost::shared_ptr<const RdataFieldsStore> ConstRdataFieldsStorePtr;
+
+void
+readInputFile(const char* const input_file, vector<ConstRdataPtr>& rdata_sets,
+ vector<ConstRdataFieldsStorePtr>& fields_sets)
+{
+ ifstream ifs;
+ ifs.open(input_file, ios_base::in);
+ if ((ifs.rdstate() & istream::failbit) != 0) {
+ cerr << "Failed to read input file: " << input_file << endl;
+ exit (1);
+ }
+ string line;
+ unsigned int linenum = 0;
+ while (getline(ifs, line), !ifs.eof()) {
+ ++linenum;
+ if (ifs.bad() || ifs.fail()) {
+ cerr << "Unexpected input at line " << linenum << endl;
+ exit (1);
+ }
+ if (line.empty() || line[0] == '#') {
+ continue; // skip comment and blank lines
+ }
+ istringstream iss(line);
+ string rrclass_string, rrtype_string;
+ stringbuf rdatabuf;
+ iss >> rrclass_string >> rrtype_string >> &rdatabuf;
+ if (iss.bad() || iss.fail()) {
+ cerr << "Unexpected input at line " << linenum << endl;
+ exit (1);
+ }
+ ConstRdataPtr rdata = createRdata(RRType(rrtype_string),
+ RRClass(rrclass_string),
+ rdatabuf.str());
+ rdata_sets.push_back(rdata);
+ fields_sets.push_back(ConstRdataFieldsStorePtr(
+ new RdataFieldsStore(rdata)));
+ }
+ ifs.close();
+}
+
+void
+usage() {
+ cerr << "Usage: rdatafields_bench [-n iterations] input_file" << endl;
+ exit (1);
+}
+}
+
+int
+main(int argc, char* argv[]) {
+ int ch;
+ int iteration = 10000;
+ while ((ch = getopt(argc, argv, "n:")) != -1) {
+ switch (ch) {
+ case 'n':
+ iteration = atoi(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 1) {
+ usage();
+ }
+ const char* const input_file = argv[0];
+
+ vector<ConstRdataPtr> rdata_sets;
+ vector<ConstRdataFieldsStorePtr> fields_sets;
+
+ readInputFile(input_file, rdata_sets, fields_sets);
+
+ cout << "Parameters:" << endl;
+ cout << " Iterations: " << iteration << endl;
+ cout << " Input File: " << input_file << endl;
+
+ typedef RdataRenderBenchMark<ConstRdataPtr> RdataBenchMark;
+ cout << "Benchmark for rendering with standard Rdata" << endl;
+ BenchMark<RdataBenchMark>(iteration, RdataBenchMark(rdata_sets));
+
+ typedef RdataRenderBenchMark<ConstRdataFieldsStorePtr> FieldsBenchMark;
+ cout << "Benchmark for rendering with RdataFields" << endl;
+ BenchMark<FieldsBenchMark>(iteration, FieldsBenchMark(fields_sets));
+
+ return (0);
+}
diff --git a/src/lib/dns/buffer.h b/src/lib/dns/buffer.h
deleted file mode 100644
index e100876..0000000
--- a/src/lib/dns/buffer.h
+++ /dev/null
@@ -1,431 +0,0 @@
-// Copyright (C) 2009 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 __BUFFER_H
-#define __BUFFER_H 1
-
-#include <vector>
-
-#include <string.h>
-
-#include <stdint.h>
-
-#include <exceptions/exceptions.h>
-
-#include <boost/shared_ptr.hpp>
-
-namespace isc {
-namespace dns {
-
-///
-/// \brief A standard DNS module exception that is thrown if an out-of-range
-/// buffer operation is being performed.
-///
-class InvalidBufferPosition : public Exception {
-public:
- InvalidBufferPosition(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
-};
-
-///\brief The \c InputBuffer class is a buffer abstraction for manipulating
-/// read-only data.
-///
-/// The main purpose of this class is to provide a safe placeholder for
-/// examining wire-format data received from a network.
-///
-/// Applications normally use this class only in a limited situation: as an
-/// interface between legacy I/O operation (such as receiving data from a BSD
-/// socket) and the rest of the BIND10 DNS library. One common usage of this
-/// class for an application would therefore be something like this:
-///
-/// \code unsigned char buf[1024];
-/// struct sockaddr addr;
-/// socklen_t addrlen = sizeof(addr);
-/// int cc = recvfrom(s, buf, sizeof(buf), 0, &addr, &addrlen);
-/// InputBuffer buffer(buf, cc);
-/// // pass the buffer to a DNS message object to parse the message \endcode
-///
-/// Other BIND10 DNS classes will then use methods of this class to get access
-/// to the data, but the application normally doesn't have to care about the
-/// details.
-///
-/// An \c InputBuffer object internally holds a reference to the given data,
-/// rather than make a local copy of the data. Also, it does not have an
-/// ownership of the given data. It is application's responsibility to ensure
-/// the data remains valid throughout the lifetime of the \c InputBuffer
-/// object. Likewise, this object generally assumes the data isn't modified
-/// throughout its lifetime; if the application modifies the data while this
-/// object retains a reference to it, the result is undefined. The application
-/// will also be responsible for releasing the data when it's not needed if it
-/// was dynamically acquired.
-///
-/// This is a deliberate design choice: although it's safer to make a local
-/// copy of the given data on construction, it would cause unacceptable
-/// performance overhead, especially considering that a DNS message can be
-/// as large as a few KB. Alternatively, we could allow the object to allocate
-/// memory internally and expose it to the application to store network data
-/// in it. This is also a bad design, however, in that we would effectively
-/// break the abstraction employed in the class, and do so by publishing
-/// "read-only" stuff as a writable memory region. Since there doesn't seem to
-/// be a perfect solution, we have adopted what we thought a "least bad" one.
-///
-/// Methods for reading data from the buffer generally work like an input
-/// stream: it begins with the head of the data, and once some length of data
-/// is read from the buffer, the next read operation will take place from the
-/// head of the unread data. An object of this class internally holds (a
-/// notion of) where the next read operation should start. We call it the
-/// <em>read position</em> in this document.
-class InputBuffer {
-public:
- ///
- /// \name Constructors and Destructor
- //@{
- /// \brief Constructor from variable length of data.
- ///
- /// It is caller's responsibility to ensure that the data is valid as long
- /// as the buffer exists.
- /// \param data A pointer to the data stored in the buffer.
- /// \param len The length of the data in bytes.
- InputBuffer(const void* data, size_t len) :
- position_(0), data_(static_cast<const uint8_t*>(data)), len_(len) {}
- //@}
-
- ///
- /// \name Getter Methods
- //@{
- /// \brief Return the length of the data stored in the buffer.
- size_t getLength() const { return (len_); }
- /// \brief Return the current read position.
- size_t getPosition() const { return (position_); }
- //@}
-
- ///
- /// \name Setter Methods
- ///
- //@{
- /// \brief Set the read position of the buffer to the given value.
- ///
- /// The new position must be in the valid range of the buffer; otherwise
- /// an exception of class \c isc::dns::InvalidBufferPosition will be thrown.
- /// \param position The new position (offset from the beginning of the
- /// buffer).
- void setPosition(size_t position)
- {
- if (position > len_)
- isc_throw(InvalidBufferPosition, "position is too large");
- position_ = position;
- }
- //@}
-
- ///
- /// \name Methods for reading data from the buffer.
- //@{
- /// \brief Read an unsigned 8-bit integer from the buffer and return it.
- ///
- /// If the remaining length of the buffer is smaller than 8-bit, an
- /// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
- uint8_t readUint8()
- {
- if (position_ + sizeof(uint8_t) > len_) {
- isc_throw(InvalidBufferPosition, "read beyond end of buffer");
- }
-
- return (data_[position_++]);
- }
- /// \brief Read an unsigned 16-bit integer in network byte order from the
- /// buffer, convert it to host byte order, and return it.
- ///
- /// If the remaining length of the buffer is smaller than 16-bit, an
- /// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
- uint16_t readUint16()
- {
- uint16_t data;
- const uint8_t* cp;
-
- if (position_ + sizeof(data) > len_) {
- isc_throw(InvalidBufferPosition, "read beyond end of buffer");
- }
-
- cp = &data_[position_];
- data = ((unsigned int)(cp[0])) << 8;
- data |= ((unsigned int)(cp[1]));
- position_ += sizeof(data);
-
- return (data);
- }
- /// \brief Read an unsigned 32-bit integer in network byte order from the
- /// buffer, convert it to host byte order, and return it.
- ///
- /// If the remaining length of the buffer is smaller than 32-bit, an
- /// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
- uint32_t readUint32()
- {
- uint32_t data;
- const uint8_t* cp;
-
- if (position_ + sizeof(data) > len_) {
- isc_throw(InvalidBufferPosition, "read beyond end of buffer");
- }
-
- cp = &data_[position_];
- data = ((unsigned int)(cp[0])) << 24;
- data |= ((unsigned int)(cp[1])) << 16;
- data |= ((unsigned int)(cp[2])) << 8;
- data |= ((unsigned int)(cp[3]));
- position_ += sizeof(data);
-
- return (data);
- }
- /// \brief Read data of the specified length from the buffer and copy it to
- /// the caller supplied buffer.
- ///
- /// The data is copied as stored in the buffer; no conversion is performed.
- /// If the remaining length of the buffer is smaller than the specified
- /// length, an exception of class \c isc::dns::InvalidBufferPosition will
- /// be thrown.
- void readData(void* data, size_t len)
- {
- if (position_ + len > len_) {
- isc_throw(InvalidBufferPosition, "read beyond end of buffer");
- }
-
- memcpy(data, &data_[position_], len);
- position_ += len;
- }
- //@}
-
-private:
- size_t position_;
-
- // XXX: The following must be private, but for a short term workaround with
- // Boost.Python binding, we changed it to protected. We should soon
- // revisit it.
-protected:
- const uint8_t* data_;
- size_t len_;
-};
-
-///
-///\brief The \c OutputBuffer class is a buffer abstraction for manipulating
-/// mutable data.
-///
-/// The main purpose of this class is to provide a safe workplace for
-/// constructing wire-format data to be sent out to a network. Here,
-/// <em>safe</em> means that it automatically allocates necessary memory and
-/// avoid buffer overrun.
-///
-/// Like for the \c InputBuffer class, applications normally use this class only
-/// in a limited situation. One common usage of this class for an application
-/// would be something like this:
-///
-/// \code OutputBuffer buffer(4096); // give a sufficiently large initial size
-/// // pass the buffer to a DNS message object to construct a wire-format
-/// // DNS message.
-/// struct sockaddr to;
-/// sendto(s, buffer.getData(), buffer.getLength(), 0, &to, sizeof(to));
-/// \endcode
-///
-/// where the \c getData() method gives a reference to the internal memory
-/// region stored in the \c buffer object. This is a suboptimal design in that
-/// it exposes an encapsulated "handle" of an object to its user.
-/// Unfortunately, there is no easy way to avoid this without involving
-/// expensive data copy if we want to use this object with a legacy API such as
-/// a BSD socket interface. And, indeed, this is one major purpose for this
-/// object. Applications should use this method only under such a special
-/// circumstance. It should also be noted that the memory region returned by
-/// \c getData() may be invalidated after a subsequent write operation.
-///
-/// An \c OutputBuffer class object automatically extends its memory region when
-/// data is written beyond the end of the current buffer. However, it will
-/// involve performance overhead such as reallocating more memory and copying
-/// data. It is therefore recommended to construct the buffer object with a
-/// sufficiently large initial size.
-/// The \c getCapacity() method provides the current maximum size of data
-/// (including the portion already written) that can be written into the buffer
-/// without causing memory reallocation.
-///
-/// Methods for writing data into the buffer generally work like an output
-/// stream: it begins with the head of the buffer, and once some length of data
-/// is written into the buffer, the next write operation will take place from
-/// the end of the buffer. Other methods to emulate "random access" are also
-/// provided (e.g., \c writeUint16At()). The normal write operations are
-/// normally exception-free as this class automatically extends the buffer
-/// when necessary. However, in extreme cases such as an attempt of writing
-/// multi-GB data, a separate exception (e.g., \c std::bad_alloc) may be thrown
-/// by the system. This also applies to the constructor with a very large
-/// initial size.
-///
-/// Note to developers: it may make more sense to introduce an abstract base
-/// class for the \c OutputBuffer and define the simple implementation as a
-/// a concrete derived class. That way we can provide flexibility for future
-/// extension such as more efficient buffer implementation or allowing users
-/// to have their own customized version without modifying the source code.
-/// We in fact considered that option, but at the moment chose the simpler
-/// approach with a single concrete class because it may make the
-/// implementation unnecessarily complicated while we were still not certain
-/// if we really want that flexibility. We may revisit the class design as
-/// we see more applications of the class. The same considerations apply to
-/// the \c InputBuffer and \c MessageRenderer classes.
-class OutputBuffer {
-public:
- ///
- /// \name Constructors and Destructor
- ///
- //@{
- /// \brief Constructor from the initial size of the buffer.
- ///
- /// \param len The initial length of the buffer in bytes.
- OutputBuffer(size_t len) { data_.reserve(len); }
- //@}
-
- ///
- /// \name Getter Methods
- ///
- //@{
- /// \brief Return the current capacity of the buffer.
- size_t getCapacity() const { return (data_.capacity()); }
- /// \brief Return a pointer to the head of the data stored in the buffer.
- ///
- /// The caller can assume that the subsequent \c getLength() bytes are
- /// identical to the stored data of the buffer.
- ///
- /// Note: The pointer returned by this method may be invalidated after a
- /// subsequent write operation.
- const void* getData() const { return (&data_[0]); }
- /// \brief Return the length of data written in the buffer.
- size_t getLength() const { return (data_.size()); }
- /// \brief Return the value of the buffer at the specified position.
- ///
- /// \c pos must specify the valid position of the buffer; otherwise an
- /// exception class of \c InvalidBufferPosition will be thrown.
- ///
- /// \param pos The position in the buffer to be returned.
- const uint8_t& operator[](size_t pos) const
- {
- if (pos >= data_.size()) {
- isc_throw(InvalidBufferPosition, "read at invalid position");
- }
- return (data_[pos]);
- }
- //@}
-
- ///
- /// \name Methods for writing data into the buffer.
- ///
- //@{
- /// \brief Insert a specified length of gap at the end of the buffer.
- ///
- /// The caller should not assume any particular value to be inserted.
- /// This method is provided as a shortcut to make a hole in the buffer
- /// that is to be filled in later, e.g, by \ref writeUint16At().
- /// \param len The length of the gap to be inserted in bytes.
- void skip(size_t len) { data_.insert(data_.end(), len, 0); }
-
- /// \brief Trim the specified length of data from the end of the buffer.
- ///
- /// The specified length must not exceed the current data size of the
- /// buffer; otherwise an exception of class \c isc::OutOfRange will
- /// be thrown.
- ///
- /// \param len The length of data that should be trimmed.
- void trim(size_t len)
- {
- if (len > data_.size()) {
- isc_throw(OutOfRange, "trimming too large from output buffer");
- }
- data_.resize(data_.size() - len);
- }
- /// \brief Clear buffer content.
- ///
- /// This method can be used to re-initialize and reuse the buffer without
- /// constructing a new one.
- void clear() { data_.clear(); }
- /// \brief Write an unsigned 8-bit integer into the buffer.
- ///
- /// \param data The 8-bit integer to be written into the buffer.
- void writeUint8(uint8_t data) { data_.push_back(data); }
-
- /// \brief Write an unsigned 16-bit integer in host byte order into the
- /// buffer in network byte order.
- ///
- /// \param data The 16-bit integer to be written into the buffer.
- void writeUint16(uint16_t data)
- {
- data_.push_back(static_cast<uint8_t>((data & 0xff00U) >> 8));
- data_.push_back(static_cast<uint8_t>(data & 0x00ffU));
- }
- /// \brief Write an unsigned 16-bit integer in host byte order at the
- /// specified position of the buffer in network byte order.
- ///
- /// The buffer must have a sufficient room to store the given data at the
- /// given position, that is, <code>pos + 2 < getLength()</code>;
- /// otherwise an exception of class \c isc::dns::InvalidBufferPosition will
- /// be thrown.
- /// Note also that this method never extends the buffer.
- ///
- /// \param data The 16-bit integer to be written into the buffer.
- /// \param pos The beginning position in the buffer to write the data.
- void writeUint16At(uint16_t data, size_t pos)
- {
- if (pos + sizeof(data) > data_.size()) {
- isc_throw(InvalidBufferPosition, "write at invalid position");
- }
-
- data_[pos] = static_cast<uint8_t>((data & 0xff00U) >> 8);
- data_[pos + 1] = static_cast<uint8_t>(data & 0x00ffU);
- }
- /// \brief Write an unsigned 32-bit integer in host byte order
- /// into the buffer in network byte order.
- ///
- /// \param data The 32-bit integer to be written into the buffer.
- void writeUint32(uint32_t data)
- {
- data_.push_back(static_cast<uint8_t>((data & 0xff000000) >> 24));
- data_.push_back(static_cast<uint8_t>((data & 0x00ff0000) >> 16));
- data_.push_back(static_cast<uint8_t>((data & 0x0000ff00) >> 8));
- data_.push_back(static_cast<uint8_t>(data & 0x000000ff));
- }
- /// \brief Copy an arbitrary length of data into the buffer.
- ///
- /// No conversion on the copied data is performed.
- ///
- /// \param data A pointer to the data to be copied into the buffer.
- /// \param len The length of the data in bytes.
- void writeData(const void *data, size_t len)
- {
- const uint8_t* cp = static_cast<const uint8_t*>(data);
- data_.insert(data_.end(), cp, cp + len);
- }
- //@}
-
-private:
- std::vector<uint8_t> data_;
-};
-
-/// \brief Pointer-like types pointing to \c InputBuffer or \c OutputBuffer
-///
-/// These types are expected to be used as an argument in asynchronous
-/// callback functions. The internal reference-counting will ensure that
-/// that ongoing state information will not be lost if the object
-/// that originated the asynchronous call falls out of scope.
-typedef boost::shared_ptr<InputBuffer> InputBufferPtr;
-typedef boost::shared_ptr<OutputBuffer> OutputBufferPtr;
-
-}
-}
-#endif // __BUFFER_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/dns/dnssectime.cc b/src/lib/dns/dnssectime.cc
deleted file mode 100644
index c889178..0000000
--- a/src/lib/dns/dnssectime.cc
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <stdint.h>
-
-#include <sys/time.h>
-
-#include <string>
-#include <iomanip>
-#include <iostream>
-#include <sstream>
-
-#include <stdio.h>
-#include <time.h>
-
-#include <exceptions/exceptions.h>
-
-#include <dns/dnssectime.h>
-
-using namespace std;
-
-namespace {
-int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
-
-inline bool
-isLeap(const int y) {
- return ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0);
-}
-
-unsigned int
-yearSecs(const int year) {
- return ((isLeap(year) ? 366 : 365 ) * 86400);
-}
-
-unsigned int
-monthSecs(const int month, const int year) {
- return ((days[month] + ((month == 1 && isLeap(year)) ? 1 : 0 )) * 86400);
-}
-}
-
-namespace isc {
-namespace dns {
-
-string
-timeToText64(uint64_t value) {
- struct tm tm;
- unsigned int secs;
-
- // We cannot rely on gmtime() because time_t may not be of 64 bit
- // integer. The following conversion logic is borrowed from BIND 9.
- tm.tm_year = 70;
- while ((secs = yearSecs(tm.tm_year + 1900)) <= value) {
- value -= secs;
- ++tm.tm_year;
- if (tm.tm_year + 1900 > 9999) {
- isc_throw(InvalidTime,
- "Time value out of range (year > 9999): " <<
- tm.tm_year + 1900);
- }
- }
- tm.tm_mon = 0;
- while ((secs = monthSecs(tm.tm_mon, tm.tm_year + 1900)) <= value) {
- value -= secs;
- tm.tm_mon++;
- }
- tm.tm_mday = 1;
- while (86400 <= value) {
- value -= 86400;
- ++tm.tm_mday;
- }
- tm.tm_hour = 0;
- while (3600 <= value) {
- value -= 3600;
- ++tm.tm_hour;
- }
- tm.tm_min = 0;
- while (60 <= value) {
- value -= 60;
- ++tm.tm_min;
- }
- tm.tm_sec = value; // now t < 60, so this substitution is safe.
-
- ostringstream oss;
- oss << setfill('0')
- << setw(4) << tm.tm_year + 1900
- << setw(2) << tm.tm_mon + 1
- << setw(2) << tm.tm_mday
- << setw(2) << tm.tm_hour
- << setw(2) << tm.tm_min
- << setw(2) << tm.tm_sec;
- return (oss.str());
-}
-
-// timeToText32() below uses the current system time. To test it with
-// unusual current time values we introduce the following function pointer;
-// when it's non NULL, we call it to get the (normally faked) current time.
-// Otherwise we use the standard gettimeofday(2). This hook is specifically
-// intended for testing purposes, so, even if it's visible outside of this
-// library, it's not even declared in a header file.
-namespace dnssectime {
-namespace detail {
-int64_t (*gettimeFunction)() = NULL;
-}
-}
-
-namespace {
-int64_t
-gettimeofdayWrapper() {
- using namespace dnssectime::detail;
- if (gettimeFunction != NULL) {
- return (gettimeFunction());
- }
-
- struct timeval now;
- gettimeofday(&now, NULL);
-
- return (static_cast<int64_t>(now.tv_sec));
-}
-}
-
-string
-timeToText32(const uint32_t value) {
- // We first adjust the time to the closest epoch based on the current time.
- // Note that the following variables must be signed in order to handle
- // time until year 2038 correctly.
- const int64_t start = gettimeofdayWrapper() - 0x7fffffff;
- int64_t base = 0;
- int64_t t;
- while ((t = (base + value)) < start) {
- base += 0x100000000LL;
- }
-
- // Then convert it to text.
- return (timeToText64(t));
-}
-
-namespace {
-const size_t DATE_LEN = 14; // YYYYMMDDHHmmSS
-
-inline void
-checkRange(const int min, const int max, const int value,
- const string& valname)
-{
- if ((value >= min) && (value <= max)) {
- return;
- }
- isc_throw(InvalidTime, "Invalid " << valname << "value: " << value);
-}
-}
-
-uint64_t
-timeFromText64(const string& time_txt) {
- // Confirm the source only consists digits. sscanf() allows some
- // minor exceptions.
- for (int i = 0; i < time_txt.length(); ++i) {
- if (!isdigit(time_txt.at(i))) {
- isc_throw(InvalidTime, "Couldn't convert non-numeric time value: "
- << time_txt);
- }
- }
-
- int year, month, day, hour, minute, second;
- if (time_txt.length() != DATE_LEN ||
- sscanf(time_txt.c_str(), "%4d%2d%2d%2d%2d%2d",
- &year, &month, &day, &hour, &minute, &second) != 6)
- {
- isc_throw(InvalidTime, "Couldn't convert time value: " << time_txt);
- }
-
- checkRange(1970, 9999, year, "year");
- checkRange(1, 12, month, "month");
- checkRange(1, days[month - 1] + ((month == 2 && isLeap(year)) ? 1 : 0),
- day, "day");
- checkRange(0, 23, hour, "hour");
- checkRange(0, 59, minute, "minute");
- checkRange(0, 60, second, "second"); // 60 == leap second.
-
- uint64_t timeval = second + (60 * minute) + (3600 * hour) +
- ((day - 1) * 86400);
- for (int m = 0; m < (month - 1); ++m) {
- timeval += days[m] * 86400;
- }
- if (isLeap(year) && month > 2) {
- timeval += 86400;
- }
- for (int y = 1970; y < year; ++y) {
- timeval += ((isLeap(y) ? 366 : 365 ) * 86400);
- }
-
- return (timeval);
-}
-
-uint32_t
-timeFromText32(const string& time_txt) {
- // The implicit conversion from uint64_t to uint32_t should just work here,
- // because we only need to drop higher 32 bits.
- return (timeFromText64(time_txt));
-}
-}
-}
diff --git a/src/lib/dns/dnssectime.h b/src/lib/dns/dnssectime.h
deleted file mode 100644
index baf866f..0000000
--- a/src/lib/dns/dnssectime.h
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright (C) 2009 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 __DNSSECTIME_H
-#define __DNSSECTIME_H 1
-
-#include <sys/types.h>
-#include <stdint.h>
-
-#include <exceptions/exceptions.h>
-
-//
-// Note: this helper module isn't specific to the DNS protocol per se.
-// We should probably move this to somewhere else, possibly in some common
-// utility area.
-//
-
-namespace isc {
-namespace dns {
-
-///
-/// \brief A standard DNS (or ISC) module exception that is thrown if
-/// a time conversion function encounters bad input
-///
-class InvalidTime : public Exception {
-public:
- InvalidTime(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
-};
-
-///
-/// \name DNSSEC time conversion functions.
-///
-/// These functions convert between times represented in seconds (in integer)
-/// since epoch and those in the textual form used in the RRSIG records.
-/// For integers we provide both 32-bit and 64-bit versions.
-/// The RRSIG expiration and inception fields are both 32-bit unsigned
-/// integers, so 32-bit versions would be more useful for protocol operations.
-/// However, with 32-bit integers we need to take into account wrap-around
-/// points and compare values using the serial number arithmetic as specified
-/// in RFC4034, which would be more error prone. We therefore provide 64-bit
-/// versions, too.
-///
-/// The timezone is always UTC for these functions.
-//@{
-/// Convert textual DNSSEC time to integer, 64-bit version.
-///
-/// The textual form must only consist of digits and be in the form of
-/// YYYYMMDDHHmmSS, where:
-/// - YYYY must be between 1970 and 9999
-/// - MM must be between 01 and 12
-/// - DD must be between 01 and 31 and must be a valid day for the month
-/// represented in 'MM'. For example, if MM is 04, DD cannot be 31.
-/// DD can be 29 when MM is 02 only when YYYY is a leap year.
-/// - HH must be between 00 and 23
-/// - mm must be between 00 and 59
-/// - SS must be between 00 and 60
-///
-/// For all fields the range includes the begin and end values. Note that
-/// 60 is allowed for 'SS', intending a leap second, although in real operation
-/// it's unlikely to be specified.
-///
-/// If the given text is valid, this function converts it to an unsigned
-/// 64-bit number of seconds since epoch (1 January 1970 00:00:00) and returns
-/// the converted value. 64 bits are sufficient to represent all possible
-/// values for the valid format uniquely, so there is no overflow.
-///
-/// \note RFC4034 also defines the textual form of an unsigned decimal integer
-/// for the corresponding time in seconds. This function doesn't support
-/// this form, and if given it throws an exception of class \c InvalidTime.
-///
-/// \exception InvalidTime The given textual representation is invalid.
-///
-/// \param time_txt Textual time in the form of YYYYMMDDHHmmSS
-/// \return Seconds since epoch corresponding to \c time_txt
-uint64_t
-timeFromText64(const std::string& time_txt);
-
-/// Convert textual DNSSEC time to integer, 32-bit version.
-///
-/// This version is the same as \c timeFromText64() except that the return
-/// value is wrapped around to an unsigned 32-bit integer, simply dropping
-/// the upper 32 bits.
-uint32_t
-timeFromText32(const std::string& time_txt);
-
-/// Convert integral DNSSEC time to textual form, 64-bit version.
-///
-/// This function takes an integer that would be seconds since epoch and
-/// converts it in the form of YYYYMMDDHHmmSS. For example, if \c value is
-/// 0, it returns "19700101000000". If the value corresponds to a point
-/// of time on and after year 10,000, which cannot be represented in the
-/// YYYY... form, an exception of class \c InvalidTime will be thrown.
-///
-/// \exception InvalidTime The given time specifies on or after year 10,000.
-/// \exception Other A standard exception, if resource allocation for the
-/// returned text fails.
-///
-/// \param value Seconds since epoch to be converted.
-/// \return Textual representation of \c value in the form of YYYYMMDDHHmmSS.
-std::string
-timeToText64(uint64_t value);
-
-/// Convert integral DNSSEC time to textual form, 32-bit version.
-///
-/// This version is the same as \c timeToText64(), but the time value
-/// is expected to be the lower 32 bits of the full 64-bit value.
-/// These two will be different on and after a certain point of time
-/// in year 2106, so this function internally resolves the ambiguity
-/// using the current system time at the time of function call;
-/// it first identifies the range of [N*2^32 - 2^31, N*2^32 + 2^31)
-/// that contains the current time, and interprets \c value in the context
-/// of that range. It then applies the same process as \c timeToText64().
-///
-/// There is one important exception in this processing, however.
-/// Until 19 Jan 2038 03:14:08 (2^31 seconds since epoch), this range
-/// would contain time before epoch. In order to ensure the returned
-/// value is also a valid input to \c timeFromText, this function uses
-/// a special range [0, 2^32) until that time. As a result, all upper
-/// half of the 32-bit values are treated as a future time. For example,
-/// 2^32-1 (the highest value in 32-bit unsigned integers) will be converted
-/// to "21060207062815", instead of "19691231235959".
-std::string
-timeToText32(const uint32_t value);
-
-//@}
-}
-}
-
-#endif // __DNSSECTIME_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/dns/edns.cc b/src/lib/dns/edns.cc
index 6e25624..447b479 100644
--- a/src/lib/dns/edns.cc
+++ b/src/lib/dns/edns.cc
@@ -36,6 +36,7 @@
using namespace std;
using namespace boost;
using namespace isc::dns::rdata;
+using namespace isc::util;
namespace isc {
namespace dns {
@@ -109,19 +110,25 @@ EDNS::toText() const {
return (ret);
}
+namespace {
+/// Helper function to define unified implementation for the public versions
+/// of toWire().
template <typename Output>
int
-EDNS::toWire(Output& output, const uint8_t extended_rcode) const {
+toWireCommon(Output& output, const uint8_t version,
+ const uint16_t udp_size, const bool dnssec_aware,
+ const uint8_t extended_rcode)
+{
// Render EDNS OPT RR
uint32_t extrcode_flags = extended_rcode << EXTRCODE_SHIFT;
- extrcode_flags |= (version_ << VERSION_SHIFT) & VERSION_MASK;
- if (dnssec_aware_) {
+ extrcode_flags |= (version << VERSION_SHIFT) & VERSION_MASK;
+ if (dnssec_aware) {
extrcode_flags |= EXTFLAG_DO;
}
// Construct an RRset corresponding to the EDNS.
// We don't support any options for now, so the OPT RR can be empty.
- RRsetPtr edns_rrset(new RRset(Name::ROOT_NAME(), RRClass(udp_size_),
+ RRsetPtr edns_rrset(new RRset(Name::ROOT_NAME(), RRClass(udp_size),
RRType::OPT(), RRTTL(extrcode_flags)));
edns_rrset->addRdata(ConstRdataPtr(new generic::OPT()));
@@ -129,9 +136,12 @@ EDNS::toWire(Output& output, const uint8_t extended_rcode) const {
return (1);
}
+}
unsigned int
-EDNS::toWire(MessageRenderer& renderer, const uint8_t extended_rcode) const {
+EDNS::toWire(AbstractMessageRenderer& renderer,
+ const uint8_t extended_rcode) const
+{
// If adding the OPT RR would exceed the size limit, don't do it.
// 11 = len(".") + type(2byte) + class(2byte) + TTL(4byte) + RDLEN(2byte)
// (RDATA is empty in this simple implementation)
@@ -139,12 +149,16 @@ EDNS::toWire(MessageRenderer& renderer, const uint8_t extended_rcode) const {
return (0);
}
- return (toWire<MessageRenderer>(renderer, extended_rcode));
+ return (toWireCommon(renderer, version_, udp_size_, dnssec_aware_,
+ extended_rcode));
}
unsigned int
-EDNS::toWire(OutputBuffer& buffer, const uint8_t extended_rcode) const {
- return (toWire<OutputBuffer>(buffer, extended_rcode));
+EDNS::toWire(isc::util::OutputBuffer& buffer,
+ const uint8_t extended_rcode) const
+{
+ return (toWireCommon(buffer, version_, udp_size_, dnssec_aware_,
+ extended_rcode));
}
EDNS*
diff --git a/src/lib/dns/edns.h b/src/lib/dns/edns.h
index ca3db47..a7bc4c4 100644
--- a/src/lib/dns/edns.h
+++ b/src/lib/dns/edns.h
@@ -24,11 +24,15 @@
#include <dns/rdata.h>
namespace isc {
+namespace util {
+class OutputBuffer;
+}
+
namespace dns {
class EDNS;
class Name;
-class MessageRenderer;
+class AbstractMessageRenderer;
class RRClass;
class RRTTL;
class RRType;
@@ -310,7 +314,7 @@ public:
/// \param extended_rcode Upper 8 bits of extended RCODE to be rendered as
/// part of the EDNS OPT RR.
/// \return 1 if the OPT RR fits in the message size limit; otherwise 0.
- unsigned int toWire(MessageRenderer& renderer,
+ unsigned int toWire(AbstractMessageRenderer& renderer,
const uint8_t extended_rcode) const;
/// \brief Render the \c EDNS in the wire format.
@@ -319,7 +323,7 @@ public:
/// except it renders the OPT RR in an \c OutputBuffer and therefore
/// does not care about message size limit.
/// As a consequence it always returns 1.
- unsigned int toWire(OutputBuffer& buffer,
+ unsigned int toWire(isc::util::OutputBuffer& buffer,
const uint8_t extended_rcode) const;
/// \brief Convert the EDNS to a string.
@@ -350,12 +354,6 @@ public:
// something like this.
//void addOption();
-private:
- /// Helper method to define unified implementation for the public versions
- /// of toWire().
- template <typename Output>
- int toWire(Output& output, const uint8_t extended_rcode) const;
-
public:
/// \brief The highest EDNS version this implementation supports.
static const uint8_t SUPPORTED_VERSION = 0;
@@ -382,7 +380,7 @@ private:
///
/// The intended usage of this function is to parse an OPT RR of an incoming
/// DNS message, while updating the RCODE of the message.
-/// One common usage patter is as follows:
+/// One common usage pattern is as follows:
///
/// \code Message msg;
/// ...
diff --git a/src/lib/dns/gen-rdatacode.py.in b/src/lib/dns/gen-rdatacode.py.in
index 04e5c6a..b3c8da2 100755
--- a/src/lib/dns/gen-rdatacode.py.in
+++ b/src/lib/dns/gen-rdatacode.py.in
@@ -86,6 +86,11 @@ def import_classheader(class_txt, type_txt, type_code, file):
continue
if re.match('// BEGIN_ISC_NAMESPACE', line):
content += 'namespace isc {\n'
+ content += 'namespace util {\n'
+ content += '''
+class InputBuffer;
+class OutputBuffer;\n'''
+ content += '}\n\n'
content += 'namespace dns {\n'
continue
if re.match('// BEGIN_RDATA_NAMESPACE', line):
@@ -105,17 +110,15 @@ def import_classheader(class_txt, type_txt, type_code, file):
content += line
if re.match('// BEGIN_COMMON_DECLARATIONS', line):
content += '''
-class InputBuffer;
-class OutputBuffer;
-class MessageRenderer;\n\n'''
+class AbstractMessageRenderer;\n\n'''
if re.match('\s+// BEGIN_COMMON_MEMBERS$', line):
content += '''
explicit ''' + type_utxt + '''(const std::string& type_str);
- ''' + type_utxt + '''(InputBuffer& buffer, size_t rdata_len);
+ ''' + type_utxt + '''(isc::util::InputBuffer& buffer, size_t rdata_len);
''' + type_utxt + '''(const ''' + type_utxt + '''& other);
virtual std::string toText() const;
- virtual void toWire(OutputBuffer& buffer) const;
- virtual void toWire(MessageRenderer& renderer) const;
+ virtual void toWire(isc::util::OutputBuffer& buffer) const;
+ virtual void toWire(AbstractMessageRenderer& renderer) const;
virtual int compare(const Rdata& other) const;\n\n'''
rdata_header.close()
return content
diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc
index 9eae605..bf7ccd5 100644
--- a/src/lib/dns/message.cc
+++ b/src/lib/dns/message.cc
@@ -15,6 +15,7 @@
#include <stdint.h>
#include <algorithm>
+#include <cassert>
#include <string>
#include <sstream>
#include <vector>
@@ -25,7 +26,8 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
+
#include <dns/edns.h>
#include <dns/exceptions.h>
#include <dns/message.h>
@@ -39,10 +41,12 @@
#include <dns/rrtype.h>
#include <dns/rrttl.h>
#include <dns/rrset.h>
+#include <dns/tsig.h>
using namespace std;
using namespace boost;
using namespace isc::dns::rdata;
+using namespace isc::util;
namespace isc {
namespace dns {
@@ -79,7 +83,7 @@ const unsigned int HEADERFLAG_MASK = 0x87b0;
const uint16_t MESSAGE_REPLYPRESERVE = (Message::HEADERFLAG_RD |
Message::HEADERFLAG_CD);
-const char *sectiontext[] = {
+const char* const sectiontext[] = {
"QUESTION",
"ANSWER",
"AUTHORITY",
@@ -112,8 +116,8 @@ public:
vector<QuestionPtr> questions_;
vector<RRsetPtr> rrsets_[NUM_SECTIONS];
ConstEDNSPtr edns_;
+ ConstTSIGRecordPtr tsig_rr_;
- // tsig/sig0: TODO
// RRsetsSorter* sorter_; : TODO
void init();
@@ -121,6 +125,17 @@ public:
void setRcode(const Rcode& rcode);
int parseQuestion(InputBuffer& buffer);
int parseSection(const Message::Section section, InputBuffer& buffer);
+ void addRR(Message::Section section, const Name& name,
+ const RRClass& rrclass, const RRType& rrtype,
+ const RRTTL& ttl, ConstRdataPtr rdata);
+ void addEDNS(Message::Section section, const Name& name,
+ const RRClass& rrclass, const RRType& rrtype,
+ const RRTTL& ttl, const Rdata& rdata);
+ void addTSIG(Message::Section section, unsigned int count,
+ const InputBuffer& buffer, size_t start_position,
+ const Name& name, const RRClass& rrclass,
+ const RRTTL& ttl, const Rdata& rdata);
+ void toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx);
};
MessageImpl::MessageImpl(Message::Mode mode) :
@@ -138,6 +153,7 @@ MessageImpl::init() {
rcode_ = NULL;
opcode_ = NULL;
edns_ = EDNSPtr();
+ tsig_rr_ = ConstTSIGRecordPtr();
for (int i = 0; i < NUM_SECTIONS; ++i) {
counts_[i] = 0;
@@ -162,6 +178,154 @@ MessageImpl::setRcode(const Rcode& rcode) {
rcode_ = &rcode_placeholder_;
}
+namespace {
+// This helper class is used by MessageImpl::toWire() to render a set of
+// RRsets of a specific section of message to a given MessageRenderer.
+//
+// A RenderSection object is expected to be used with a QuestionIterator or
+// SectionIterator. Its operator() is called for each RRset as the iterator
+// iterates over the corresponding section, and it renders the RRset to
+// the given MessageRenderer, while counting the number of RRs (note: not
+// RRsets) successfully rendered. If the MessageRenderer reports the need
+// for truncation (via its isTruncated() method), the RenderSection object
+// stops rendering further RRsets. In addition, unless partial_ok (given on
+// construction) is true, it removes any RRs that are partially rendered
+// from the MessageRenderer.
+//
+// On the completion of rendering the entire section, the owner of the
+// RenderSection object can get the number of rendered RRs via the
+// getTotalCount() method.
+template <typename T>
+struct RenderSection {
+ RenderSection(AbstractMessageRenderer& renderer, const bool partial_ok) :
+ counter_(0), renderer_(renderer), partial_ok_(partial_ok),
+ truncated_(false)
+ {}
+ void operator()(const T& entry) {
+ // If it's already truncated, ignore the rest of the section.
+ if (truncated_) {
+ return;
+ }
+ const size_t pos0 = renderer_.getLength();
+ counter_ += entry->toWire(renderer_);
+ if (renderer_.isTruncated()) {
+ truncated_ = true;
+ if (!partial_ok_) {
+ // roll back to the end of the previous RRset.
+ renderer_.trim(renderer_.getLength() - pos0);
+ }
+ }
+ }
+ unsigned int getTotalCount() { return (counter_); }
+ unsigned int counter_;
+ AbstractMessageRenderer& renderer_;
+ const bool partial_ok_;
+ bool truncated_;
+};
+}
+
+void
+MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
+ if (mode_ != Message::RENDER) {
+ isc_throw(InvalidMessageOperation,
+ "Message rendering attempted in non render mode");
+ }
+ if (rcode_ == NULL) {
+ isc_throw(InvalidMessageOperation,
+ "Message rendering attempted without Rcode set");
+ }
+ if (opcode_ == NULL) {
+ isc_throw(InvalidMessageOperation,
+ "Message rendering attempted without Opcode set");
+ }
+
+ // reserve room for the header
+ renderer.skip(HEADERLEN);
+
+ uint16_t qdcount =
+ for_each(questions_.begin(), questions_.end(),
+ RenderSection<QuestionPtr>(renderer, false)).getTotalCount();
+
+ // TODO: sort RRsets in each section based on configuration policy.
+ uint16_t ancount = 0;
+ if (!renderer.isTruncated()) {
+ ancount =
+ for_each(rrsets_[Message::SECTION_ANSWER].begin(),
+ rrsets_[Message::SECTION_ANSWER].end(),
+ RenderSection<RRsetPtr>(renderer, true)).getTotalCount();
+ }
+ uint16_t nscount = 0;
+ if (!renderer.isTruncated()) {
+ nscount =
+ for_each(rrsets_[Message::SECTION_AUTHORITY].begin(),
+ rrsets_[Message::SECTION_AUTHORITY].end(),
+ RenderSection<RRsetPtr>(renderer, true)).getTotalCount();
+ }
+ uint16_t arcount = 0;
+ if (renderer.isTruncated()) {
+ flags_ |= Message::HEADERFLAG_TC;
+ } else {
+ arcount =
+ for_each(rrsets_[Message::SECTION_ADDITIONAL].begin(),
+ rrsets_[Message::SECTION_ADDITIONAL].end(),
+ RenderSection<RRsetPtr>(renderer, false)).getTotalCount();
+ }
+
+ // Add EDNS OPT RR if necessary. Basically, we add it only when EDNS
+ // has been explicitly set. However, if the RCODE would require it and
+ // no EDNS has been set we generate a temporary local EDNS and use it.
+ if (!renderer.isTruncated()) {
+ ConstEDNSPtr local_edns = edns_;
+ if (!local_edns && rcode_->getExtendedCode() != 0) {
+ local_edns = ConstEDNSPtr(new EDNS());
+ }
+ if (local_edns) {
+ arcount += local_edns->toWire(renderer, rcode_->getExtendedCode());
+ }
+ }
+
+ // Adjust the counter buffer.
+ // XXX: these may not be equal to the number of corresponding entries
+ // in rrsets_[] or questions_ if truncation occurred or an EDNS OPT RR
+ // was inserted. This is not good, and we should revisit the entire
+ // design.
+ counts_[Message::SECTION_QUESTION] = qdcount;
+ counts_[Message::SECTION_ANSWER] = ancount;
+ counts_[Message::SECTION_AUTHORITY] = nscount;
+ counts_[Message::SECTION_ADDITIONAL] = arcount;
+
+ // fill in the header
+ size_t header_pos = 0;
+ renderer.writeUint16At(qid_, header_pos);
+ header_pos += sizeof(uint16_t);
+
+ uint16_t codes_and_flags =
+ (opcode_->getCode() << OPCODE_SHIFT) & OPCODE_MASK;
+ codes_and_flags |= (rcode_->getCode() & RCODE_MASK);
+ codes_and_flags |= (flags_ & HEADERFLAG_MASK);
+ renderer.writeUint16At(codes_and_flags, header_pos);
+ header_pos += sizeof(uint16_t);
+ // TODO: should avoid repeated pattern
+ renderer.writeUint16At(qdcount, header_pos);
+ header_pos += sizeof(uint16_t);
+ renderer.writeUint16At(ancount, header_pos);
+ header_pos += sizeof(uint16_t);
+ renderer.writeUint16At(nscount, header_pos);
+ header_pos += sizeof(uint16_t);
+ renderer.writeUint16At(arcount, header_pos);
+
+ // Add TSIG, if necessary, at the end of the message.
+ // TODO: truncate case consideration
+ if (tsig_ctx != NULL) {
+ tsig_ctx->sign(qid_, renderer.getData(),
+ renderer.getLength())->toWire(renderer);
+
+ // update the ARCOUNT for the TSIG RR. Note that for a sane DNS
+ // message arcount should never overflow to 0.
+ renderer.writeUint16At(++arcount, header_pos);
+ }
+}
+
Message::Message(Mode mode) :
impl_(new MessageImpl(mode))
{}
@@ -260,6 +424,16 @@ Message::setEDNS(ConstEDNSPtr edns) {
impl_->edns_ = edns;
}
+const TSIGRecord*
+Message::getTSIGRecord() const {
+ if (impl_->mode_ != Message::PARSE) {
+ isc_throw(InvalidMessageOperation,
+ "getTSIGRecord performed in non-parse mode");
+ }
+
+ return (impl_->tsig_rr_.get());
+}
+
unsigned int
Message::getRRCount(const Section section) const {
if (section >= MessageImpl::NUM_SECTIONS) {
@@ -361,129 +535,14 @@ Message::addQuestion(const Question& question) {
addQuestion(QuestionPtr(new Question(question)));
}
-namespace {
-template <typename T>
-struct RenderSection {
- RenderSection(MessageRenderer& renderer, const bool partial_ok) :
- counter_(0), renderer_(renderer), partial_ok_(partial_ok),
- truncated_(false)
- {}
- void operator()(const T& entry) {
- // If it's already truncated, ignore the rest of the section.
- if (truncated_) {
- return;
- }
- const size_t pos0 = renderer_.getLength();
- counter_ += entry->toWire(renderer_);
- if (renderer_.isTruncated()) {
- truncated_ = true;
- if (!partial_ok_) {
- // roll back to the end of the previous RRset.
- renderer_.trim(renderer_.getLength() - pos0);
- }
- }
- }
- unsigned int getTotalCount() { return (counter_); }
- unsigned int counter_;
- MessageRenderer& renderer_;
- const bool partial_ok_;
- bool truncated_;
-};
+void
+Message::toWire(AbstractMessageRenderer& renderer) {
+ impl_->toWire(renderer, NULL);
}
void
-Message::toWire(MessageRenderer& renderer) {
- if (impl_->mode_ != Message::RENDER) {
- isc_throw(InvalidMessageOperation,
- "Message rendering attempted in non render mode");
- }
- if (impl_->rcode_ == NULL) {
- isc_throw(InvalidMessageOperation,
- "Message rendering attempted without Rcode set");
- }
- if (impl_->opcode_ == NULL) {
- isc_throw(InvalidMessageOperation,
- "Message rendering attempted without Opcode set");
- }
-
- // reserve room for the header
- renderer.skip(HEADERLEN);
-
- uint16_t qdcount =
- for_each(impl_->questions_.begin(), impl_->questions_.end(),
- RenderSection<QuestionPtr>(renderer, false)).getTotalCount();
-
- // TBD: sort RRsets in each section based on configuration policy.
- uint16_t ancount = 0;
- if (!renderer.isTruncated()) {
- ancount =
- for_each(impl_->rrsets_[SECTION_ANSWER].begin(),
- impl_->rrsets_[SECTION_ANSWER].end(),
- RenderSection<RRsetPtr>(renderer, true)).getTotalCount();
- }
- uint16_t nscount = 0;
- if (!renderer.isTruncated()) {
- nscount =
- for_each(impl_->rrsets_[SECTION_AUTHORITY].begin(),
- impl_->rrsets_[SECTION_AUTHORITY].end(),
- RenderSection<RRsetPtr>(renderer, true)).getTotalCount();
- }
- uint16_t arcount = 0;
- if (renderer.isTruncated()) {
- setHeaderFlag(HEADERFLAG_TC, true);
- } else {
- arcount =
- for_each(impl_->rrsets_[SECTION_ADDITIONAL].begin(),
- impl_->rrsets_[SECTION_ADDITIONAL].end(),
- RenderSection<RRsetPtr>(renderer, false)).getTotalCount();
- }
-
- // Add EDNS OPT RR if necessary. Basically, we add it only when EDNS
- // has been explicitly set. However, if the RCODE would require it and
- // no EDNS has been set we generate a temporary local EDNS and use it.
- if (!renderer.isTruncated()) {
- ConstEDNSPtr local_edns = impl_->edns_;
- if (!local_edns && impl_->rcode_->getExtendedCode() != 0) {
- local_edns = ConstEDNSPtr(new EDNS());
- }
- if (local_edns) {
- arcount += local_edns->toWire(renderer,
- impl_->rcode_->getExtendedCode());
- }
- }
-
- // Adjust the counter buffer.
- // XXX: these may not be equal to the number of corresponding entries
- // in rrsets_[] or questions_ if truncation occurred or an EDNS OPT RR
- // was inserted. This is not good, and we should revisit the entire
- // design.
- impl_->counts_[SECTION_QUESTION] = qdcount;
- impl_->counts_[SECTION_ANSWER] = ancount;
- impl_->counts_[SECTION_AUTHORITY] = nscount;
- impl_->counts_[SECTION_ADDITIONAL] = arcount;
-
- // TBD: TSIG, SIG(0) etc.
-
- // fill in the header
- size_t header_pos = 0;
- renderer.writeUint16At(impl_->qid_, header_pos);
- header_pos += sizeof(uint16_t);
-
- uint16_t codes_and_flags =
- (impl_->opcode_->getCode() << OPCODE_SHIFT) & OPCODE_MASK;
- codes_and_flags |= (impl_->rcode_->getCode() & RCODE_MASK);
- codes_and_flags |= (impl_->flags_ & HEADERFLAG_MASK);
- renderer.writeUint16At(codes_and_flags, header_pos);
- header_pos += sizeof(uint16_t);
- // XXX: should avoid repeated pattern (TODO)
- renderer.writeUint16At(qdcount, header_pos);
- header_pos += sizeof(uint16_t);
- renderer.writeUint16At(ancount, header_pos);
- header_pos += sizeof(uint16_t);
- renderer.writeUint16At(nscount, header_pos);
- header_pos += sizeof(uint16_t);
- renderer.writeUint16At(arcount, header_pos);
- header_pos += sizeof(uint16_t);
+Message::toWire(AbstractMessageRenderer& renderer, TSIGContext& tsig_ctx) {
+ impl_->toWire(renderer, &tsig_ctx);
}
void
@@ -611,6 +670,9 @@ MessageImpl::parseSection(const Message::Section section,
unsigned int added = 0;
for (unsigned int count = 0; count < counts_[section]; ++count) {
+ // We need to remember the start position for TSIG processing
+ const size_t start_position = buffer.getPosition();
+
const Name name(buffer);
// buffer must store at least RR TYPE, RR CLASS, TTL, and RDLEN.
@@ -628,32 +690,12 @@ MessageImpl::parseSection(const Message::Section section,
ConstRdataPtr rdata = createRdata(rrtype, rrclass, buffer, rdlen);
if (rrtype == RRType::OPT()) {
- if (section != Message::SECTION_ADDITIONAL) {
- isc_throw(DNSMessageFORMERR,
- "EDNS OPT RR found in an invalid section");
- }
- if (edns_) {
- isc_throw(DNSMessageFORMERR, "multiple EDNS OPT RR found");
- }
-
- uint8_t extended_rcode;
- edns_ = ConstEDNSPtr(createEDNSFromRR(name, rrclass, rrtype, ttl,
- *rdata, extended_rcode));
- setRcode(Rcode(rcode_->getCode(), extended_rcode));
- continue;
+ addEDNS(section, name, rrclass, rrtype, ttl, *rdata);
+ } else if (rrtype == RRType::TSIG()) {
+ addTSIG(section, count, buffer, start_position, name, rrclass, ttl,
+ *rdata);
} else {
- vector<RRsetPtr>::iterator it =
- find_if(rrsets_[section].begin(), rrsets_[section].end(),
- MatchRR(name, rrtype, rrclass));
- if (it != rrsets_[section].end()) {
- (*it)->setTTL(min((*it)->getTTL(), ttl));
- (*it)->addRdata(rdata);
- } else {
- RRsetPtr rrset =
- RRsetPtr(new RRset(name, rrclass, rrtype, ttl));
- rrset->addRdata(rdata);
- rrsets_[section].push_back(rrset);
- }
+ addRR(section, name, rrclass, rrtype, ttl, rdata);
++added;
}
}
@@ -661,6 +703,65 @@ MessageImpl::parseSection(const Message::Section section,
return (added);
}
+void
+MessageImpl::addRR(Message::Section section, const Name& name,
+ const RRClass& rrclass, const RRType& rrtype,
+ const RRTTL& ttl, ConstRdataPtr rdata)
+{
+ vector<RRsetPtr>::iterator it =
+ find_if(rrsets_[section].begin(), rrsets_[section].end(),
+ MatchRR(name, rrtype, rrclass));
+ if (it != rrsets_[section].end()) {
+ (*it)->setTTL(min((*it)->getTTL(), ttl));
+ (*it)->addRdata(rdata);
+ } else {
+ RRsetPtr rrset(new RRset(name, rrclass, rrtype, ttl));
+ rrset->addRdata(rdata);
+ rrsets_[section].push_back(rrset);
+ }
+}
+
+void
+MessageImpl::addEDNS(Message::Section section, const Name& name,
+ const RRClass& rrclass, const RRType& rrtype,
+ const RRTTL& ttl, const Rdata& rdata)
+{
+ if (section != Message::SECTION_ADDITIONAL) {
+ isc_throw(DNSMessageFORMERR,
+ "EDNS OPT RR found in an invalid section");
+ }
+ if (edns_) {
+ isc_throw(DNSMessageFORMERR, "multiple EDNS OPT RR found");
+ }
+
+ uint8_t extended_rcode;
+ edns_ = ConstEDNSPtr(createEDNSFromRR(name, rrclass, rrtype, ttl, rdata,
+ extended_rcode));
+ setRcode(Rcode(rcode_->getCode(), extended_rcode));
+}
+
+void
+MessageImpl::addTSIG(Message::Section section, unsigned int count,
+ const InputBuffer& buffer, size_t start_position,
+ const Name& name, const RRClass& rrclass,
+ const RRTTL& ttl, const Rdata& rdata)
+{
+ if (section != Message::SECTION_ADDITIONAL) {
+ isc_throw(DNSMessageFORMERR,
+ "TSIG RR found in an invalid section");
+ }
+ if (count != counts_[section] - 1) {
+ isc_throw(DNSMessageFORMERR, "TSIG RR is not the last record");
+ }
+ if (tsig_rr_) {
+ isc_throw(DNSMessageFORMERR, "multiple TSIG RRs found");
+ }
+ tsig_rr_ = ConstTSIGRecordPtr(new TSIGRecord(name, rrclass,
+ ttl, rdata,
+ buffer.getPosition() -
+ start_position));
+}
+
namespace {
template <typename T>
struct SectionFormatter {
@@ -694,31 +795,31 @@ Message::toText() const {
// for simplicity we don't consider extended rcode (unlike BIND9)
s += ", status: " + impl_->rcode_->toText();
s += ", id: " + boost::lexical_cast<string>(impl_->qid_);
- s += "\n;; flags: ";
+ s += "\n;; flags:";
if (getHeaderFlag(HEADERFLAG_QR)) {
- s += "qr ";
+ s += " qr";
}
if (getHeaderFlag(HEADERFLAG_AA)) {
- s += "aa ";
+ s += " aa";
}
if (getHeaderFlag(HEADERFLAG_TC)) {
- s += "tc ";
+ s += " tc";
}
if (getHeaderFlag(HEADERFLAG_RD)) {
- s += "rd ";
+ s += " rd";
}
if (getHeaderFlag(HEADERFLAG_RA)) {
- s += "ra ";
+ s += " ra";
}
if (getHeaderFlag(HEADERFLAG_AD)) {
- s += "ad ";
+ s += " ad";
}
if (getHeaderFlag(HEADERFLAG_CD)) {
- s += "cd ";
+ s += " cd";
}
// for simplicity, don't consider the update case for now
- s += "; QUESTION: " +
+ s += "; QUERY: " + // note: not "QUESTION" to be compatible with BIND 9 dig
lexical_cast<string>(impl_->counts_[SECTION_QUESTION]);
s += ", ANSWER: " +
lexical_cast<string>(impl_->counts_[SECTION_ANSWER]);
@@ -729,6 +830,9 @@ Message::toText() const {
if (impl_->edns_ != NULL) {
++arcount;
}
+ if (impl_->tsig_rr_ != NULL) {
+ ++arcount;
+ }
s += ", ADDITIONAL: " + lexical_cast<string>(arcount) + "\n";
if (impl_->edns_ != NULL) {
@@ -765,6 +869,11 @@ Message::toText() const {
SectionFormatter<RRsetPtr>(SECTION_ADDITIONAL, s));
}
+ if (impl_->tsig_rr_ != NULL) {
+ s += "\n;; TSIG PSEUDOSECTION:\n";
+ s += impl_->tsig_rr_->toText();
+ }
+
return (s);
}
diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h
index 91a241a..fcc53e9 100644
--- a/src/lib/dns/message.h
+++ b/src/lib/dns/message.h
@@ -28,7 +28,13 @@
#include <dns/rrset.h>
namespace isc {
+namespace util {
+class InputBuffer;
+}
+
namespace dns {
+class TSIGContext;
+class TSIGRecord;
///
/// \brief A standard DNS module exception that is thrown if a wire format
@@ -76,8 +82,7 @@ public:
typedef uint16_t qid_t;
-class InputBuffer;
-class MessageRenderer;
+class AbstractMessageRenderer;
class Message;
class MessageImpl;
class Opcode;
@@ -199,8 +204,8 @@ public:
HEADERFLAG_TC = 0x0200, ///< Truncation
HEADERFLAG_RD = 0x0100, ///< Recursion desired
HEADERFLAG_RA = 0x0080, ///< Recursion available
- HEADERFLAG_AD = 0x0020, ///< DNSSEC checking disabled (RFC4035)
- HEADERFLAG_CD = 0x0010 ///< Authentic %data (RFC4035)
+ HEADERFLAG_AD = 0x0020, ///< Authentic %data (RFC4035)
+ HEADERFLAG_CD = 0x0010 ///< DNSSEC checking disabled (RFC4035)
};
/// \brief Constants to specify sections of a DNS message.
@@ -365,6 +370,25 @@ public:
/// \c Message.
void setEDNS(ConstEDNSPtr edns);
+ /// \brief Return, if any, the TSIG record contained in the received
+ /// message.
+ ///
+ /// Currently, this method is only intended to return a TSIG record
+ /// for an incoming message built via the \c fromWire() method in the
+ /// PARSE mode. A call to this method in the RENDER mode is invalid and
+ /// result in an exception. Also, calling this method is meaningless
+ /// unless \c fromWire() is performed.
+ ///
+ /// The returned pointer is valid only during the lifetime of the
+ /// \c Message object and until \c clear() is called. The \c Message
+ /// object retains the ownership of \c TSIGRecord; the caller must not
+ /// try to delete it.
+ ///
+ /// \exception InvalidMessageOperation Message is not in the PARSE mode.
+ ///
+ /// \return A pointer to the stored \c TSIGRecord or \c NULL.
+ const TSIGRecord* getTSIGRecord() const;
+
/// \brief Returns the number of RRs contained in the given section.
///
/// In the \c PARSE mode, the returned value may not be identical to
@@ -520,19 +544,37 @@ public:
/// class \c InvalidMessageOperation will be thrown.
std::string toText() const;
- /// \brief Render the message in wire formant into a \c MessageRenderer
+ /// \brief Render the message in wire formant into a message renderer
/// object.
///
/// 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.
- void toWire(MessageRenderer& renderer);
+ ///
+ /// \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.
+ ///
+ /// \param renderer See the other version
+ /// \param tsig_ctx A TSIG context that is to be used for signing the
+ /// message
+ void toWire(AbstractMessageRenderer& renderer, TSIGContext& tsig_ctx);
/// \brief Parse the header section of the \c Message.
- void parseHeader(InputBuffer& buffer);
+ void parseHeader(isc::util::InputBuffer& buffer);
/// \brief Parse the \c Message.
- void fromWire(InputBuffer& buffer);
+ void fromWire(isc::util::InputBuffer& buffer);
///
/// \name Protocol constants
@@ -559,7 +601,18 @@ private:
/// that ongoing state information will not be lost if the object
/// that originated the asynchronous call falls out of scope.
typedef boost::shared_ptr<Message> MessagePtr;
+typedef boost::shared_ptr<const Message> ConstMessagePtr;
+/// Insert the \c Message as a string into stream.
+///
+/// This method convert \c message into a string and inserts it into the
+/// output stream \c os.
+///
+/// \param os A \c std::ostream object on which the insertion operation is
+/// performed.
+/// \param record A \c Message object output by the operation.
+/// \return A reference to the same \c std::ostream object referenced by
+/// parameter \c os after the insertion operation.
std::ostream& operator<<(std::ostream& os, const Message& message);
}
}
diff --git a/src/lib/dns/messagerenderer.cc b/src/lib/dns/messagerenderer.cc
index 212411a..767aca9 100644
--- a/src/lib/dns/messagerenderer.cc
+++ b/src/lib/dns/messagerenderer.cc
@@ -16,10 +16,12 @@
#include <cassert>
#include <set>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
+using namespace isc::util;
+
namespace isc {
namespace dns {
@@ -150,12 +152,10 @@ struct MessageRenderer::MessageRendererImpl {
///
/// \param buffer An \c OutputBuffer object to which wire format data is
/// written.
- MessageRendererImpl(OutputBuffer& buffer) :
- buffer_(buffer), nbuffer_(Name::MAX_WIRE), msglength_limit_(512),
+ MessageRendererImpl() :
+ nbuffer_(Name::MAX_WIRE), msglength_limit_(512),
truncated_(false), compress_mode_(MessageRenderer::CASE_INSENSITIVE)
{}
- /// The buffer that holds the entire DNS message.
- OutputBuffer& buffer_;
/// A local working buffer to convert each given name into wire format.
/// This could be a local variable of the \c writeName() method, but
/// we keep it in the class so that we can reuse it and avoid construction
@@ -174,7 +174,8 @@ struct MessageRenderer::MessageRendererImpl {
};
MessageRenderer::MessageRenderer(OutputBuffer& buffer) :
- impl_(new MessageRendererImpl(buffer))
+ AbstractMessageRenderer(buffer),
+ impl_(new MessageRendererImpl)
{}
MessageRenderer::~MessageRenderer() {
@@ -182,18 +183,8 @@ MessageRenderer::~MessageRenderer() {
}
void
-MessageRenderer::skip(const size_t len) {
- impl_->buffer_.skip(len);
-}
-
-void
-MessageRenderer::trim(const size_t len) {
- impl_->buffer_.trim(len);
-}
-
-void
MessageRenderer::clear() {
- impl_->buffer_.clear();
+ AbstractMessageRenderer::clear();
impl_->nbuffer_.clear();
impl_->nodeset_.clear();
impl_->msglength_limit_ = 512;
@@ -201,41 +192,6 @@ MessageRenderer::clear() {
impl_->compress_mode_ = CASE_INSENSITIVE;
}
-void
-MessageRenderer::writeUint8(const uint8_t data) {
- impl_->buffer_.writeUint8(data);
-}
-
-void
-MessageRenderer::writeUint16(const uint16_t data) {
- impl_->buffer_.writeUint16(data);
-}
-
-void
-MessageRenderer::writeUint16At(const uint16_t data, const size_t pos) {
- impl_->buffer_.writeUint16At(data, pos);
-}
-
-void
-MessageRenderer::writeUint32(const uint32_t data) {
- impl_->buffer_.writeUint32(data);
-}
-
-void
-MessageRenderer::writeData(const void* const data, const size_t len) {
- impl_->buffer_.writeData(data, len);
-}
-
-const void*
-MessageRenderer::getData() const {
- return (impl_->buffer_.getData());
-}
-
-size_t
-MessageRenderer::getLength() const {
- return (impl_->buffer_.getLength());
-}
-
size_t
MessageRenderer::getLengthLimit() const {
return (impl_->msglength_limit_);
@@ -272,8 +228,9 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
name.toWire(impl_->nbuffer_);
unsigned int i;
- std::set<NameCompressNode>::const_iterator notfound = impl_->nodeset_.end();
- std::set<NameCompressNode>::const_iterator n = notfound;
+ std::set<NameCompressNode, NameCompare>::const_iterator notfound =
+ impl_->nodeset_.end();
+ std::set<NameCompressNode, NameCompare>::const_iterator n = notfound;
// Find the longest ancestor name in the rendered set that matches the
// given name.
@@ -291,15 +248,15 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
}
// Record the current offset before extending the buffer.
- const size_t offset = impl_->buffer_.getLength();
+ const size_t offset = getLength();
// Write uncompress part...
- impl_->buffer_.writeData(impl_->nbuffer_.getData(),
- compress ? i : impl_->nbuffer_.getLength());
+ writeData(impl_->nbuffer_.getData(),
+ compress ? i : impl_->nbuffer_.getLength());
if (compress && n != notfound) {
// ...and compression pointer if available.
uint16_t pointer = (*n).pos_;
pointer |= Name::COMPRESS_POINTER_MARK16;
- impl_->buffer_.writeUint16(pointer);
+ writeUint16(pointer);
}
// Finally, add to the set the newly rendered name and its ancestors that
@@ -311,11 +268,17 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
if (offset + j > Name::MAX_COMPRESS_POINTER) {
break;
}
- impl_->nodeset_.insert(NameCompressNode(*this, impl_->buffer_,
+ impl_->nodeset_.insert(NameCompressNode(*this, getBuffer(),
offset + j,
impl_->nbuffer_.getLength() -
j));
}
}
+
+void
+AbstractMessageRenderer::clear() {
+ buffer_.clear();
+}
+
}
}
diff --git a/src/lib/dns/messagerenderer.h b/src/lib/dns/messagerenderer.h
index 9a7f149..52d9245 100644
--- a/src/lib/dns/messagerenderer.h
+++ b/src/lib/dns/messagerenderer.h
@@ -15,64 +15,64 @@
#ifndef __MESSAGERENDERER_H
#define __MESSAGERENDERER_H 1
+#include <util/buffer.h>
+
namespace isc {
+
namespace dns {
// forward declarations
-class OutputBuffer;
class Name;
+/// \brief The \c AbstractMessageRenderer class is an abstract base class
+/// that provides common interfaces for rendering a DNS message into a buffer
+/// in wire format.
///
-/// \brief The \c MessageRenderer class encapsulates implementation details
-/// of rendering a DNS message into a buffer in wire format.
+/// A specific derived class of \c AbstractMessageRenderer (we call it
+/// a renderer class hereafter) is simply responsible for name compression at
+/// least in the current design. A renderer class object (conceptually)
+/// manages the positions of names rendered in some sort of buffer and uses
+/// that information to render subsequent names with compression.
///
-/// In effect, it's simply responsible for name compression at least in the
-/// current implementation. A \c MessageRenderer class object manages the
-/// positions of names rendered in a buffer and uses that information to render
-/// subsequent names with compression.
-///
-/// This class is mainly intended to be used as a helper for a more
+/// A renderer class is mainly intended to be used as a helper for a more
/// comprehensive \c Message class internally; normal applications won't have
-/// to care about this class.
-///
-/// A \c MessageRenderer class object is constructed with a \c OutputBuffer
-/// object, which is the buffer into which the rendered %data will be written.
-/// Normally the buffer is expected to be empty on construction, but it doesn't
-/// have to be so; the \c MessageRenderer object will start rendering from the
-/// end of the buffer at the time of construction. However, if the
-/// pre-existing portion of the buffer contains DNS names, these names won't
-/// be considered for name compression.
+/// to care about details of this class.
///
-/// Once a \c MessageRenderer object is constructed with a buffer, it is
-/// generally expected that all rendering operations are performed via the
-/// \c MessageRenderer object. If the application modifies the buffer in
-/// parallel with the \c MessageRenderer, the result will be undefined.
+/// Once a renderer class object is constructed with a buffer, it is
+/// generally expected that all rendering operations are performed via that
+/// object. If the application modifies the buffer in
+/// parallel with the renderer, the result will be undefined.
///
/// Note to developers: we introduced a separate class for name compression
/// because previous benchmark with BIND9 showed compression affects overall
/// response performance very much. By having a separate class dedicated for
/// this purpose, we'll be able to change the internal implementation of name
/// compression in the future without affecting other part of the API and
-/// implementation. For the same reason, we adopt the "pimpl" idiom in the
-/// class definition (i.e., using a pointer to a \c MessageRendererImpl class,
-/// which is defined with the class implementation, not in the header file):
-/// we may want to modify the compression implementation without modifying the
-/// header file thereby requesting rebuild the package.
+/// implementation.
///
-/// Furthermore, we may eventually want to allow other developers to develop
-/// and use their own compression implementation. Should such a case become
-/// realistic, we may want to make the \c MessageRendererImpl class an abstract
-/// base class and let concrete derived classes have their own implementations.
-/// At the moment we don't the strong need for it, so we rather avoid over
-/// abstraction and keep the definition simpler.
-class MessageRenderer {
+/// In addition, by introducing a class hierarchy from
+/// \c AbstractMessageRenderer, we allow an application to use a customized
+/// renderer class for specific purposes. For example, a high performance
+/// DNS server may want to use an optimized renderer class assuming some
+/// specific underlying data representation.
+///
+/// \note Some functions (like writeUint8) are not virtual. It is because
+/// it is hard to imagine any version of message renderer that would
+/// do anything else than just putting the data into a buffer, so we
+/// provide a default implementation and having them virtual would only
+/// hurt the performance with no real gain. If it would happen a different
+/// implementation is really needed, we can make them virtual in future.
+/// The only one that is virtual is writeName and it's because this
+/// function is much more complicated, therefore there's a lot of space
+/// for different implementations or behaviours.
+class AbstractMessageRenderer {
public:
/// \brief Compression mode constants.
///
/// The \c CompressMode enum type represents the name compression mode
- /// for the \c MessageRenderer.
+ /// for renderer classes.
/// \c CASE_INSENSITIVE means compress names in case-insensitive manner;
/// \c CASE_SENSITIVE means compress names in case-sensitive manner.
- /// By default, \c MessageRenderer compresses names in case-insensitive
+ /// By default, a renderer compresses names in case-insensitive
/// manner.
/// Compression mode can be dynamically modified by the
/// \c setCompressMode() method.
@@ -80,7 +80,7 @@ public:
/// is not an intended usage. In this case the names already compressed
/// are intact; only names being compressed after the mode change are
/// affected by the change.
- /// If the internal \c MessageRenderer is reinitialized by the \c clear()
+ /// If a renderer class object is reinitialized by the \c clear()
/// method, the compression mode will be reset to the default, which is
/// \c CASE_INSENSITIVE
///
@@ -93,25 +93,39 @@ public:
CASE_INSENSITIVE, //!< Compress names case-insensitive manner (default)
CASE_SENSITIVE //!< Compress names case-sensitive manner
};
-public:
+protected:
///
/// \name Constructors and Destructor
//@{
- /// \brief Constructor from an output buffer.
- ///
- /// \param buffer An \c OutputBuffer object to which wire format data is
- /// written.
- MessageRenderer(OutputBuffer& buffer);
+ /// \brief The default constructor.
+ ///
+ /// This is intentionally defined as \c protected as this base class should
+ /// never be instantiated (except as part of a derived class).
+ /// \param buffer The buffer where the data should be rendered into.
+ /// \todo We might want to revisit this API at some point and remove the
+ /// buffer parameter. In that case it would create it's own buffer and
+ /// a function to extract the data would be available instead. It seems
+ /// like a cleaner design, but it's left undone until we would actually
+ /// benefit from the change.
+ AbstractMessageRenderer(isc::util::OutputBuffer& buffer) :
+ buffer_(buffer)
+ {}
+public:
/// \brief The destructor.
- ///
- /// The destructor does nothing on the given \c buffer on construction;
- /// in fact, it is expected that the user will use the resulting buffer
- /// for some post rendering purposes (e.g., send the data to the network).
- /// It's the user's responsibility to do any necessary cleanup for the
- /// \c buffer.
- ~MessageRenderer();
+ virtual ~AbstractMessageRenderer() {}
//@}
-
+protected:
+ /// \brief Return the output buffer we render into.
+ const isc::util::OutputBuffer& getBuffer() const { return (buffer_); }
+ isc::util::OutputBuffer& getBuffer() { return (buffer_); }
+private:
+ /// \short Buffer to store data
+ ///
+ /// It was decided that there's no need to have this in every subclass,
+ /// at least not now, and this reduces code size and gives compiler a better
+ /// chance to optimise.
+ isc::util::OutputBuffer& buffer_;
+public:
///
/// \name Getter Methods
///
@@ -121,9 +135,15 @@ public:
///
/// This method works exactly same as the same method of the \c OutputBuffer
/// class; all notes for \c OutputBuffer apply.
- const void* getData() const;
+ const void* getData() const {
+ return (buffer_.getData());
+ }
+
/// \brief Return the length of data written in the internal buffer.
- size_t getLength() const;
+ size_t getLength() const {
+ return (buffer_.getLength());
+ }
+
/// \brief Return whether truncation has occurred while rendering.
///
/// Once the return value of this method is \c true, it doesn't make sense
@@ -133,20 +153,22 @@ public:
/// This method never throws an exception.
///
/// \return true if truncation has occurred; otherwise \c false.
- bool isTruncated() const;
+ virtual bool isTruncated() const = 0;
+
/// \brief Return the maximum length of rendered data that can fit in the
/// corresponding DNS message without truncation.
///
/// This method never throws an exception.
///
/// \return The maximum length in bytes.
- size_t getLengthLimit() const;
- /// \brief Return the compression mode of the \c MessageRenderer.
+ virtual size_t getLengthLimit() const = 0;
+
+ /// \brief Return the compression mode of the renderer class object.
///
/// This method never throws an exception.
///
/// \return The current compression mode.
- CompressMode getCompressMode() const;
+ virtual CompressMode getCompressMode() const = 0;
//@}
///
@@ -157,20 +179,22 @@ public:
/// rendering.
///
/// This method never throws an exception.
- void setTruncated();
+ virtual void setTruncated() = 0;
+
/// \brief Set the maximum length of rendered data that can fit in the
/// corresponding DNS message without truncation.
///
/// This method never throws an exception.
///
/// \param len The maximum length in bytes.
- void setLengthLimit(size_t len);
- /// \brief Set the compression mode of the \c MessageRenderer.
+ virtual void setLengthLimit(size_t len) = 0;
+
+ /// \brief Set the compression mode of the renderer class object.
///
/// This method never throws an exception.
///
/// \param mode A \c CompressMode value representing the compression mode.
- void setCompressMode(CompressMode mode);
+ virtual void setCompressMode(CompressMode mode) = 0;
//@}
///
@@ -184,7 +208,10 @@ public:
/// that is to be filled in later, e.g, by \ref writeUint16At().
///
/// \param len The length of the gap to be inserted in bytes.
- void skip(size_t len);
+ void skip(size_t len) {
+ buffer_.skip(len);
+ }
+
/// \brief Trim the specified length of data from the end of the internal
/// buffer.
///
@@ -195,21 +222,31 @@ public:
/// be thrown.
///
/// \param len The length of data that should be trimmed.
- void trim(size_t len);
+ void trim(size_t len) {
+ buffer_.trim(len);
+ }
+
/// \brief Clear the internal buffer and other internal resources.
///
/// This method can be used to re-initialize and reuse the renderer
/// without constructing a new one.
- void clear();
+ virtual void clear();
+
/// \brief Write an unsigned 8-bit integer into the internal buffer.
///
/// \param data The 8-bit integer to be written into the internal buffer.
- void writeUint8(uint8_t data);
+ void writeUint8(const uint8_t data) {
+ buffer_.writeUint8(data);
+ }
+
/// \brief Write an unsigned 16-bit integer in host byte order into the
/// internal buffer in network byte order.
///
/// \param data The 16-bit integer to be written into the buffer.
- void writeUint16(uint16_t data);
+ void writeUint16(uint16_t data) {
+ buffer_.writeUint16(data);
+ }
+
/// \brief Write an unsigned 16-bit integer in host byte order at the
/// specified position of the internal buffer in network byte order.
///
@@ -221,26 +258,29 @@ public:
///
/// \param data The 16-bit integer to be written into the internal buffer.
/// \param pos The beginning position in the buffer to write the data.
- void writeUint16At(uint16_t data, size_t pos);
+ void writeUint16At(uint16_t data, size_t pos) {
+ buffer_.writeUint16At(data, pos);
+ }
+
/// \brief Write an unsigned 32-bit integer in host byte order into the
/// internal buffer in network byte order.
///
/// \param data The 32-bit integer to be written into the buffer.
- void writeUint32(uint32_t data);
+ void writeUint32(uint32_t data) {
+ buffer_.writeUint32(data);
+ }
+
/// \brief Copy an arbitrary length of data into the internal buffer
- /// of the \c MessageRenderer.
+ /// of the renderer object.
///
/// No conversion on the copied data is performed.
///
/// \param data A pointer to the data to be copied into the internal buffer.
/// \param len The length of the data in bytes.
- void writeData(const void *data, size_t len);
- //@}
+ void writeData(const void *data, size_t len) {
+ buffer_.writeData(data, len);
+ }
- ///
- /// \name Rendering Methods
- ///
- //@{
/// \brief Write a \c Name object into the internal buffer in wire format,
/// with or without name compression.
///
@@ -255,8 +295,41 @@ public:
///
/// \param name A \c Name object to be written.
/// \param compress A boolean indicating whether to enable name compression.
- void writeName(const Name& name, bool compress = true);
+ virtual void writeName(const Name& name, bool compress = true) = 0;
//@}
+};
+
+/// The \c MessageRenderer is a concrete derived class of
+/// \c AbstractMessageRenderer as a general purpose implementation of the
+/// renderer interfaces.
+///
+/// A \c MessageRenderer object is constructed with a \c OutputBuffer
+/// object, which is the buffer into which the rendered %data will be written.
+/// Normally the buffer is expected to be empty on construction, but it doesn't
+/// have to be so; the renderer object will start rendering from the
+/// end of the buffer at the time of construction. However, if the
+/// pre-existing portion of the buffer contains DNS names, these names won't
+/// be considered for name compression.
+class MessageRenderer : public AbstractMessageRenderer {
+public:
+ using AbstractMessageRenderer::CASE_INSENSITIVE;
+ using AbstractMessageRenderer::CASE_SENSITIVE;
+
+ /// \brief Constructor from an output buffer.
+ ///
+ /// \param buffer An \c OutputBuffer object to which wire format data is
+ /// written.
+ MessageRenderer(isc::util::OutputBuffer& buffer);
+
+ virtual ~MessageRenderer();
+ virtual bool isTruncated() const;
+ virtual size_t getLengthLimit() const;
+ virtual CompressMode getCompressMode() const;
+ virtual void setTruncated();
+ virtual void setLengthLimit(size_t len);
+ virtual void setCompressMode(CompressMode mode);
+ virtual void clear();
+ virtual void writeName(const Name& name, bool compress = true);
private:
struct MessageRendererImpl;
MessageRendererImpl* impl_;
diff --git a/src/lib/dns/name.cc b/src/lib/dns/name.cc
index 8786bcf..4cd0b2b 100644
--- a/src/lib/dns/name.cc
+++ b/src/lib/dns/name.cc
@@ -20,14 +20,14 @@
#include <iostream>
#include <algorithm>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
using namespace std;
+using namespace isc::util;
using isc::dns::NameComparisonResult;
-using isc::dns::MessageRenderer;
namespace isc {
namespace dns {
@@ -403,7 +403,7 @@ Name::toWire(OutputBuffer& buffer) const {
}
void
-Name::toWire(MessageRenderer& renderer) const {
+Name::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeName(*this);
}
diff --git a/src/lib/dns/name.h b/src/lib/dns/name.h
index dc1b5b3..4ff7fe5 100644
--- a/src/lib/dns/name.h
+++ b/src/lib/dns/name.h
@@ -23,10 +23,13 @@
#include <exceptions/exceptions.h>
namespace isc {
-namespace dns {
+namespace util {
class InputBuffer;
class OutputBuffer;
-class MessageRenderer;
+}
+
+namespace dns {
+class AbstractMessageRenderer;
///
/// \brief A standard DNS module exception that is thrown if the name parser
@@ -247,7 +250,7 @@ public:
///
/// \param buffer A buffer storing the wire format %data.
/// \param downcase Whether to convert upper case alphabets to lower case.
- explicit Name(InputBuffer& buffer, bool downcase = false);
+ explicit Name(isc::util::InputBuffer& buffer, bool downcase = false);
///
/// We use the default copy constructor intentionally.
//@}
@@ -347,7 +350,7 @@ public:
///
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer and name compression information.
- void toWire(MessageRenderer& renderer) const;
+ void toWire(AbstractMessageRenderer& renderer) const;
/// \brief Render the <code>Name</code> in the wire format without
/// compression.
@@ -359,7 +362,7 @@ public:
/// then this method should not throw an exception.
///
/// \param buffer An output buffer to store the wire %data.
- void toWire(OutputBuffer& buffer) const;
+ void toWire(isc::util::OutputBuffer& buffer) const;
//@}
///
diff --git a/src/lib/dns/python/Makefile.am b/src/lib/dns/python/Makefile.am
index 9d171cd..6c4ef54 100644
--- a/src/lib/dns/python/Makefile.am
+++ b/src/lib/dns/python/Makefile.am
@@ -5,26 +5,35 @@ AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
pyexec_LTLIBRARIES = pydnspp.la
-pydnspp_la_SOURCES = pydnspp.cc pydnspp_common.cc
+pydnspp_la_SOURCES = pydnspp.cc pydnspp_common.cc pydnspp_towire.h
+pydnspp_la_SOURCES += name_python.cc name_python.h
+pydnspp_la_SOURCES += messagerenderer_python.cc messagerenderer_python.h
+pydnspp_la_SOURCES += rcode_python.cc rcode_python.h
+pydnspp_la_SOURCES += tsigkey_python.cc tsigkey_python.h
+pydnspp_la_SOURCES += tsigerror_python.cc tsigerror_python.h
+pydnspp_la_SOURCES += tsig_rdata_python.cc tsig_rdata_python.h
+pydnspp_la_SOURCES += tsigrecord_python.cc tsigrecord_python.h
+pydnspp_la_SOURCES += tsig_python.cc tsig_python.h
+
pydnspp_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+# Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be
+# placed after -Wextra defined in AM_CXXFLAGS
+pydnspp_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS)
# directly included from source files, so these don't have their own
# rules
EXTRA_DIST = pydnspp_common.h
EXTRA_DIST += edns_python.cc
-EXTRA_DIST += messagerenderer_python.cc
EXTRA_DIST += message_python.cc
EXTRA_DIST += rrclass_python.cc
-EXTRA_DIST += name_python.cc
EXTRA_DIST += opcode_python.cc
-EXTRA_DIST += rcode_python.cc
EXTRA_DIST += rrset_python.cc
EXTRA_DIST += question_python.cc
EXTRA_DIST += rrttl_python.cc
EXTRA_DIST += rdata_python.cc
EXTRA_DIST += rrtype_python.cc
-EXTRA_DIST += tsigkey_python.cc
+EXTRA_DIST += tsigerror_python_inc.cc
# Python prefers .so, while some OSes (specifically MacOS) use a different
# suffix for dynamic objects. -module is necessary to work this around.
diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc
index e54dba0..83c3bfa 100644
--- a/src/lib/dns/python/edns_python.cc
+++ b/src/lib/dns/python/edns_python.cc
@@ -17,6 +17,7 @@
#include <dns/edns.h>
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
//
@@ -107,7 +108,7 @@ PyMethodDef EDNS_methods[] = {
// Most of the functions are not actually implemented and NULL here.
PyTypeObject edns_type = {
PyVarObject_HEAD_INIT(NULL, 0)
- "libdns_python.EDNS",
+ "pydnspp.EDNS",
sizeof(s_EDNS), // tp_basicsize
0, // tp_itemsize
(destructor)EDNS_destroy, // tp_dealloc
@@ -202,7 +203,7 @@ EDNS_init(s_EDNS* self, PyObject* args) {
// in this context so that we can share the try-catch logic with
// EDNS_createFromRR() (see below).
uint8_t extended_rcode;
- self->edns = createFromRR(*name->name, *rrclass->rrclass,
+ self->edns = createFromRR(*name->cppobj, *rrclass->rrclass,
*rrtype->rrtype, *rrttl->rrttl,
*rdata->rdata, extended_rcode);
return (self->edns != NULL ? 0 : -1);
@@ -297,12 +298,15 @@ EDNS_getUDPSize(const s_EDNS* const self) {
PyObject*
EDNS_setUDPSize(s_EDNS* self, PyObject* args) {
- unsigned int size;
- if (!PyArg_ParseTuple(args, "I", &size)) {
+ long size;
+ if (!PyArg_ParseTuple(args, "l", &size)) {
+ PyErr_Clear();
+ PyErr_SetString(PyExc_TypeError,
+ "No valid type in set_udp_size argument");
return (NULL);
}
- if (size > 65535) {
- PyErr_SetString(PyExc_OverflowError,
+ if (size < 0 || size > 0xffff) {
+ PyErr_SetString(PyExc_ValueError,
"UDP size is not an unsigned 16-bit integer");
return (NULL);
}
@@ -330,7 +334,7 @@ EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) {
return (NULL);
}
- edns_obj->edns = createFromRR(*name->name, *rrclass->rrclass,
+ edns_obj->edns = createFromRR(*name->cppobj, *rrclass->rrclass,
*rrtype->rrtype, *rrttl->rrttl,
*rdata->rdata, extended_rcode);
if (edns_obj->edns != NULL) {
diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc
index e19547e..2842588 100644
--- a/src/lib/dns/python/message_python.cc
+++ b/src/lib/dns/python/message_python.cc
@@ -14,17 +14,22 @@
#include <exceptions/exceptions.h>
#include <dns/message.h>
+#include <dns/rcode.h>
+#include <dns/tsig.h>
+
using namespace isc::dns;
+using namespace isc::util;
+namespace {
//
// Declaration of the custom exceptions
// Initialization and addition of these go in the initModulePart
// function at the end of this file
//
-static PyObject* po_MessageTooShort;
-static PyObject* po_InvalidMessageSection;
-static PyObject* po_InvalidMessageOperation;
-static PyObject* po_InvalidMessageUDPSize;
+PyObject* po_MessageTooShort;
+PyObject* po_InvalidMessageSection;
+PyObject* po_InvalidMessageOperation;
+PyObject* po_InvalidMessageUDPSize;
//
// Definition of the classes
@@ -35,10 +40,6 @@ static PyObject* po_InvalidMessageUDPSize;
// and a type description
//
-// Section
-//
-
-//
// Message
//
@@ -54,36 +55,37 @@ public:
//
// General creation and destruction
-static int Message_init(s_Message* self, PyObject* args);
-static void Message_destroy(s_Message* self);
-
-static PyObject* Message_getHeaderFlag(s_Message* self, PyObject* args);
-static PyObject* Message_setHeaderFlag(s_Message* self, PyObject* args);
-static PyObject* Message_getQid(s_Message* self);
-static PyObject* Message_setQid(s_Message* self, PyObject* args);
-static PyObject* Message_getRcode(s_Message* self);
-static PyObject* Message_setRcode(s_Message* self, PyObject* args);
-static PyObject* Message_getOpcode(s_Message* self);
-static PyObject* Message_setOpcode(s_Message* self, PyObject* args);
-static PyObject* Message_getEDNS(s_Message* self);
-static PyObject* Message_setEDNS(s_Message* self, PyObject* args);
-static PyObject* Message_getRRCount(s_Message* self, PyObject* args);
+int Message_init(s_Message* self, PyObject* args);
+void Message_destroy(s_Message* self);
+
+PyObject* Message_getHeaderFlag(s_Message* self, PyObject* args);
+PyObject* Message_setHeaderFlag(s_Message* self, PyObject* args);
+PyObject* Message_getQid(s_Message* self);
+PyObject* Message_setQid(s_Message* self, PyObject* args);
+PyObject* Message_getRcode(s_Message* self);
+PyObject* Message_setRcode(s_Message* self, PyObject* args);
+PyObject* Message_getOpcode(s_Message* self);
+PyObject* Message_setOpcode(s_Message* self, PyObject* args);
+PyObject* Message_getEDNS(s_Message* self);
+PyObject* Message_setEDNS(s_Message* self, PyObject* args);
+PyObject* Message_getTSIGRecord(s_Message* self);
+PyObject* Message_getRRCount(s_Message* self, PyObject* args);
// use direct iterators for these? (or simply lists for now?)
-static PyObject* Message_getQuestion(s_Message* self);
-static PyObject* Message_getSection(s_Message* self, PyObject* args);
+PyObject* Message_getQuestion(s_Message* self);
+PyObject* Message_getSection(s_Message* self, PyObject* args);
//static PyObject* Message_beginQuestion(s_Message* self, PyObject* args);
//static PyObject* Message_endQuestion(s_Message* self, PyObject* args);
//static PyObject* Message_beginSection(s_Message* self, PyObject* args);
//static PyObject* Message_endSection(s_Message* self, PyObject* args);
-static PyObject* Message_addQuestion(s_Message* self, PyObject* args);
-static PyObject* Message_addRRset(s_Message* self, PyObject* args);
-static PyObject* Message_clear(s_Message* self, PyObject* args);
-static PyObject* Message_makeResponse(s_Message* self);
-static PyObject* Message_toText(s_Message* self);
-static PyObject* Message_str(PyObject* self);
-static PyObject* Message_toWire(s_Message* self, PyObject* args);
-static PyObject* Message_fromWire(s_Message* self, PyObject* args);
+PyObject* Message_addQuestion(s_Message* self, PyObject* args);
+PyObject* Message_addRRset(s_Message* self, PyObject* args);
+PyObject* Message_clear(s_Message* self, PyObject* args);
+PyObject* Message_makeResponse(s_Message* self);
+PyObject* Message_toText(s_Message* self);
+PyObject* Message_str(PyObject* self);
+PyObject* Message_toWire(s_Message* self, PyObject* args);
+PyObject* Message_fromWire(s_Message* self, PyObject* args);
// This list contains the actual set of functions we have in
// python. Each entry has
@@ -91,7 +93,7 @@ static PyObject* Message_fromWire(s_Message* self, PyObject* args);
// 2. Our static function here
// 3. Argument type
// 4. Documentation
-static PyMethodDef Message_methods[] = {
+PyMethodDef Message_methods[] = {
{ "get_header_flag", reinterpret_cast<PyCFunction>(Message_getHeaderFlag),
METH_VARARGS,
"Return whether the specified header flag bit is set in the "
@@ -125,6 +127,11 @@ static PyMethodDef Message_methods[] = {
{ "set_edns", reinterpret_cast<PyCFunction>(Message_setEDNS), METH_VARARGS,
"Set EDNS for the message."
},
+ { "get_tsig_record",
+ reinterpret_cast<PyCFunction>(Message_getTSIGRecord), METH_NOARGS,
+ "Return, if any, the TSIG record contained in the received message. "
+ "If no TSIG RR is set in the message, None will be returned."
+ },
{ "get_rr_count", reinterpret_cast<PyCFunction>(Message_getRRCount), METH_VARARGS,
"Returns the number of RRs contained in the given section." },
{ "get_question", reinterpret_cast<PyCFunction>(Message_getQuestion), METH_NOARGS,
@@ -174,7 +181,7 @@ static PyMethodDef Message_methods[] = {
// This defines the complete type for reflection in python and
// parsing of PyObject* to s_Message
// Most of the functions are not actually implemented and NULL here.
-static PyTypeObject message_type = {
+PyTypeObject message_type = {
PyVarObject_HEAD_INIT(NULL, 0)
"pydnspp.Message",
sizeof(s_Message), // tp_basicsize
@@ -224,11 +231,11 @@ static PyTypeObject message_type = {
0 // tp_version_tag
};
-static int
+int
Message_init(s_Message* self, PyObject* args) {
- unsigned int i;
-
- if (PyArg_ParseTuple(args, "I", &i)) {
+ int i;
+
+ if (PyArg_ParseTuple(args, "i", &i)) {
PyErr_Clear();
if (i == Message::PARSE) {
self->message = new Message(Message::PARSE);
@@ -247,14 +254,14 @@ Message_init(s_Message* self, PyObject* args) {
return (-1);
}
-static void
+void
Message_destroy(s_Message* self) {
delete self->message;
self->message = NULL;
Py_TYPE(self)->tp_free(self);
}
-static PyObject*
+PyObject*
Message_getHeaderFlag(s_Message* self, PyObject* args) {
unsigned int messageflag;
if (!PyArg_ParseTuple(args, "I", &messageflag)) {
@@ -272,19 +279,19 @@ Message_getHeaderFlag(s_Message* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Message_setHeaderFlag(s_Message* self, PyObject* args) {
- int messageflag;
+ long messageflag;
PyObject *on = Py_True;
- if (!PyArg_ParseTuple(args, "i|O!", &messageflag, &PyBool_Type, &on)) {
+ if (!PyArg_ParseTuple(args, "l|O!", &messageflag, &PyBool_Type, &on)) {
PyErr_Clear();
PyErr_SetString(PyExc_TypeError,
"no valid type in set_header_flag argument");
return (NULL);
}
- if (messageflag < 0) {
- PyErr_SetString(PyExc_TypeError, "invalid Message header flag");
+ if (messageflag < 0 || messageflag > 0xffff) {
+ PyErr_SetString(PyExc_ValueError, "Message header flag out of range");
return (NULL);
}
@@ -303,17 +310,26 @@ Message_setHeaderFlag(s_Message* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Message_getQid(s_Message* self) {
return (Py_BuildValue("I", self->message->getQid()));
}
-static PyObject*
+PyObject*
Message_setQid(s_Message* self, PyObject* args) {
- uint16_t id;
- if (!PyArg_ParseTuple(args, "H", &id)) {
+ long id;
+ if (!PyArg_ParseTuple(args, "l", &id)) {
+ PyErr_Clear();
+ PyErr_SetString(PyExc_TypeError,
+ "no valid type in set_qid argument");
return (NULL);
}
+ if (id < 0 || id > 0xffff) {
+ PyErr_SetString(PyExc_ValueError,
+ "Message id out of range");
+ return (NULL);
+ }
+
try {
self->message->setQid(id);
Py_RETURN_NONE;
@@ -323,21 +339,21 @@ Message_setQid(s_Message* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Message_getRcode(s_Message* self) {
s_Rcode* rcode;
rcode = static_cast<s_Rcode*>(rcode_type.tp_alloc(&rcode_type, 0));
if (rcode != NULL) {
- rcode->rcode = NULL;
+ rcode->cppobj = NULL;
try {
- rcode->rcode = new Rcode(self->message->getRcode());
+ rcode->cppobj = new Rcode(self->message->getRcode());
} catch (const InvalidMessageOperation& imo) {
PyErr_SetString(po_InvalidMessageOperation, imo.what());
} catch (...) {
PyErr_SetString(po_IscException, "Unexpected exception");
}
- if (rcode->rcode == NULL) {
+ if (rcode->cppobj == NULL) {
Py_DECREF(rcode);
return (NULL);
}
@@ -346,14 +362,14 @@ Message_getRcode(s_Message* self) {
return (rcode);
}
-static PyObject*
+PyObject*
Message_setRcode(s_Message* self, PyObject* args) {
s_Rcode* rcode;
if (!PyArg_ParseTuple(args, "O!", &rcode_type, &rcode)) {
return (NULL);
}
try {
- self->message->setRcode(*rcode->rcode);
+ self->message->setRcode(*rcode->cppobj);
Py_RETURN_NONE;
} catch (const InvalidMessageOperation& imo) {
PyErr_SetString(po_InvalidMessageOperation, imo.what());
@@ -361,7 +377,7 @@ Message_setRcode(s_Message* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Message_getOpcode(s_Message* self) {
s_Opcode* opcode;
@@ -384,7 +400,7 @@ Message_getOpcode(s_Message* self) {
return (opcode);
}
-static PyObject*
+PyObject*
Message_setOpcode(s_Message* self, PyObject* args) {
s_Opcode* opcode;
if (!PyArg_ParseTuple(args, "O!", &opcode_type, &opcode)) {
@@ -399,7 +415,7 @@ Message_setOpcode(s_Message* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Message_getEDNS(s_Message* self) {
s_EDNS* edns;
EDNS* edns_body;
@@ -419,7 +435,7 @@ Message_getEDNS(s_Message* self) {
return (edns);
}
-static PyObject*
+PyObject*
Message_setEDNS(s_Message* self, PyObject* args) {
s_EDNS* edns;
if (!PyArg_ParseTuple(args, "O!", &edns_type, &edns)) {
@@ -434,7 +450,30 @@ Message_setEDNS(s_Message* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
+Message_getTSIGRecord(s_Message* self) {
+ try {
+ const TSIGRecord* tsig_record = self->message->getTSIGRecord();
+
+ if (tsig_record == NULL) {
+ Py_RETURN_NONE;
+ }
+ return (createTSIGRecordObject(*tsig_record));
+ } catch (const InvalidMessageOperation& ex) {
+ PyErr_SetString(po_InvalidMessageOperation, ex.what());
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpected failure in getting TSIGRecord from message: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "getting TSIGRecord from message");
+ }
+ return (NULL);
+}
+
+PyObject*
Message_getRRCount(s_Message* self, PyObject* args) {
unsigned int section;
if (!PyArg_ParseTuple(args, "I", §ion)) {
@@ -453,7 +492,7 @@ Message_getRRCount(s_Message* self, PyObject* args) {
}
// TODO use direct iterators for these? (or simply lists for now?)
-static PyObject*
+PyObject*
Message_getQuestion(s_Message* self) {
QuestionIterator qi, qi_end;
try {
@@ -492,7 +531,7 @@ Message_getQuestion(s_Message* self) {
return (list);
}
-static PyObject*
+PyObject*
Message_getSection(s_Message* self, PyObject* args) {
unsigned int section;
if (!PyArg_ParseTuple(args, "I", §ion)) {
@@ -549,7 +588,7 @@ Message_getSection(s_Message* self, PyObject* args) {
//static PyObject* Message_beginSection(s_Message* self, PyObject* args);
//static PyObject* Message_endSection(s_Message* self, PyObject* args);
//static PyObject* Message_addQuestion(s_Message* self, PyObject* args);
-static PyObject*
+PyObject*
Message_addQuestion(s_Message* self, PyObject* args) {
s_Question *question;
@@ -562,12 +601,12 @@ Message_addQuestion(s_Message* self, PyObject* args) {
Py_RETURN_NONE;
}
-static PyObject*
+PyObject*
Message_addRRset(s_Message* self, PyObject* args) {
PyObject *sign = Py_False;
- unsigned int section;
+ int section;
s_RRset* rrset;
- if (!PyArg_ParseTuple(args, "IO!|O!", §ion, &rrset_type, &rrset,
+ if (!PyArg_ParseTuple(args, "iO!|O!", §ion, &rrset_type, &rrset,
&PyBool_Type, &sign)) {
return (NULL);
}
@@ -589,10 +628,10 @@ Message_addRRset(s_Message* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Message_clear(s_Message* self, PyObject* args) {
- unsigned int i;
- if (PyArg_ParseTuple(args, "I", &i)) {
+ int i;
+ if (PyArg_ParseTuple(args, "i", &i)) {
PyErr_Clear();
if (i == Message::PARSE) {
self->message->clear(Message::PARSE);
@@ -610,13 +649,13 @@ Message_clear(s_Message* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Message_makeResponse(s_Message* self) {
self->message->makeResponse();
Py_RETURN_NONE;
}
-static PyObject*
+PyObject*
Message_toText(s_Message* self) {
// Py_BuildValue makes python objects from native data
try {
@@ -631,7 +670,7 @@ Message_toText(s_Message* self) {
}
}
-static PyObject*
+PyObject*
Message_str(PyObject* self) {
// Simply call the to_text method we already defined
return (PyObject_CallMethod(self,
@@ -639,13 +678,19 @@ Message_str(PyObject* self) {
const_cast<char*>("")));
}
-static PyObject*
+PyObject*
Message_toWire(s_Message* self, PyObject* args) {
s_MessageRenderer* mr;
+ s_TSIGContext* tsig_ctx = NULL;
- if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
+ if (PyArg_ParseTuple(args, "O!|O!", &messagerenderer_type, &mr,
+ &tsigcontext_type, &tsig_ctx)) {
try {
- self->message->toWire(*mr->messagerenderer);
+ if (tsig_ctx == NULL) {
+ self->message->toWire(*mr->messagerenderer);
+ } else {
+ self->message->toWire(*mr->messagerenderer, *tsig_ctx->cppobj);
+ }
// If we return NULL it is seen as an error, so use this for
// None returns
Py_RETURN_NONE;
@@ -653,6 +698,11 @@ Message_toWire(s_Message* self, PyObject* args) {
PyErr_Clear();
PyErr_SetString(po_InvalidMessageOperation, imo.what());
return (NULL);
+ } catch (const TSIGContextError& ex) {
+ // toWire() with a TSIG context can fail due to this if the
+ // python program has a bug.
+ PyErr_SetString(po_TSIGContextError, ex.what());
+ return (NULL);
}
}
PyErr_Clear();
@@ -661,7 +711,7 @@ Message_toWire(s_Message* self, PyObject* args) {
return (NULL);
}
-static PyObject*
+PyObject*
Message_fromWire(s_Message* self, PyObject* args) {
const char* b;
Py_ssize_t len;
@@ -755,3 +805,4 @@ initModulePart_Message(PyObject* mod) {
return (true);
}
+} // end of unnamed namespace
diff --git a/src/lib/dns/python/messagerenderer_python.cc b/src/lib/dns/python/messagerenderer_python.cc
index a00d8d4..e6f5d3e 100644
--- a/src/lib/dns/python/messagerenderer_python.cc
+++ b/src/lib/dns/python/messagerenderer_python.cc
@@ -12,38 +12,41 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <Python.h>
+
+#include <util/buffer.h>
+
#include <dns/messagerenderer.h>
-// For each class, we need a struct, a helper functions (init, destroy,
-// and static wrappers around the methods we export), a list of methods,
-// and a type description
+#include "pydnspp_common.h"
+#include "messagerenderer_python.h"
+
using namespace isc::dns;
+using namespace isc::dns::python;
+using namespace isc::util;
// MessageRenderer
-// since we don't use *Buffer in the python version (but work with
-// the already existing bytearray type where we use these custom buffers
-// in c++, we need to keep track of one here.
-class s_MessageRenderer : public PyObject {
-public:
- OutputBuffer* outputbuffer;
- MessageRenderer* messagerenderer;
-};
+s_MessageRenderer::s_MessageRenderer() : outputbuffer(NULL),
+ messagerenderer(NULL)
+{
+}
-static int MessageRenderer_init(s_MessageRenderer* self);
-static void MessageRenderer_destroy(s_MessageRenderer* self);
+namespace {
+int MessageRenderer_init(s_MessageRenderer* self);
+void MessageRenderer_destroy(s_MessageRenderer* self);
-static PyObject* MessageRenderer_getData(s_MessageRenderer* self);
-static PyObject* MessageRenderer_getLength(s_MessageRenderer* self);
-static PyObject* MessageRenderer_isTruncated(s_MessageRenderer* self);
-static PyObject* MessageRenderer_getLengthLimit(s_MessageRenderer* self);
-static PyObject* MessageRenderer_getCompressMode(s_MessageRenderer* self);
-static PyObject* MessageRenderer_setTruncated(s_MessageRenderer* self);
-static PyObject* MessageRenderer_setLengthLimit(s_MessageRenderer* self, PyObject* args);
-static PyObject* MessageRenderer_setCompressMode(s_MessageRenderer* self, PyObject* args);
-static PyObject* MessageRenderer_clear(s_MessageRenderer* self);
+PyObject* MessageRenderer_getData(s_MessageRenderer* self);
+PyObject* MessageRenderer_getLength(s_MessageRenderer* self);
+PyObject* MessageRenderer_isTruncated(s_MessageRenderer* self);
+PyObject* MessageRenderer_getLengthLimit(s_MessageRenderer* self);
+PyObject* MessageRenderer_getCompressMode(s_MessageRenderer* self);
+PyObject* MessageRenderer_setTruncated(s_MessageRenderer* self);
+PyObject* MessageRenderer_setLengthLimit(s_MessageRenderer* self, PyObject* args);
+PyObject* MessageRenderer_setCompressMode(s_MessageRenderer* self, PyObject* args);
+PyObject* MessageRenderer_clear(s_MessageRenderer* self);
-static PyMethodDef MessageRenderer_methods[] = {
+PyMethodDef MessageRenderer_methods[] = {
{ "get_data", reinterpret_cast<PyCFunction>(MessageRenderer_getData), METH_NOARGS,
"Returns the data as a bytes() object" },
{ "get_length", reinterpret_cast<PyCFunction>(MessageRenderer_getLength), METH_NOARGS,
@@ -66,69 +69,14 @@ static PyMethodDef MessageRenderer_methods[] = {
{ NULL, NULL, 0, NULL }
};
-static PyTypeObject messagerenderer_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "pydnspp.MessageRenderer",
- sizeof(s_MessageRenderer), // tp_basicsize
- 0, // tp_itemsize
- (destructor)MessageRenderer_destroy,// tp_dealloc
- NULL, // tp_print
- NULL, // tp_getattr
- NULL, // tp_setattr
- NULL, // tp_reserved
- NULL, // tp_repr
- NULL, // tp_as_number
- NULL, // tp_as_sequence
- NULL, // tp_as_mapping
- NULL, // tp_hash
- NULL, // tp_call
- NULL, // tp_str
- NULL, // tp_getattro
- NULL, // tp_setattro
- NULL, // tp_as_buffer
- Py_TPFLAGS_DEFAULT, // tp_flags
- "The MessageRenderer class encapsulates implementation details "
- "of rendering a DNS message into a buffer in wire format. "
- "In effect, it's simply responsible for name compression at least in the "
- "current implementation. A MessageRenderer class object manages the "
- "positions of names rendered in a buffer and uses that information to render "
- "subsequent names with compression.",
- NULL, // tp_traverse
- NULL, // tp_clear
- NULL, // tp_richcompare
- 0, // tp_weaklistoffset
- NULL, // tp_iter
- NULL, // tp_iternext
- MessageRenderer_methods, // tp_methods
- NULL, // tp_members
- NULL, // tp_getset
- NULL, // tp_base
- NULL, // tp_dict
- NULL, // tp_descr_get
- NULL, // tp_descr_set
- 0, // tp_dictoffset
- (initproc)MessageRenderer_init, // tp_init
- NULL, // tp_alloc
- PyType_GenericNew, // tp_new
- NULL, // tp_free
- NULL, // tp_is_gc
- NULL, // tp_bases
- NULL, // tp_mro
- NULL, // tp_cache
- NULL, // tp_subclasses
- NULL, // tp_weaklist
- NULL, // tp_del
- 0 // tp_version_tag
-};
-
-static int
+int
MessageRenderer_init(s_MessageRenderer* self) {
self->outputbuffer = new OutputBuffer(4096);
self->messagerenderer = new MessageRenderer(*self->outputbuffer);
return (0);
}
-static void
+void
MessageRenderer_destroy(s_MessageRenderer* self) {
delete self->messagerenderer;
delete self->outputbuffer;
@@ -137,19 +85,19 @@ MessageRenderer_destroy(s_MessageRenderer* self) {
Py_TYPE(self)->tp_free(self);
}
-static PyObject*
+PyObject*
MessageRenderer_getData(s_MessageRenderer* self) {
return (Py_BuildValue("y#",
self->messagerenderer->getData(),
self->messagerenderer->getLength()));
}
-static PyObject*
+PyObject*
MessageRenderer_getLength(s_MessageRenderer* self) {
return (Py_BuildValue("I", self->messagerenderer->getLength()));
}
-static PyObject*
+PyObject*
MessageRenderer_isTruncated(s_MessageRenderer* self) {
if (self->messagerenderer->isTruncated()) {
Py_RETURN_TRUE;
@@ -158,40 +106,48 @@ MessageRenderer_isTruncated(s_MessageRenderer* self) {
}
}
-static PyObject*
+PyObject*
MessageRenderer_getLengthLimit(s_MessageRenderer* self) {
return (Py_BuildValue("I", self->messagerenderer->getLengthLimit()));
}
-static PyObject*
+PyObject*
MessageRenderer_getCompressMode(s_MessageRenderer* self) {
return (Py_BuildValue("I", self->messagerenderer->getCompressMode()));
}
-static PyObject*
+PyObject*
MessageRenderer_setTruncated(s_MessageRenderer* self) {
self->messagerenderer->setTruncated();
Py_RETURN_NONE;
}
-static PyObject*
+PyObject*
MessageRenderer_setLengthLimit(s_MessageRenderer* self,
PyObject* args)
{
- unsigned int lengthlimit;
- if (!PyArg_ParseTuple(args, "I", &lengthlimit)) {
+ long lengthlimit;
+ if (!PyArg_ParseTuple(args, "l", &lengthlimit)) {
+ PyErr_Clear();
+ PyErr_SetString(PyExc_TypeError,
+ "No valid type in set_length_limit argument");
+ return (NULL);
+ }
+ if (lengthlimit < 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "MessageRenderer length limit out of range");
return (NULL);
}
self->messagerenderer->setLengthLimit(lengthlimit);
Py_RETURN_NONE;
}
-static PyObject*
+PyObject*
MessageRenderer_setCompressMode(s_MessageRenderer* self,
PyObject* args)
{
- unsigned int mode;
- if (!PyArg_ParseTuple(args, "I", &mode)) {
+ int mode;
+ if (!PyArg_ParseTuple(args, "i", &mode)) {
return (NULL);
}
@@ -211,14 +167,71 @@ MessageRenderer_setCompressMode(s_MessageRenderer* self,
}
}
-static PyObject*
+PyObject*
MessageRenderer_clear(s_MessageRenderer* self) {
self->messagerenderer->clear();
Py_RETURN_NONE;
}
+} // end of unnamed namespace
// end of MessageRenderer
-
+namespace isc {
+namespace dns {
+namespace python {
+PyTypeObject messagerenderer_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp.MessageRenderer",
+ sizeof(s_MessageRenderer), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)MessageRenderer_destroy,// tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ NULL, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "The MessageRenderer class encapsulates implementation details "
+ "of rendering a DNS message into a buffer in wire format. "
+ "In effect, it's simply responsible for name compression at least in the "
+ "current implementation. A MessageRenderer class object manages the "
+ "positions of names rendered in a buffer and uses that information to render "
+ "subsequent names with compression.",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ MessageRenderer_methods, // tp_methods
+ NULL, // tp_members
+ NULL, // tp_getset
+ NULL, // tp_base
+ NULL, // tp_dict
+ NULL, // tp_descr_get
+ NULL, // tp_descr_set
+ 0, // tp_dictoffset
+ (initproc)MessageRenderer_init, // tp_init
+ NULL, // tp_alloc
+ PyType_GenericNew, // tp_new
+ NULL, // tp_free
+ NULL, // tp_is_gc
+ NULL, // tp_bases
+ NULL, // tp_mro
+ NULL, // tp_cache
+ NULL, // tp_subclasses
+ NULL, // tp_weaklist
+ NULL, // tp_del
+ 0 // tp_version_tag
+};
// Module Initialization, all statics are initialized here
bool
@@ -251,5 +264,6 @@ initModulePart_MessageRenderer(PyObject* mod) {
return (true);
}
-
-
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/messagerenderer_python.h b/src/lib/dns/python/messagerenderer_python.h
new file mode 100644
index 0000000..3bb096e
--- /dev/null
+++ b/src/lib/dns/python/messagerenderer_python.h
@@ -0,0 +1,52 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_MESSAGERENDERER_H
+#define __PYTHON_MESSAGERENDERER_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace util {
+class OutputBuffer;
+}
+namespace dns {
+class MessageRenderer;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object.
+//
+// since we don't use *Buffer in the python version (but work with
+// the already existing bytearray type where we use these custom buffers
+// in C++, we need to keep track of one here.
+class s_MessageRenderer : public PyObject {
+public:
+ s_MessageRenderer();
+ isc::util::OutputBuffer* outputbuffer;
+ MessageRenderer* messagerenderer;
+};
+
+extern PyTypeObject messagerenderer_type;
+
+bool initModulePart_MessageRenderer(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_MESSAGERENDERER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/name_python.cc b/src/lib/dns/python/name_python.cc
index 3d7a196..d00c6f7 100644
--- a/src/lib/dns/python/name_python.cc
+++ b/src/lib/dns/python/name_python.cc
@@ -12,26 +12,18 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-//
-// Declaration of the custom exceptions
-// Initialization and addition of these go in the module init at the
-// end
-//
-static PyObject* po_EmptyLabel;
-static PyObject* po_TooLongName;
-static PyObject* po_TooLongLabel;
-static PyObject* po_BadLabelType;
-static PyObject* po_BadEscape;
-static PyObject* po_IncompleteName;
-static PyObject* po_InvalidBufferPosition;
-static PyObject* po_DNSMessageFORMERR;
+#include <Python.h>
-//
-// Declaration of enums
-// Initialization and addition of these go in the module init at the
-// end
-//
-static PyObject* po_NameRelation;
+#include <util/buffer.h>
+#include <util/python/pycppwrapper_util.h>
+
+#include <dns/exceptions.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+
+#include "pydnspp_common.h"
+#include "messagerenderer_python.h"
+#include "name_python.h"
//
// Definition of the classes
@@ -41,20 +33,20 @@ static PyObject* po_NameRelation;
// and static wrappers around the methods we export), a list of methods,
// and a type description
using namespace isc::dns;
+using namespace isc::dns::python;
+using namespace isc::util;
+using namespace isc::util::python;
+namespace {
// NameComparisonResult
-class s_NameComparisonResult : public PyObject {
-public:
- isc::dns::NameComparisonResult* ncr;
-};
-static int NameComparisonResult_init(s_NameComparisonResult*, PyObject*);
-static void NameComparisonResult_destroy(s_NameComparisonResult* self);
-static PyObject* NameComparisonResult_getOrder(s_NameComparisonResult* self);
-static PyObject* NameComparisonResult_getCommonLabels(s_NameComparisonResult* self);
-static PyObject* NameComparisonResult_getRelation(s_NameComparisonResult* self);
+int NameComparisonResult_init(s_NameComparisonResult*, PyObject*);
+void NameComparisonResult_destroy(s_NameComparisonResult* self);
+PyObject* NameComparisonResult_getOrder(s_NameComparisonResult* self);
+PyObject* NameComparisonResult_getCommonLabels(s_NameComparisonResult* self);
+PyObject* NameComparisonResult_getRelation(s_NameComparisonResult* self);
-static PyMethodDef NameComparisonResult_methods[] = {
+PyMethodDef NameComparisonResult_methods[] = {
{ "get_order", reinterpret_cast<PyCFunction>(NameComparisonResult_getOrder), METH_NOARGS,
"Returns the order" },
{ "get_common_labels", reinterpret_cast<PyCFunction>(NameComparisonResult_getCommonLabels), METH_NOARGS,
@@ -64,122 +56,61 @@ static PyMethodDef NameComparisonResult_methods[] = {
{ NULL, NULL, 0, NULL }
};
-static PyTypeObject name_comparison_result_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "pydnspp.NameComparisonResult",
- sizeof(s_NameComparisonResult), // tp_basicsize
- 0, // tp_itemsize
- (destructor)NameComparisonResult_destroy, // tp_dealloc
- NULL, // tp_print
- NULL, // tp_getattr
- NULL, // tp_setattr
- NULL, // tp_reserved
- NULL, // tp_repr
- NULL, // tp_as_number
- NULL, // tp_as_sequence
- NULL, // tp_as_mapping
- NULL, // tp_hash
- NULL, // tp_call
- NULL, // tp_str
- NULL, // tp_getattro
- NULL, // tp_setattro
- NULL, // tp_as_buffer
- Py_TPFLAGS_DEFAULT, // tp_flags
- "This is a supplemental class used only as a return value of Name.compare(). "
- "It encapsulate a tuple of the comparison: ordering, number of common labels, "
- "and relationship as follows:\n"
- "- ordering: relative ordering under the DNSSEC order relation\n"
- "- labels: the number of common significant labels of the two names being"
- " compared\n"
- "- relationship: see NameComparisonResult.NameRelation\n",
- NULL, // tp_traverse
- NULL, // tp_clear
- NULL, // tp_richcompare
- 0, // tp_weaklistoffset
- NULL, // tp_iter
- NULL, // tp_iternext
- NameComparisonResult_methods, // tp_methods
- NULL, // tp_members
- NULL, // tp_getset
- NULL, // tp_base
- NULL, // tp_dict
- NULL, // tp_descr_get
- NULL, // tp_descr_set
- 0, // tp_dictoffset
- (initproc)NameComparisonResult_init, // tp_init
- NULL, // tp_alloc
- PyType_GenericNew, // tp_new
- NULL, // tp_free
- NULL, // tp_is_gc
- NULL, // tp_bases
- NULL, // tp_mro
- NULL, // tp_cache
- NULL, // tp_subclasses
- NULL, // tp_weaklist
- NULL, // tp_del
- 0 // tp_version_tag
-};
-
-static int
+int
NameComparisonResult_init(s_NameComparisonResult*, PyObject*) {
PyErr_SetString(PyExc_NotImplementedError,
"NameComparisonResult can't be built directly");
return (-1);
}
-static void
+void
NameComparisonResult_destroy(s_NameComparisonResult* self) {
- delete self->ncr;
- self->ncr = NULL;
+ delete self->cppobj;
+ self->cppobj = NULL;
Py_TYPE(self)->tp_free(self);
}
-static PyObject*
+PyObject*
NameComparisonResult_getOrder(s_NameComparisonResult* self) {
- return (Py_BuildValue("i", self->ncr->getOrder()));
+ return (Py_BuildValue("i", self->cppobj->getOrder()));
}
-static PyObject*
+PyObject*
NameComparisonResult_getCommonLabels(s_NameComparisonResult* self) {
- return (Py_BuildValue("I", self->ncr->getCommonLabels()));
+ return (Py_BuildValue("I", self->cppobj->getCommonLabels()));
}
-static PyObject*
+PyObject*
NameComparisonResult_getRelation(s_NameComparisonResult* self) {
- return (Py_BuildValue("I", self->ncr->getRelation()));
+ return (Py_BuildValue("I", self->cppobj->getRelation()));
}
-
// end of NameComparisonResult
// Name
-
-class s_Name : public PyObject {
-public:
- isc::dns::Name* name;
- size_t position;
-};
-
-static int Name_init(s_Name* self, PyObject* args);
-static void Name_destroy(s_Name* self);
-
-static PyObject* Name_toWire(s_Name* self, PyObject* args);
-static PyObject* Name_toText(s_Name* self);
-static PyObject* Name_str(PyObject* self);
-static PyObject* Name_getLabelCount(s_Name* self);
-static PyObject* Name_at(s_Name* self, PyObject* args);
-static PyObject* Name_getLength(s_Name* self);
-
-static PyObject* Name_compare(s_Name* self, PyObject* args);
-static PyObject* Name_equals(s_Name* self, PyObject* args);
-
-static PyObject* Name_richcmp(s_Name* self, s_Name* other, int op);
-static PyObject* Name_split(s_Name* self, PyObject* args);
-static PyObject* Name_reverse(s_Name* self);
-static PyObject* Name_concatenate(s_Name* self, PyObject* args);
-static PyObject* Name_downcase(s_Name* self);
-static PyObject* Name_isWildCard(s_Name* self);
-
-static PyMethodDef Name_methods[] = {
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_Name, Name> NameContainer;
+
+int Name_init(s_Name* self, PyObject* args);
+void Name_destroy(s_Name* self);
+
+PyObject* Name_toWire(s_Name* self, PyObject* args);
+PyObject* Name_toText(s_Name* self);
+PyObject* Name_str(PyObject* self);
+PyObject* Name_getLabelCount(s_Name* self);
+PyObject* Name_at(s_Name* self, PyObject* args);
+PyObject* Name_getLength(s_Name* self);
+
+PyObject* Name_compare(s_Name* self, PyObject* args);
+PyObject* Name_equals(s_Name* self, PyObject* args);
+
+PyObject* Name_richcmp(s_Name* self, s_Name* other, int op);
+PyObject* Name_split(s_Name* self, PyObject* args);
+PyObject* Name_reverse(s_Name* self);
+PyObject* Name_concatenate(s_Name* self, PyObject* args);
+PyObject* Name_downcase(s_Name* self);
+PyObject* Name_isWildCard(s_Name* self);
+
+PyMethodDef Name_methods[] = {
{ "at", reinterpret_cast<PyCFunction>(Name_at), METH_VARARGS,
"Returns the integer value of the name data at the specified position" },
{ "get_length", reinterpret_cast<PyCFunction>(Name_getLength), METH_NOARGS,
@@ -216,63 +147,7 @@ static PyMethodDef Name_methods[] = {
{ NULL, NULL, 0, NULL }
};
-static PyTypeObject name_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "pydnspp.Name",
- sizeof(s_Name), // tp_basicsize
- 0, // tp_itemsize
- (destructor)Name_destroy, // tp_dealloc
- NULL, // tp_print
- NULL, // tp_getattr
- NULL, // tp_setattr
- NULL, // tp_reserved
- NULL, // tp_repr
- NULL, // tp_as_number
- NULL, // tp_as_sequence
- NULL, // tp_as_mapping
- NULL, // tp_hash
- NULL, // tp_call
- Name_str, // tp_str
- NULL, // tp_getattro
- NULL, // tp_setattro
- NULL, // tp_as_buffer
- Py_TPFLAGS_DEFAULT, // tp_flags
- "The Name class encapsulates DNS names.\n"
- "It provides interfaces to construct a name from string or wire-format data, "
- "transform a name into a string or wire-format data, compare two names, get "
- "access to various properties of a name, etc.",
- NULL, // tp_traverse
- NULL, // tp_clear
- (richcmpfunc)Name_richcmp, // tp_richcompare
- 0, // tp_weaklistoffset
- NULL, // tp_iter
- NULL, // tp_iternext
- Name_methods, // tp_methods
- NULL, // tp_members
- NULL, // tp_getset
- NULL, // tp_base
- NULL, // tp_dict
- NULL, // tp_descr_get
- NULL, // tp_descr_set
- 0, // tp_dictoffset
- (initproc)Name_init, // tp_init
- NULL, // tp_alloc
- PyType_GenericNew, // tp_new
- NULL, // tp_free
- NULL, // tp_is_gc
- NULL, // tp_bases
- NULL, // tp_mro
- NULL, // tp_cache
- NULL, // tp_subclasses
- NULL, // tp_weaklist
- // Note: not sure if the following are correct. Added them just to
- // make the compiler happy.
- NULL, // tp_del
- 0 // tp_version_tag
-};
-
-
-static int
+int
Name_init(s_Name* self, PyObject* args) {
const char* s;
PyObject* downcase = Py_False;
@@ -285,7 +160,7 @@ Name_init(s_Name* self, PyObject* args) {
try {
const std::string n(s);
- self->name = new Name(n, downcase == Py_True);
+ self->cppobj = new Name(n, downcase == Py_True);
self->position = 0;
} catch (const EmptyLabel&) {
PyErr_SetString(po_EmptyLabel, "EmptyLabel");
@@ -321,18 +196,24 @@ Name_init(s_Name* self, PyObject* args) {
PyObject* bytes_obj;
const char* bytes;
Py_ssize_t len;
- unsigned int position = 0;
+ long position = 0;
// It was not a string (see comment above), so try bytes, and
// create with buffer object
- if (PyArg_ParseTuple(args, "O|IO!", &bytes_obj, &position,
+ if (PyArg_ParseTuple(args, "O|lO!", &bytes_obj, &position,
&PyBool_Type, &downcase) &&
PyObject_AsCharBuffer(bytes_obj, &bytes, &len) != -1) {
try {
+ if (position < 0) {
+ // Throw IndexError here since name index should be unsigned
+ PyErr_SetString(PyExc_IndexError,
+ "Name index shouldn't be negative");
+ return (-1);
+ }
InputBuffer buffer(bytes, len);
buffer.setPosition(position);
- self->name = new Name(buffer, downcase == Py_True);
+ self->cppobj = new Name(buffer, downcase == Py_True);
self->position = buffer.getPosition();
} catch (const InvalidBufferPosition&) {
PyErr_SetString(po_InvalidBufferPosition,
@@ -354,21 +235,28 @@ Name_init(s_Name* self, PyObject* args) {
return (-1);
}
-static void
+void
Name_destroy(s_Name* self) {
- delete self->name;
- self->name = NULL;
+ delete self->cppobj;
+ self->cppobj = NULL;
Py_TYPE(self)->tp_free(self);
}
-static PyObject*
+PyObject*
Name_at(s_Name* self, PyObject* args) {
- unsigned int pos;
- if (!PyArg_ParseTuple(args, "I", &pos)) {
+ int pos;
+ if (!PyArg_ParseTuple(args, "i", &pos)) {
return (NULL);
}
+ if (pos < 0) {
+ // Throw IndexError here since name index should be unsigned
+ PyErr_SetString(PyExc_IndexError,
+ "name index shouldn't be negative");
+ return (NULL);
+ }
+
try {
- return (Py_BuildValue("I", self->name->at(pos)));
+ return (Py_BuildValue("I", self->cppobj->at(pos)));
} catch (const isc::OutOfRange&) {
PyErr_SetString(PyExc_IndexError,
"name index out of range");
@@ -376,22 +264,22 @@ Name_at(s_Name* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Name_getLength(s_Name* self) {
- return (Py_BuildValue("i", self->name->getLength()));
+ return (Py_BuildValue("i", self->cppobj->getLength()));
}
-static PyObject*
+PyObject*
Name_getLabelCount(s_Name* self) {
- return (Py_BuildValue("i", self->name->getLabelCount()));
+ return (Py_BuildValue("i", self->cppobj->getLabelCount()));
}
-static PyObject*
+PyObject*
Name_toText(s_Name* self) {
- return (Py_BuildValue("s", self->name->toText().c_str()));
+ return (Py_BuildValue("s", self->cppobj->toText().c_str()));
}
-static PyObject*
+PyObject*
Name_str(PyObject* self) {
// Simply call the to_text method we already defined
// str() is not defined in the c++ version, only to_text
@@ -401,16 +289,16 @@ Name_str(PyObject* self) {
const_cast<char*>("")));
}
-static PyObject*
+PyObject*
Name_toWire(s_Name* self, PyObject* args) {
PyObject* bytes;
s_MessageRenderer* mr;
-
+
if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
PyObject* bytes_o = bytes;
-
+
OutputBuffer buffer(Name::MAX_WIRE);
- self->name->toWire(buffer);
+ self->cppobj->toWire(buffer);
PyObject* name_bytes = PyBytes_FromStringAndSize(static_cast<const char*>(buffer.getData()), buffer.getLength());
PyObject* result = PySequence_InPlaceConcat(bytes_o, name_bytes);
// We need to release the object we temporarily created here
@@ -418,7 +306,7 @@ Name_toWire(s_Name* self, PyObject* args) {
Py_DECREF(name_bytes);
return (result);
} else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
- self->name->toWire(*mr->messagerenderer);
+ self->cppobj->toWire(*mr->messagerenderer);
// If we return NULL it is seen as an error, so use this for
// None returns
Py_RETURN_NONE;
@@ -429,7 +317,7 @@ Name_toWire(s_Name* self, PyObject* args) {
return (NULL);
}
-static PyObject*
+PyObject*
Name_compare(s_Name* self, PyObject* args) {
s_Name* other;
@@ -438,71 +326,87 @@ Name_compare(s_Name* self, PyObject* args) {
s_NameComparisonResult* ret = PyObject_New(s_NameComparisonResult, &name_comparison_result_type);
if (ret != NULL) {
- ret->ncr = new NameComparisonResult(
- self->name->compare(*other->name));
+ ret->cppobj = new NameComparisonResult(
+ self->cppobj->compare(*other->cppobj));
}
return (ret);
}
-static PyObject*
+PyObject*
Name_equals(s_Name* self, PyObject* args) {
s_Name* other;
if (!PyArg_ParseTuple(args, "O!", &name_type, &other))
return (NULL);
- if (self->name->equals(*other->name))
+ if (self->cppobj->equals(*other->cppobj))
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
}
-static PyObject*
+PyObject*
Name_split(s_Name* self, PyObject* args) {
- unsigned int first, n;
+ int first, n;
s_Name* ret = NULL;
-
- if (PyArg_ParseTuple(args, "II", &first, &n)) {
+
+ if (PyArg_ParseTuple(args, "ii", &first, &n)) {
+ if (first < 0 || n < 0) {
+ // Throw IndexError here since name index should be unsigned
+ PyErr_SetString(PyExc_IndexError,
+ "name index shouldn't be negative");
+ return (NULL);
+ }
ret = PyObject_New(s_Name, &name_type);
if (ret != NULL) {
- ret->name = NULL;
+ ret->cppobj = NULL;
try {
- ret->name = new Name(self->name->split(first, n));
+ ret->cppobj = new Name(self->cppobj->split(first, n));
} catch(const isc::OutOfRange& oor) {
PyErr_SetString(PyExc_IndexError, oor.what());
- ret->name = NULL;
+ ret->cppobj = NULL;
}
- if (ret->name == NULL) {
+ if (ret->cppobj == NULL) {
Py_DECREF(ret);
return (NULL);
}
}
- } else if (PyArg_ParseTuple(args, "I", &n)) {
+ } else if (PyArg_ParseTuple(args, "i", &n)) {
+ PyErr_Clear();
+ if (n < 0) {
+ // Throw IndexError here since name index should be unsigned
+ PyErr_SetString(PyExc_IndexError,
+ "name index shouldn't be negative");
+ return (NULL);
+ }
ret = PyObject_New(s_Name, &name_type);
if (ret != NULL) {
- ret->name = NULL;
+ ret->cppobj = NULL;
try {
- ret->name = new Name(self->name->split(n));
+ ret->cppobj = new Name(self->cppobj->split(n));
} catch(const isc::OutOfRange& oor) {
PyErr_SetString(PyExc_IndexError, oor.what());
- ret->name = NULL;
+ ret->cppobj = NULL;
}
- if (ret->name == NULL) {
+ if (ret->cppobj == NULL) {
Py_DECREF(ret);
return (NULL);
}
}
}
+
+ PyErr_Clear();
+ PyErr_SetString(PyExc_TypeError,
+ "No valid type in split argument");
return (ret);
}
-#include <iostream>
//
// richcmp defines the ==, !=, >, <, >= and <= operators in python
// It is translated to a function that gets 3 arguments, an object,
// an object to compare to, and an operator.
//
-static PyObject*
+PyObject*
Name_richcmp(s_Name* self, s_Name* other, int op) {
bool c;
@@ -514,22 +418,22 @@ Name_richcmp(s_Name* self, s_Name* other, int op) {
switch (op) {
case Py_LT:
- c = *self->name < *other->name;
+ c = *self->cppobj < *other->cppobj;
break;
case Py_LE:
- c = *self->name <= *other->name;
+ c = *self->cppobj <= *other->cppobj;
break;
case Py_EQ:
- c = *self->name == *other->name;
+ c = *self->cppobj == *other->cppobj;
break;
case Py_NE:
- c = *self->name != *other->name;
+ c = *self->cppobj != *other->cppobj;
break;
case Py_GT:
- c = *self->name > *other->name;
+ c = *self->cppobj > *other->cppobj;
break;
case Py_GE:
- c = *self->name >= *other->name;
+ c = *self->cppobj >= *other->cppobj;
break;
default:
PyErr_SetString(PyExc_IndexError,
@@ -543,13 +447,13 @@ Name_richcmp(s_Name* self, s_Name* other, int op) {
}
}
-static PyObject*
+PyObject*
Name_reverse(s_Name* self) {
s_Name* ret = PyObject_New(s_Name, &name_type);
if (ret != NULL) {
- ret->name = new Name(self->name->reverse());
- if (ret->name == NULL) {
+ ret->cppobj = new Name(self->cppobj->reverse());
+ if (ret->cppobj == NULL) {
Py_DECREF(ret);
return (NULL);
}
@@ -557,7 +461,7 @@ Name_reverse(s_Name* self) {
return (ret);
}
-static PyObject*
+PyObject*
Name_concatenate(s_Name* self, PyObject* args) {
s_Name* other;
@@ -567,7 +471,7 @@ Name_concatenate(s_Name* self, PyObject* args) {
s_Name* ret = PyObject_New(s_Name, &name_type);
if (ret != NULL) {
try {
- ret->name = new Name(self->name->concatenate(*other->name));
+ ret->cppobj = new Name(self->cppobj->concatenate(*other->cppobj));
} catch (const TooLongName& tln) {
PyErr_SetString(po_TooLongName, tln.what());
return (NULL);
@@ -576,23 +480,159 @@ Name_concatenate(s_Name* self, PyObject* args) {
return (ret);
}
-static PyObject*
+PyObject*
Name_downcase(s_Name* self) {
- self->name->downcase();
+ self->cppobj->downcase();
Py_INCREF(self);
return (self);
}
-static PyObject*
+PyObject*
Name_isWildCard(s_Name* self) {
- if (self->name->isWildcard()) {
+ if (self->cppobj->isWildcard()) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
}
// end of Name
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+//
+// Definition of the custom exceptions
+// Initialization and addition of these go in the module init at the
+// end
+//
+PyObject* po_EmptyLabel;
+PyObject* po_TooLongName;
+PyObject* po_TooLongLabel;
+PyObject* po_BadLabelType;
+PyObject* po_BadEscape;
+PyObject* po_IncompleteName;
+PyObject* po_InvalidBufferPosition;
+PyObject* po_DNSMessageFORMERR;
+
+//
+// Definition of enums
+// Initialization and addition of these go in the module init at the
+// end
+//
+PyObject* po_NameRelation;
+
+PyTypeObject name_comparison_result_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp.NameComparisonResult",
+ sizeof(s_NameComparisonResult), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)NameComparisonResult_destroy, // tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ NULL, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "This is a supplemental class used only as a return value of Name.compare(). "
+ "It encapsulate a tuple of the comparison: ordering, number of common labels, "
+ "and relationship as follows:\n"
+ "- ordering: relative ordering under the DNSSEC order relation\n"
+ "- labels: the number of common significant labels of the two names being"
+ " compared\n"
+ "- relationship: see NameComparisonResult.NameRelation\n",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ NameComparisonResult_methods, // tp_methods
+ NULL, // tp_members
+ NULL, // tp_getset
+ NULL, // tp_base
+ NULL, // tp_dict
+ NULL, // tp_descr_get
+ NULL, // tp_descr_set
+ 0, // tp_dictoffset
+ (initproc)NameComparisonResult_init, // tp_init
+ NULL, // tp_alloc
+ PyType_GenericNew, // tp_new
+ NULL, // tp_free
+ NULL, // tp_is_gc
+ NULL, // tp_bases
+ NULL, // tp_mro
+ NULL, // tp_cache
+ NULL, // tp_subclasses
+ NULL, // tp_weaklist
+ NULL, // tp_del
+ 0 // tp_version_tag
+};
+
+PyTypeObject name_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp.Name",
+ sizeof(s_Name), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)Name_destroy, // tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ Name_str, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "The Name class encapsulates DNS names.\n"
+ "It provides interfaces to construct a name from string or wire-format data, "
+ "transform a name into a string or wire-format data, compare two names, get "
+ "access to various properties of a name, etc.",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ (richcmpfunc)Name_richcmp, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ Name_methods, // tp_methods
+ NULL, // tp_members
+ NULL, // tp_getset
+ NULL, // tp_base
+ NULL, // tp_dict
+ NULL, // tp_descr_get
+ NULL, // tp_descr_set
+ 0, // tp_dictoffset
+ (initproc)Name_init, // tp_init
+ NULL, // tp_alloc
+ PyType_GenericNew, // tp_new
+ NULL, // tp_free
+ NULL, // tp_is_gc
+ NULL, // tp_bases
+ NULL, // tp_mro
+ NULL, // tp_cache
+ NULL, // tp_subclasses
+ NULL, // tp_weaklist
+ // Note: not sure if the following are correct. Added them just to
+ // make the compiler happy.
+ NULL, // tp_del
+ 0 // tp_version_tag
+};
// Module Initialization, all statics are initialized here
bool
@@ -638,7 +678,7 @@ initModulePart_Name(PyObject* mod) {
addClassVariable(name_type, "COMPRESS_POINTER_MARK16", Py_BuildValue("I", Name::COMPRESS_POINTER_MARK16));
s_Name* root_name = PyObject_New(s_Name, &name_type);
- root_name->name = new Name(Name::ROOT_NAME());
+ root_name->cppobj = new Name(Name::ROOT_NAME());
PyObject* po_ROOT_NAME = root_name;
addClassVariable(name_type, "ROOT_NAME", po_ROOT_NAME);
@@ -675,3 +715,13 @@ initModulePart_Name(PyObject* mod) {
return (true);
}
+
+PyObject*
+createNameObject(const Name& source) {
+ NameContainer container = PyObject_New(s_Name, &name_type);
+ container.set(new Name(source));
+ return (container.release());
+}
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/name_python.h b/src/lib/dns/python/name_python.h
new file mode 100644
index 0000000..f8e793d
--- /dev/null
+++ b/src/lib/dns/python/name_python.h
@@ -0,0 +1,84 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_NAME_H
+#define __PYTHON_NAME_H 1
+
+#include <Python.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+namespace isc {
+namespace dns {
+class NameComparisonResult;
+class Name;
+
+namespace python {
+
+//
+// Declaration of the custom exceptions
+// Initialization and addition of these go in the module init at the
+// end
+//
+extern PyObject* po_EmptyLabel;
+extern PyObject* po_TooLongName;
+extern PyObject* po_TooLongLabel;
+extern PyObject* po_BadLabelType;
+extern PyObject* po_BadEscape;
+extern PyObject* po_IncompleteName;
+extern PyObject* po_InvalidBufferPosition;
+extern PyObject* po_DNSMessageFORMERR;
+
+//
+// Declaration of enums
+// Initialization and addition of these go in the module init at the
+// end
+//
+extern PyObject* po_NameRelation;
+
+// The s_* Class simply covers one instantiation of the object.
+class s_NameComparisonResult : public PyObject {
+public:
+ s_NameComparisonResult() : cppobj(NULL) {}
+ NameComparisonResult* cppobj;
+};
+
+class s_Name : public PyObject {
+public:
+ s_Name() : cppobj(NULL), position(0) {}
+ Name* cppobj;
+ size_t position;
+};
+
+extern PyTypeObject name_comparison_result_type;
+extern PyTypeObject name_type;
+
+bool initModulePart_Name(PyObject* mod);
+
+/// This is A simple shortcut to create a python Name object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called with in a try block
+/// followed by necessary setup for python exception.
+PyObject* createNameObject(const Name& source);
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_NAME_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc
index 7b41598..07abf71 100644
--- a/src/lib/dns/python/pydnspp.cc
+++ b/src/lib/dns/python/pydnspp.cc
@@ -31,23 +31,38 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
+
#include <dns/exceptions.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
-#include <dns/python/pydnspp_common.h>
-
+#include "pydnspp_common.h"
+#include "messagerenderer_python.h"
+#include "name_python.h"
+#include "rcode_python.h"
+#include "tsigkey_python.h"
+#include "tsig_rdata_python.h"
+#include "tsigerror_python.h"
+#include "tsigrecord_python.h"
+#include "tsig_python.h"
+
+namespace isc {
+namespace dns {
+namespace python {
// For our 'general' isc::Exceptions
-static PyObject* po_IscException;
-static PyObject* po_InvalidParameter;
+PyObject* po_IscException;
+PyObject* po_InvalidParameter;
// For our own isc::dns::Exception
-static PyObject* po_DNSMessageBADVERS;
+PyObject* po_DNSMessageBADVERS;
+}
+}
+}
// order is important here!
-#include <dns/python/messagerenderer_python.cc>
-#include <dns/python/name_python.cc> // needs Messagerenderer
+using namespace isc::dns::python;
+
#include <dns/python/rrclass_python.cc> // needs Messagerenderer
#include <dns/python/rrtype_python.cc> // needs Messagerenderer
#include <dns/python/rrttl_python.cc> // needs Messagerenderer
@@ -55,16 +70,15 @@ static PyObject* po_DNSMessageBADVERS;
#include <dns/python/rrset_python.cc> // needs Rdata, RRTTL
#include <dns/python/question_python.cc> // needs RRClass, RRType, RRTTL,
// Name
-#include <dns/python/tsigkey_python.cc> // needs Name
#include <dns/python/opcode_python.cc>
-#include <dns/python/rcode_python.cc>
#include <dns/python/edns_python.cc> // needs Messagerenderer, Rcode
#include <dns/python/message_python.cc> // needs RRset, Question
//
// Definition of the module
//
-static PyModuleDef pydnspp = {
+namespace {
+PyModuleDef pydnspp = {
{ PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
"pydnspp",
"Python bindings for the classes in the isc::dns namespace.\n\n"
@@ -79,10 +93,11 @@ static PyModuleDef pydnspp = {
NULL,
NULL
};
+}
PyMODINIT_FUNC
PyInit_pydnspp(void) {
- PyObject *mod = PyModule_Create(&pydnspp);
+ PyObject* mod = PyModule_Create(&pydnspp);
if (mod == NULL) {
return (NULL);
}
@@ -153,6 +168,21 @@ PyInit_pydnspp(void) {
return (NULL);
}
+ if (!initModulePart_TSIG(mod)) {
+ return (NULL);
+ }
+
+ if (!initModulePart_TSIGError(mod)) {
+ return (NULL);
+ }
+
+ if (!initModulePart_TSIGRecord(mod)) {
+ return (NULL);
+ }
+
+ if (!initModulePart_TSIGContext(mod)) {
+ return (NULL);
+ }
+
return (mod);
}
-
diff --git a/src/lib/dns/python/pydnspp_common.cc b/src/lib/dns/python/pydnspp_common.cc
index 6c26367..8ca763a 100644
--- a/src/lib/dns/python/pydnspp_common.cc
+++ b/src/lib/dns/python/pydnspp_common.cc
@@ -15,6 +15,9 @@
#include <Python.h>
#include <pydnspp_common.h>
+namespace isc {
+namespace dns {
+namespace python {
int
readDataFromSequence(uint8_t *data, size_t len, PyObject* sequence) {
PyObject* el = NULL;
@@ -44,8 +47,15 @@ readDataFromSequence(uint8_t *data, size_t len, PyObject* sequence) {
}
-void addClassVariable(PyTypeObject& c, const char* name,
- PyObject* obj)
-{
- PyDict_SetItemString(c.tp_dict, name, obj);
+int
+addClassVariable(PyTypeObject& c, const char* name, PyObject* obj) {
+ if (obj == NULL) {
+ PyErr_SetString(PyExc_ValueError,
+ "NULL object is specified for a class variable");
+ return (-1);
+ }
+ return (PyDict_SetItemString(c.tp_dict, name, obj));
+}
+}
+}
}
diff --git a/src/lib/dns/python/pydnspp_common.h b/src/lib/dns/python/pydnspp_common.h
index 32e2b78..ed90998 100644
--- a/src/lib/dns/python/pydnspp_common.h
+++ b/src/lib/dns/python/pydnspp_common.h
@@ -15,9 +15,22 @@
#ifndef __LIBDNS_PYTHON_COMMON_H
#define __LIBDNS_PYTHON_COMMON_H 1
-//
-// Shared functions for python/c API
-//
+#include <Python.h>
+
+#include <stdexcept>
+#include <string>
+
+#include <util/python/pycppwrapper_util.h>
+
+namespace isc {
+namespace dns {
+namespace python {
+// For our 'general' isc::Exceptions
+extern PyObject* po_IscException;
+extern PyObject* po_InvalidParameter;
+
+// For our own isc::dns::Exception
+extern PyObject* po_DNSMessageBADVERS;
// This function reads 'bytes' from a sequence
// This sequence can be anything that implements the Sequence interface,
@@ -31,6 +44,12 @@
// case nothing is removed
int readDataFromSequence(uint8_t *data, size_t len, PyObject* sequence);
-void addClassVariable(PyTypeObject& c, const char* name, PyObject* obj);
-
+int addClassVariable(PyTypeObject& c, const char* name, PyObject* obj);
+} // namespace python
+} // namespace dns
+} // namespace isc
#endif // __LIBDNS_PYTHON_COMMON_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/pydnspp_towire.h b/src/lib/dns/python/pydnspp_towire.h
new file mode 100644
index 0000000..66362a0
--- /dev/null
+++ b/src/lib/dns/python/pydnspp_towire.h
@@ -0,0 +1,127 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LIBDNS_PYTHON_TOWIRE_H
+#define __LIBDNS_PYTHON_TOWIRE_H 1
+
+#include <Python.h>
+
+#include <stdexcept>
+#include <string>
+
+#include <dns/messagerenderer.h>
+
+#include <util/buffer.h>
+#include <util/python/pycppwrapper_util.h>
+
+#include "messagerenderer_python.h"
+
+namespace isc {
+namespace dns {
+namespace python {
+
+// The following two templated structures are a helper to use the same
+// toWire() template implementation for two types of toWire() methods:
+// return an integer or have no return value.
+template <typename CPPCLASS>
+struct ToWireCallVoid {
+ ToWireCallVoid(CPPCLASS& cppobj) : cppobj_(cppobj) {}
+ int operator()(AbstractMessageRenderer& renderer) const {
+ cppobj_.toWire(renderer);
+ return (0);
+ }
+ const CPPCLASS& cppobj_;
+};
+
+template <typename CPPCLASS>
+struct ToWireCallInt {
+ ToWireCallInt(CPPCLASS& cppobj) : cppobj_(cppobj) {}
+ int operator()(AbstractMessageRenderer& renderer) const {
+ return (cppobj_.toWire(renderer));
+ }
+ const CPPCLASS& cppobj_;
+};
+
+// This templated function gives a common implementation of the toWire()
+// wrapper for various libdns++ classes. PYSTRUCT and CPPCLASS are
+// (C++ binding of) python and (pure) C++ classes (e.g., s_Name and Name),
+// and TOWIRECALLER is either ToWireCallVoid<CPPCLASS> or
+// ToWireCallInt<CPPCLASS>, depending on the toWire() method of the class
+// returns a value or not.
+//
+// See, e.g., tsigrecord_python.cc for how to use it.
+//
+// This should be able to be used without modification for most classes that
+// have toWire(). But if the underlying toWire() has an extra argument, the
+// definition will need to be adjusted accordingly.
+template <typename PYSTRUCT, typename CPPCLASS, typename TOWIRECALLER>
+PyObject*
+toWireWrapper(const PYSTRUCT* const self, PyObject* args) {
+ try {
+ // To OutputBuffer version
+ PyObject* bytes; // this won't have own reference, no risk of leak.
+ if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
+ // render the object into a buffer (this can throw)
+ isc::util::OutputBuffer buffer(0);
+ self->cppobj->toWire(buffer);
+
+ // convert the rendered data into PyObject. This could leak later,
+ // so we need to store it in a container.
+ PyObject* rd_bytes = PyBytes_FromStringAndSize(
+ static_cast<const char*>(buffer.getData()),
+ buffer.getLength());
+ isc::util::python::PyObjectContainer rd_bytes_container(rd_bytes);
+
+ // concat the latest data to the given existing sequence. concat
+ // operation could fail, so we use a container to clean it up
+ // safely should that happen.
+ PyObject* result = PySequence_InPlaceConcat(bytes, rd_bytes);
+ isc::util::python::PyObjectContainer result_container(result);
+
+ return (result_container.release());
+ }
+
+ // To MessageRenderer version
+ s_MessageRenderer* renderer;
+ if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &renderer)) {
+ const unsigned int n = TOWIRECALLER(*self->cppobj)(
+ *renderer->messagerenderer);
+
+ return (Py_BuildValue("I", n));
+ }
+ } catch (const std::exception& ex) {
+ const std::string ex_what =
+ "Failed to render an libdns++ object wire-format: "
+ + std::string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (NULL);
+ } catch (...) {
+ PyErr_SetString(po_IscException, "Unexpectedly failed to render an "
+ "libdns++ object wire-format.");
+ return (NULL);
+ }
+
+ PyErr_Clear();
+ PyErr_SetString(PyExc_TypeError,
+ "Incorrect arguments for a to_wire() method");
+ return (NULL);
+}
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __LIBDNS_PYTHON_TOWIRE_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/question_python.cc b/src/lib/dns/python/question_python.cc
index a039284..c702f85 100644
--- a/src/lib/dns/python/question_python.cc
+++ b/src/lib/dns/python/question_python.cc
@@ -144,7 +144,7 @@ Question_init(s_Question* self, PyObject* args) {
&rrclass_type, &rrclass,
&rrtype_type, &rrtype
)) {
- self->question = QuestionPtr(new Question(*name->name, *rrclass->rrclass,
+ self->question = QuestionPtr(new Question(*name->cppobj, *rrclass->rrclass,
*rrtype->rrtype));
return (0);
} else if (PyArg_ParseTuple(args, "y#|I", &b, &len, &position)) {
@@ -169,7 +169,7 @@ Question_init(s_Question* self, PyObject* args) {
}
self->question = QuestionPtr();
-
+
PyErr_Clear();
PyErr_SetString(PyExc_TypeError,
"no valid type in constructor argument");
@@ -189,7 +189,7 @@ Question_getName(s_Question* self) {
// is this the best way to do this?
name = static_cast<s_Name*>(name_type.tp_alloc(&name_type, 0));
if (name != NULL) {
- name->name = new Name(self->question->getName());
+ name->cppobj = new Name(self->question->getName());
}
return (name);
diff --git a/src/lib/dns/python/rcode_python.cc b/src/lib/dns/python/rcode_python.cc
index fce8eef..b594ad3 100644
--- a/src/lib/dns/python/rcode_python.cc
+++ b/src/lib/dns/python/rcode_python.cc
@@ -12,9 +12,17 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <Python.h>
+
+#include <exceptions/exceptions.h>
+
#include <dns/rcode.h>
+#include "pydnspp_common.h"
+#include "rcode_python.h"
+
using namespace isc::dns;
+using namespace isc::dns::python;
//
// Declaration of the custom exceptions (None for this class)
@@ -27,25 +35,14 @@ using namespace isc::dns;
// and static wrappers around the methods we export), a list of methods,
// and a type description
-namespace {
//
// Rcode
//
-// We added a helper variable static_code here
-// Since we can create Rcodes dynamically with Rcode(int), but also
-// use the static globals (Rcode::NOERROR() etc), we use this
-// variable to see if the code came from one of the latter, in which
-// case Rcode_destroy should not free it (the other option is to
-// allocate new Rcodes for every use of the static ones, but this
-// seems more efficient).
-class s_Rcode : public PyObject {
-public:
- s_Rcode() : rcode(NULL), static_code(false) {}
- const Rcode* rcode;
- bool static_code;
-};
+// Trivial constructor.
+s_Rcode::s_Rcode() : cppobj(NULL), static_code(false) {}
+namespace {
int Rcode_init(s_Rcode* const self, PyObject* args);
void Rcode_destroy(s_Rcode* const self);
@@ -118,71 +115,20 @@ PyMethodDef Rcode_methods[] = {
{ NULL, NULL, 0, NULL }
};
-PyTypeObject rcode_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "pydnspp.Rcode",
- sizeof(s_Rcode), // tp_basicsize
- 0, // tp_itemsize
- (destructor)Rcode_destroy, // tp_dealloc
- NULL, // tp_print
- NULL, // tp_getattr
- NULL, // tp_setattr
- NULL, // tp_reserved
- NULL, // tp_repr
- NULL, // tp_as_number
- NULL, // tp_as_sequence
- NULL, // tp_as_mapping
- NULL, // tp_hash
- NULL, // tp_call
- Rcode_str, // tp_str
- NULL, // tp_getattro
- NULL, // tp_setattro
- NULL, // tp_as_buffer
- Py_TPFLAGS_DEFAULT, // tp_flags
- "The Rcode class objects represent standard RCODEs"
- "of the header section of DNS messages.",
- NULL, // tp_traverse
- NULL, // tp_clear
- (richcmpfunc)Rcode_richcmp, // tp_richcompare
- 0, // tp_weaklistoffset
- NULL, // tp_iter
- NULL, // tp_iternext
- Rcode_methods, // tp_methods
- NULL, // tp_members
- NULL, // tp_getset
- NULL, // tp_base
- NULL, // tp_dict
- NULL, // tp_descr_get
- NULL, // tp_descr_set
- 0, // tp_dictoffset
- (initproc)Rcode_init, // tp_init
- NULL, // tp_alloc
- PyType_GenericNew, // tp_new
- NULL, // tp_free
- NULL, // tp_is_gc
- NULL, // tp_bases
- NULL, // tp_mro
- NULL, // tp_cache
- NULL, // tp_subclasses
- NULL, // tp_weaklist
- NULL, // tp_del
- 0 // tp_version_tag
-};
-
int
Rcode_init(s_Rcode* const self, PyObject* args) {
- int code = 0;
+ long code = 0;
int ext_code = 0;
- if (PyArg_ParseTuple(args, "i", &code)) {
+ if (PyArg_ParseTuple(args, "l", &code)) {
if (code < 0 || code > 0xffff) {
- PyErr_SetString(PyExc_OverflowError, "Rcode out of range");
+ PyErr_SetString(PyExc_ValueError, "Rcode out of range");
return (-1);
}
ext_code = -1;
- } else if (PyArg_ParseTuple(args, "ii", &code, &ext_code)) {
+ } else if (PyArg_ParseTuple(args, "li", &code, &ext_code)) {
if (code < 0 || code > 0xff || ext_code < 0 || ext_code > 0xff) {
- PyErr_SetString(PyExc_OverflowError, "Rcode out of range");
+ PyErr_SetString(PyExc_ValueError, "Rcode out of range");
return (-1);
}
} else {
@@ -193,9 +139,9 @@ Rcode_init(s_Rcode* const self, PyObject* args) {
}
try {
if (ext_code == -1) {
- self->rcode = new Rcode(code);
+ self->cppobj = new Rcode(code);
} else {
- self->rcode = new Rcode(code, ext_code);
+ self->cppobj = new Rcode(code, ext_code);
}
self->static_code = false;
} catch (const isc::OutOfRange& ex) {
@@ -211,27 +157,27 @@ Rcode_init(s_Rcode* const self, PyObject* args) {
void
Rcode_destroy(s_Rcode* const self) {
// Depending on whether we created the rcode or are referring
- // to a global one, we do or do not delete self->rcode here
+ // to a global one, we do or do not delete self->cppobj here
if (!self->static_code) {
- delete self->rcode;
+ delete self->cppobj;
}
- self->rcode = NULL;
+ self->cppobj = NULL;
Py_TYPE(self)->tp_free(self);
}
PyObject*
Rcode_getCode(const s_Rcode* const self) {
- return (Py_BuildValue("I", self->rcode->getCode()));
+ return (Py_BuildValue("I", self->cppobj->getCode()));
}
PyObject*
Rcode_getExtendedCode(const s_Rcode* const self) {
- return (Py_BuildValue("I", self->rcode->getExtendedCode()));
+ return (Py_BuildValue("I", self->cppobj->getExtendedCode()));
}
PyObject*
Rcode_toText(const s_Rcode* const self) {
- return (Py_BuildValue("s", self->rcode->toText().c_str()));
+ return (Py_BuildValue("s", self->cppobj->toText().c_str()));
}
PyObject*
@@ -245,7 +191,7 @@ PyObject*
Rcode_createStatic(const Rcode& rcode) {
s_Rcode* ret = PyObject_New(s_Rcode, &rcode_type);
if (ret != NULL) {
- ret->rcode = &rcode;
+ ret->cppobj = &rcode;
ret->static_code = true;
}
return (ret);
@@ -357,10 +303,10 @@ Rcode_richcmp(const s_Rcode* const self, const s_Rcode* const other,
PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode");
return (NULL);
case Py_EQ:
- c = (*self->rcode == *other->rcode);
+ c = (*self->cppobj == *other->cppobj);
break;
case Py_NE:
- c = (*self->rcode != *other->rcode);
+ c = (*self->cppobj != *other->cppobj);
break;
case Py_GT:
PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode");
@@ -374,6 +320,61 @@ Rcode_richcmp(const s_Rcode* const self, const s_Rcode* const other,
else
Py_RETURN_FALSE;
}
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+PyTypeObject rcode_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp.Rcode",
+ sizeof(s_Rcode), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)Rcode_destroy, // tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ Rcode_str, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "The Rcode class objects represent standard RCODEs"
+ "of the header section of DNS messages.",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ reinterpret_cast<richcmpfunc>(Rcode_richcmp), // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ Rcode_methods, // tp_methods
+ NULL, // tp_members
+ NULL, // tp_getset
+ NULL, // tp_base
+ NULL, // tp_dict
+ NULL, // tp_descr_get
+ NULL, // tp_descr_set
+ 0, // tp_dictoffset
+ (initproc)Rcode_init, // tp_init
+ NULL, // tp_alloc
+ PyType_GenericNew, // tp_new
+ NULL, // tp_free
+ NULL, // tp_is_gc
+ NULL, // tp_bases
+ NULL, // tp_mro
+ NULL, // tp_cache
+ NULL, // tp_subclasses
+ NULL, // tp_weaklist
+ NULL, // tp_del
+ 0 // tp_version_tag
+};
// Module Initialization, all statics are initialized here
bool
@@ -428,4 +429,6 @@ initModulePart_Rcode(PyObject* mod) {
return (true);
}
-} // end of unnamed namespace
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/rcode_python.h b/src/lib/dns/python/rcode_python.h
new file mode 100644
index 0000000..9b5e699
--- /dev/null
+++ b/src/lib/dns/python/rcode_python.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_RCODE_H
+#define __PYTHON_RCODE_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class Rcode;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object.
+//
+// We added a helper variable static_code here
+// Since we can create Rcodes dynamically with Rcode(int), but also
+// use the static globals (Rcode::NOERROR() etc), we use this
+// variable to see if the code came from one of the latter, in which
+// case Rcode_destroy should not free it (the other option is to
+// allocate new Rcodes for every use of the static ones, but this
+// seems more efficient).
+//
+// Follow-up note: we don't have to use the proxy function in the python lib;
+// we can just define class specific constants directly (see TSIGError).
+// We should make this cleanup later.
+class s_Rcode : public PyObject {
+public:
+ s_Rcode();
+ const Rcode* cppobj;
+ bool static_code;
+};
+
+extern PyTypeObject rcode_type;
+
+bool initModulePart_Rcode(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_RCODE_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/rdata_python.cc b/src/lib/dns/python/rdata_python.cc
index 8579b7e..faa4f4c 100644
--- a/src/lib/dns/python/rdata_python.cc
+++ b/src/lib/dns/python/rdata_python.cc
@@ -14,6 +14,7 @@
#include <dns/rdata.h>
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
//
diff --git a/src/lib/dns/python/rrclass_python.cc b/src/lib/dns/python/rrclass_python.cc
index 3cfed4c..6d150c2 100644
--- a/src/lib/dns/python/rrclass_python.cc
+++ b/src/lib/dns/python/rrclass_python.cc
@@ -14,6 +14,7 @@
#include <dns/rrclass.h>
using namespace isc::dns;
+using namespace isc::util;
//
// Declaration of the custom exceptions
@@ -152,7 +153,7 @@ static PyTypeObject rrclass_type = {
static int
RRClass_init(s_RRClass* self, PyObject* args) {
const char* s;
- unsigned int i;
+ long i;
PyObject* bytes = NULL;
// The constructor argument can be a string ("IN"), an integer (1),
// or a sequence of numbers between 0 and 65535 (wire code)
@@ -165,10 +166,11 @@ RRClass_init(s_RRClass* self, PyObject* args) {
if (PyArg_ParseTuple(args, "s", &s)) {
self->rrclass = new RRClass(s);
return (0);
- } else if (PyArg_ParseTuple(args, "I", &i)) {
- PyErr_Clear();
- if (i > 65535) {
- PyErr_SetString(po_InvalidRRClass, "RR class number too high");
+ } else if (PyArg_ParseTuple(args, "l", &i)) {
+ if (i < 0 || i > 0xffff) {
+ PyErr_Clear();
+ PyErr_SetString(PyExc_ValueError,
+ "RR class number out of range");
return (-1);
}
self->rrclass = new RRClass(i);
diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc
index 2292784..71a0710 100644
--- a/src/lib/dns/python/rrset_python.cc
+++ b/src/lib/dns/python/rrset_python.cc
@@ -29,6 +29,7 @@ static PyObject* po_EmptyRRset;
// and static wrappers around the methods we export), a list of methods,
// and a type description
using namespace isc::dns;
+using namespace isc::util;
// RRset
@@ -167,7 +168,7 @@ RRset_init(s_RRset* self, PyObject* args) {
&rrtype_type, &rrtype,
&rrttl_type, &rrttl
)) {
- self->rrset = RRsetPtr(new RRset(*name->name, *rrclass->rrclass,
+ self->rrset = RRsetPtr(new RRset(*name->cppobj, *rrclass->rrclass,
*rrtype->rrtype, *rrttl->rrttl));
return (0);
}
@@ -196,8 +197,8 @@ RRset_getName(s_RRset* self) {
// is this the best way to do this?
name = static_cast<s_Name*>(name_type.tp_alloc(&name_type, 0));
if (name != NULL) {
- name->name = new Name(self->rrset->getName());
- if (name->name == NULL)
+ name->cppobj = new Name(self->rrset->getName());
+ if (name->cppobj == NULL)
{
Py_DECREF(name);
return (NULL);
@@ -264,7 +265,7 @@ RRset_setName(s_RRset* self, PyObject* args) {
if (!PyArg_ParseTuple(args, "O!", &name_type, &name)) {
return (NULL);
}
- self->rrset->setName(*name->name);
+ self->rrset->setName(*name->cppobj);
Py_RETURN_NONE;
}
diff --git a/src/lib/dns/python/rrttl_python.cc b/src/lib/dns/python/rrttl_python.cc
index e7391d0..c4b25bf 100644
--- a/src/lib/dns/python/rrttl_python.cc
+++ b/src/lib/dns/python/rrttl_python.cc
@@ -18,6 +18,7 @@
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
//
// Declaration of the custom exceptions
@@ -145,7 +146,7 @@ static PyTypeObject rrttl_type = {
static int
RRTTL_init(s_RRTTL* self, PyObject* args) {
const char* s;
- unsigned long i;
+ long long i;
PyObject* bytes = NULL;
// The constructor argument can be a string ("1234"), an integer (1),
// or a sequence of numbers between 0 and 255 (wire code)
@@ -158,8 +159,12 @@ RRTTL_init(s_RRTTL* self, PyObject* args) {
if (PyArg_ParseTuple(args, "s", &s)) {
self->rrttl = new RRTTL(s);
return (0);
- } else if (PyArg_ParseTuple(args, "I", &i)) {
+ } else if (PyArg_ParseTuple(args, "L", &i)) {
PyErr_Clear();
+ if (i < 0 || i > 0xffffffff) {
+ PyErr_SetString(PyExc_ValueError, "RR TTL number out of range");
+ return (-1);
+ }
self->rrttl = new RRTTL(i);
return (0);
} else if (PyArg_ParseTuple(args, "O", &bytes) &&
diff --git a/src/lib/dns/python/rrtype_python.cc b/src/lib/dns/python/rrtype_python.cc
index 012f251..00e0acd 100644
--- a/src/lib/dns/python/rrtype_python.cc
+++ b/src/lib/dns/python/rrtype_python.cc
@@ -18,6 +18,7 @@
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
//
// Declaration of the custom exceptions
@@ -182,7 +183,7 @@ static PyTypeObject rrtype_type = {
static int
RRType_init(s_RRType* self, PyObject* args) {
const char* s;
- unsigned int i;
+ long i;
PyObject* bytes = NULL;
// The constructor argument can be a string ("A"), an integer (1),
// or a sequence of numbers between 0 and 65535 (wire code)
@@ -195,10 +196,10 @@ RRType_init(s_RRType* self, PyObject* args) {
if (PyArg_ParseTuple(args, "s", &s)) {
self->rrtype = new RRType(s);
return (0);
- } else if (PyArg_ParseTuple(args, "I", &i)) {
+ } else if (PyArg_ParseTuple(args, "l", &i)) {
PyErr_Clear();
- if (i > 65535) {
- PyErr_SetString(po_InvalidRRType, "RR Type number too high");
+ if (i < 0 || i > 0xffff) {
+ PyErr_SetString(PyExc_ValueError, "RR Type number out of range");
return (-1);
}
self->rrtype = new RRType(i);
@@ -259,10 +260,10 @@ static PyObject*
RRType_toWire(s_RRType* self, PyObject* args) {
PyObject* bytes;
s_MessageRenderer* mr;
-
+
if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
PyObject* bytes_o = bytes;
-
+
OutputBuffer buffer(2);
self->rrtype->toWire(buffer);
PyObject* n = PyBytes_FromStringAndSize(static_cast<const char*>(buffer.getData()), buffer.getLength());
diff --git a/src/lib/dns/python/tests/Makefile.am b/src/lib/dns/python/tests/Makefile.am
index 7f3c213..61d7df6 100644
--- a/src/lib/dns/python/tests/Makefile.am
+++ b/src/lib/dns/python/tests/Makefile.am
@@ -11,7 +11,11 @@ PYTESTS += rrclass_python_test.py
PYTESTS += rrset_python_test.py
PYTESTS += rrttl_python_test.py
PYTESTS += rrtype_python_test.py
+PYTESTS += tsig_python_test.py
+PYTESTS += tsig_rdata_python_test.py
+PYTESTS += tsigerror_python_test.py
PYTESTS += tsigkey_python_test.py
+PYTESTS += tsigrecord_python_test.py
EXTRA_DIST = $(PYTESTS)
EXTRA_DIST += testutil.py
@@ -20,7 +24,7 @@ EXTRA_DIST += testutil.py
# required by loadable python modules.
LIBRARY_PATH_PLACEHOLDER =
if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
endif
# test using command-line arguments, so use check-local target instead of TESTS
@@ -32,8 +36,13 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
- env PYTHONPATH=$(abs_top_srcdir)/src/lib/dns/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
+ env PYTHONPATH=$(abs_top_builddir)/src/lib/util/pyunittests/.libs:$(abs_top_srcdir)/src/lib/dns/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
TESTDATA_PATH=$(abs_top_srcdir)/src/lib/dns/tests/testdata:$(abs_top_builddir)/src/lib/dns/tests/testdata \
$(LIBRARY_PATH_PLACEHOLDER) \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/dns/python/tests/edns_python_test.py b/src/lib/dns/python/tests/edns_python_test.py
index 3f03c90..b249213 100644
--- a/src/lib/dns/python/tests/edns_python_test.py
+++ b/src/lib/dns/python/tests/edns_python_test.py
@@ -62,17 +62,16 @@ class EDNSTest(unittest.TestCase):
edns = EDNS()
edns.set_udp_size(511)
self.assertEqual(511, edns.get_udp_size())
- edns.set_udp_size(0)
- self.assertEqual(0, edns.get_udp_size())
- edns.set_udp_size(65535)
- self.assertEqual(65535, edns.get_udp_size())
-
self.assertRaises(TypeError, edns.set_udp_size, "wrong")
# Range check. We need to do this at the binding level, so we need
# explicit tests for it.
- self.assertRaises(OverflowError, edns.set_udp_size, 65536)
- self.assertRaises(OverflowError, edns.set_udp_size, -1)
+ edns.set_udp_size(0)
+ self.assertEqual(0, edns.get_udp_size())
+ edns.set_udp_size(65535)
+ self.assertEqual(65535, edns.get_udp_size())
+ self.assertRaises(ValueError, edns.set_udp_size, 0x10000)
+ self.assertRaises(ValueError, edns.set_udp_size, -1)
def test_get_version(self):
self.assertEqual(EDNS.SUPPORTED_VERSION, EDNS().get_version())
diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py
index 6ef42d4..41b9a67 100644
--- a/src/lib/dns/python/tests/message_python_test.py
+++ b/src/lib/dns/python/tests/message_python_test.py
@@ -62,6 +62,12 @@ def create_message():
message_render.add_rrset(Message.SECTION_ANSWER, rrset)
return message_render
+def strip_mutable_tsig_data(data):
+ # Unfortunately we cannot easily compare TSIG RR because we can't tweak
+ # current time. As a work around this helper function strips off the time
+ # dependent part of TSIG RDATA, i.e., the MAC (assuming HMAC-MD5) and
+ # Time Signed.
+ return data[0:-32] + data[-26:-22] + data[-6:]
class MessageTest(unittest.TestCase):
@@ -80,8 +86,12 @@ class MessageTest(unittest.TestCase):
"2001:db8::134"))
self.bogus_section = Message.SECTION_ADDITIONAL + 1
+ self.bogus_below_section = Message.SECTION_QUESTION - 1
+ self.tsig_key = TSIGKey("www.example.com:SFuWd/q99SzF8Yzd1QbB9g==")
+ self.tsig_ctx = TSIGContext(self.tsig_key)
def test_init(self):
+ self.assertRaises(TypeError, Message, -1)
self.assertRaises(TypeError, Message, 3)
self.assertRaises(TypeError, Message, "wrong")
@@ -109,22 +119,28 @@ class MessageTest(unittest.TestCase):
self.assertRaises(InvalidParameter, self.r.set_header_flag, 0)
self.assertRaises(InvalidParameter, self.r.set_header_flag, 0x7000)
self.assertRaises(InvalidParameter, self.r.set_header_flag, 0x0800)
- self.assertRaises(InvalidParameter, self.r.set_header_flag, 0x10000)
- self.assertRaises(TypeError, self.r.set_header_flag, 0x80000000)
- # this would cause overflow and result in a "valid" flag
- self.assertRaises(TypeError, self.r.set_header_flag,
- Message.HEADERFLAG_AA | 0x100000000)
- self.assertRaises(TypeError, self.r.set_header_flag, -1)
-
self.assertRaises(InvalidMessageOperation,
self.p.set_header_flag, Message.HEADERFLAG_AA)
+ # Range check. We need to do this at the binding level, so we need
+ # explicit tests for it.
+ self.assertRaises(ValueError, self.r.set_header_flag, 0x10000)
+ self.assertRaises(ValueError, self.r.set_header_flag, -1)
+
def test_set_qid(self):
self.assertRaises(TypeError, self.r.set_qid, "wrong")
self.assertRaises(InvalidMessageOperation,
self.p.set_qid, 123)
self.r.set_qid(1234)
self.assertEqual(1234, self.r.get_qid())
+ # Range check. We need to do this at the binding level, so we need
+ # explicit tests for it.
+ self.r.set_qid(0)
+ self.assertEqual(0, self.r.get_qid())
+ self.r.set_qid(0xffff)
+ self.assertEqual(0xffff, self.r.get_qid())
+ self.assertRaises(ValueError, self.r.set_qid, -1)
+ self.assertRaises(ValueError, self.r.set_qid, 0x10000)
def test_set_rcode(self):
self.assertRaises(TypeError, self.r.set_rcode, "wrong")
@@ -135,7 +151,7 @@ class MessageTest(unittest.TestCase):
self.assertRaises(InvalidMessageOperation,
self.p.set_rcode, rcode)
-
+
self.assertRaises(InvalidMessageOperation, self.p.get_rcode)
def test_set_opcode(self):
@@ -197,7 +213,7 @@ class MessageTest(unittest.TestCase):
self.assertRaises(InvalidMessageOperation, self.p.add_rrset,
Message.SECTION_ANSWER, self.rrset_a)
-
+
self.assertFalse(compare_rrset_list(section_rrset, self.r.get_section(Message.SECTION_ANSWER)))
self.assertEqual(0, self.r.get_rr_count(Message.SECTION_ANSWER))
self.r.add_rrset(Message.SECTION_ANSWER, self.rrset_a)
@@ -239,6 +255,8 @@ class MessageTest(unittest.TestCase):
Message.SECTION_ANSWER, self.rrset_a)
self.assertRaises(OverflowError, self.r.add_rrset,
self.bogus_section, self.rrset_a)
+ self.assertRaises(OverflowError, self.r.add_rrset,
+ self.bogus_below_section, self.rrset_a)
def test_clear(self):
self.assertEqual(None, self.r.clear(Message.PARSE))
@@ -267,12 +285,39 @@ class MessageTest(unittest.TestCase):
self.assertRaises(InvalidMessageOperation, self.r.to_wire,
MessageRenderer())
+ def __common_tsigquery_setup(self):
+ self.r.set_opcode(Opcode.QUERY())
+ self.r.set_rcode(Rcode.NOERROR())
+ self.r.set_header_flag(Message.HEADERFLAG_RD)
+ self.r.add_question(Question(Name("www.example.com"),
+ RRClass("IN"), RRType("A")))
+
+ def __common_tsig_checks(self, expected_file):
+ renderer = MessageRenderer()
+ self.r.to_wire(renderer, self.tsig_ctx)
+ actual_wire = strip_mutable_tsig_data(renderer.get_data())
+ expected_wire = strip_mutable_tsig_data(read_wire_data(expected_file))
+ self.assertEqual(expected_wire, actual_wire)
+
+ def test_to_wire_with_tsig(self):
+ self.r.set_qid(0x2d65)
+ self.__common_tsigquery_setup()
+ self.__common_tsig_checks("message_toWire2.wire")
+
+ def test_to_wire_with_edns_tsig(self):
+ self.r.set_qid(0x6cd)
+ self.__common_tsigquery_setup()
+ edns = EDNS()
+ edns.set_udp_size(4096)
+ self.r.set_edns(edns)
+ self.__common_tsig_checks("message_toWire3.wire")
+
def test_to_text(self):
message_render = create_message()
msg_str =\
""";; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4149
-;; flags: qr aa rd ; QUESTION: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
+;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;test.example.com. IN A
@@ -377,7 +422,54 @@ test.example.com. 3600 IN A 192.0.2.2
factoryFromFile,
message_parse,
"message_fromWire9")
-
+
+ def test_from_wire_with_tsig(self):
+ # Initially there should be no TSIG
+ self.assertEqual(None, self.p.get_tsig_record())
+
+ # getTSIGRecord() is only valid in the parse mode.
+ self.assertRaises(InvalidMessageOperation, self.r.get_tsig_record)
+
+ factoryFromFile(self.p, "message_toWire2.wire")
+ tsig_rr = self.p.get_tsig_record()
+ self.assertEqual(Name("www.example.com"), tsig_rr.get_name())
+ self.assertEqual(85, tsig_rr.get_length())
+ self.assertEqual(TSIGKey.HMACMD5_NAME,
+ tsig_rr.get_rdata().get_algorithm())
+
+ # If we clear the message for reuse, the recorded TSIG will be cleared.
+ self.p.clear(Message.PARSE)
+ self.assertEqual(None, self.p.get_tsig_record())
+
+ def test_from_wire_with_tsigcompressed(self):
+ # Mostly same as fromWireWithTSIG, but the TSIG owner name is
+ # compressed.
+ factoryFromFile(self.p, "message_fromWire12.wire");
+ tsig_rr = self.p.get_tsig_record()
+ self.assertEqual(Name("www.example.com"), tsig_rr.get_name())
+ # len(www.example.com) = 17, but when fully compressed, the length is
+ # 2 bytes. So the length of the record should be 15 bytes shorter.
+ self.assertEqual(70, tsig_rr.get_length())
+
+ def test_from_wire_with_badtsig(self):
+ # Multiple TSIG RRs
+ self.assertRaises(DNSMessageFORMERR, factoryFromFile,
+ self.p, "message_fromWire13.wire")
+ self.p.clear(Message.PARSE)
+
+ # TSIG in the answer section (must be in additional)
+ self.assertRaises(DNSMessageFORMERR, factoryFromFile,
+ self.p, "message_fromWire14.wire")
+ self.p.clear(Message.PARSE)
+
+ # TSIG is not the last record.
+ self.assertRaises(DNSMessageFORMERR, factoryFromFile,
+ self.p, "message_fromWire15.wire")
+ self.p.clear(Message.PARSE)
+
+ # Unexpected RR Class (this will fail in constructing TSIGRecord)
+ self.assertRaises(DNSMessageFORMERR, factoryFromFile,
+ self.p, "message_fromWire16.wire")
if __name__ == '__main__':
unittest.main()
diff --git a/src/lib/dns/python/tests/messagerenderer_python_test.py b/src/lib/dns/python/tests/messagerenderer_python_test.py
index 544ad23..5362496 100644
--- a/src/lib/dns/python/tests/messagerenderer_python_test.py
+++ b/src/lib/dns/python/tests/messagerenderer_python_test.py
@@ -98,6 +98,11 @@ class MessageRendererTest(unittest.TestCase):
renderer.set_length_limit(1024)
self.assertEqual(1024, renderer.get_length_limit())
self.assertRaises(TypeError, renderer.set_length_limit, "wrong")
+ # Range check. We need to do this at the binding level, so we need
+ # explicit tests for it.
+ renderer.set_length_limit(0)
+ self.assertEqual(0, renderer.get_length_limit())
+ self.assertRaises(ValueError, renderer.set_length_limit, -1)
def test_messagerenderer_set_compress_mode(self):
renderer = MessageRenderer()
diff --git a/src/lib/dns/python/tests/name_python_test.py b/src/lib/dns/python/tests/name_python_test.py
index 81060e9..b8e625a 100644
--- a/src/lib/dns/python/tests/name_python_test.py
+++ b/src/lib/dns/python/tests/name_python_test.py
@@ -95,11 +95,14 @@ class NameTest(unittest.TestCase):
b = bytearray()
b += b'\x07example'*32 + b'\x03com\x00'
self.assertRaises(DNSMessageFORMERR, Name, b, 0)
+ self.assertRaises(IndexError, Name, b, -1)
def test_at(self):
self.assertEqual(7, self.name1.at(0))
self.assertEqual(101, self.name1.at(1))
self.assertRaises(IndexError, self.name1.at, 100)
+ self.assertRaises(IndexError, self.name1.at, 0x10000)
+ self.assertRaises(IndexError, self.name1.at, -1)
self.assertRaises(TypeError, self.name1.at, "wrong")
def test_get_length(self):
@@ -151,14 +154,25 @@ class NameTest(unittest.TestCase):
self.assertEqual("completely.different.", s.to_text())
self.assertRaises(TypeError, self.name1.split, "wrong", 1)
self.assertRaises(TypeError, self.name1.split, 1, "wrong")
- self.assertRaises(IndexError, self.name1.split, 123, 1)
- self.assertRaises(IndexError, self.name1.split, 1, 123)
s = self.name1.split(1)
self.assertEqual("com.", s.to_text())
+
+ # Range check. We need to do this at the binding level, so we need
+ # explicit tests for it.
+ self.assertRaises(IndexError, self.name1.split, 123, 1)
+ self.assertRaises(IndexError, self.name1.split, 1, 123)
+ self.assertRaises(IndexError, self.name1.split, 0x10000, 5)
+ self.assertRaises(IndexError, self.name1.split, -1, -1)
+ self.assertRaises(IndexError, self.name1.split, 0, -1)
+ self.assertRaises(IndexError, self.name1.split, -1, 0x10000)
+
s = self.name1.split(0)
self.assertEqual("example.com.", s.to_text())
self.assertRaises(IndexError, self.name1.split, 123)
+ self.assertRaises(IndexError, self.name1.split, 0x10000)
+ self.assertRaises(IndexError, self.name1.split, -123)
+ self.assertRaises(TypeError, self.name1.split, -1)
def test_reverse(self):
self.assertEqual("com.example.", self.name1.reverse().to_text())
@@ -169,7 +183,6 @@ class NameTest(unittest.TestCase):
self.assertEqual("example.com.example.com.", self.name1.concatenate(self.name1).to_text())
self.assertRaises(TypeError, self.name1.concatenate, "wrong")
self.assertRaises(TooLongName, self.name1.concatenate, Name("example."*31))
-
def test_downcase(self):
self.assertEqual("EXAMPLE.com.", self.name4.to_text())
diff --git a/src/lib/dns/python/tests/question_python_test.py b/src/lib/dns/python/tests/question_python_test.py
index 6c4db4b..69e3051 100644
--- a/src/lib/dns/python/tests/question_python_test.py
+++ b/src/lib/dns/python/tests/question_python_test.py
@@ -45,7 +45,7 @@ class QuestionTest(unittest.TestCase):
# tests below based on cpp unit tests
# also tests get_name, get_class and get_type
def test_from_wire(self):
-
+
q = question_from_wire("question_fromWire")
self.assertEqual(self.example_name1, q.get_name())
diff --git a/src/lib/dns/python/tests/rcode_python_test.py b/src/lib/dns/python/tests/rcode_python_test.py
index 3577f84..77fed3a 100644
--- a/src/lib/dns/python/tests/rcode_python_test.py
+++ b/src/lib/dns/python/tests/rcode_python_test.py
@@ -23,12 +23,8 @@ from pydnspp import *
class RcodeTest(unittest.TestCase):
def test_init(self):
self.assertRaises(TypeError, Rcode, "wrong")
- self.assertRaises(OverflowError, Rcode, 65536)
- self.assertEqual(Rcode(0).get_code(), 0)
-
- self.assertEqual(0, Rcode(0).get_code())
self.assertEqual(0xfff, Rcode(0xfff).get_code()) # possible max code
-
+
# should fail on attempt of construction with an out of range code
self.assertRaises(OverflowError, Rcode, 0x1000)
self.assertRaises(OverflowError, Rcode, 0xffff)
@@ -38,7 +34,18 @@ class RcodeTest(unittest.TestCase):
self.assertEqual(Rcode.BADVERS_CODE, Rcode(0, 1).get_code())
self.assertEqual(0xfff, Rcode(0xf, 0xff).get_code())
self.assertRaises(OverflowError, Rcode, 0x10, 0xff)
-
+
+ # Range check. We need to do this at the binding level, so we need
+ # explicit tests for it.
+ self.assertEqual(Rcode(0).get_code(), 0)
+ self.assertEqual(Rcode(4095).get_code(), 4095)
+ self.assertEqual(Rcode(0, 0).get_code(), 0)
+ self.assertEqual(Rcode(0, 0).get_extended_code(), 0)
+ self.assertEqual(Rcode(15, 255).get_code(), 4095)
+ self.assertRaises(ValueError, Rcode, 65536)
+ self.assertRaises(ValueError, Rcode, 0x10, 0x100)
+ self.assertRaises(ValueError, Rcode, 0x100, 0x10)
+
def test_constants(self):
self.assertEqual(Rcode.NOERROR_CODE, Rcode(0).get_code())
self.assertEqual(Rcode.FORMERR_CODE, Rcode(1).get_code())
diff --git a/src/lib/dns/python/tests/rrclass_python_test.py b/src/lib/dns/python/tests/rrclass_python_test.py
index b5e8b5e..38d8c8c 100644
--- a/src/lib/dns/python/tests/rrclass_python_test.py
+++ b/src/lib/dns/python/tests/rrclass_python_test.py
@@ -32,12 +32,17 @@ class RRClassTest(unittest.TestCase):
b = bytearray(1)
b[0] = 123
self.assertRaises(TypeError, RRClass, b)
- self.assertRaises(InvalidRRClass, RRClass, 65536)
self.assertEqual(self.c1, RRClass(1))
b = bytearray()
self.c1.to_wire(b)
self.assertEqual(self.c1, RRClass(b))
-
+ # Range check. We need to do this at the binding level, so we need
+ # explicit tests for it.
+ self.assertRaises(ValueError, RRClass, 65536)
+ self.assertRaises(TypeError, RRClass, -1)
+ self.assertEqual(RRClass(65535).get_code(), 65535)
+ self.assertEqual(RRClass(0).get_code(), 0)
+
def test_rrclass_to_text(self):
self.assertEqual("IN", self.c1.to_text())
self.assertEqual("IN", str(self.c1))
diff --git a/src/lib/dns/python/tests/rrttl_python_test.py b/src/lib/dns/python/tests/rrttl_python_test.py
index 095a0b1..aa4a34d 100644
--- a/src/lib/dns/python/tests/rrttl_python_test.py
+++ b/src/lib/dns/python/tests/rrttl_python_test.py
@@ -25,7 +25,7 @@ class RRTTLTest(unittest.TestCase):
def setUp(self):
self.t1 = RRTTL(1)
self.t2 = RRTTL(3600)
-
+
def test_init(self):
self.assertRaises(InvalidRRTTL, RRTTL, "wrong")
self.assertRaises(TypeError, RRTTL, Exception())
@@ -39,7 +39,13 @@ class RRTTLTest(unittest.TestCase):
b[2] = 0
b[3] = 15
self.assertEqual(15, RRTTL(b).get_value())
-
+ # Range check. We need to do this at the binding level, so we need
+ # explicit tests for it.
+ self.assertRaises(TypeError, RRTTL, -1)
+ self.assertRaises(ValueError, RRTTL, 4294967296)
+ self.assertEqual(0, RRTTL(0).get_value())
+ self.assertEqual(4294967295, RRTTL(4294967295).get_value())
+
def test_rrttl_to_text(self):
self.assertEqual("1", self.t1.to_text())
self.assertEqual("1", str(self.t1))
diff --git a/src/lib/dns/python/tests/rrtype_python_test.py b/src/lib/dns/python/tests/rrtype_python_test.py
index c35e1e7..7135426 100644
--- a/src/lib/dns/python/tests/rrtype_python_test.py
+++ b/src/lib/dns/python/tests/rrtype_python_test.py
@@ -22,7 +22,7 @@ import os
from pydnspp import *
class TestModuleSpec(unittest.TestCase):
-
+
rrtype_1 = RRType(1)
rrtype_0x80 = RRType(0x80);
rrtype_0x800 = RRType(0x800);
@@ -32,24 +32,28 @@ class TestModuleSpec(unittest.TestCase):
def test_init(self):
- self.assertRaises(InvalidRRType, RRType, 65537)
b = bytearray(b'\x00\x01')
self.assertEqual(RRType("A"), RRType(b))
b = bytearray(b'\x01')
self.assertRaises(IncompleteRRType, RRType, b)
self.assertRaises(TypeError, RRType, Exception)
-
+ # Range check. We need to do this at the binding level, so we need
+ # explicit tests for it.
+ self.assertRaises(ValueError, RRType, 65536)
+ self.assertRaises(TypeError, RRType, -1)
+ self.assertEqual("TYPE65535", RRType(65535).to_text());
+ self.assertEqual("TYPE0", RRType(0).to_text());
+
def test_init_from_text(self):
self.assertEqual("A", RRType("A").to_text())
self.assertEqual("NS", RRType("NS").to_text());
self.assertEqual("NS", str(RRType("NS")));
-
self.assertEqual("TYPE65535", RRType("TYPE65535").to_text());
-
+
self.assertEqual(53, RRType("TYPE00053").get_code());
self.assertRaises(InvalidRRType, RRType, "TYPE000053");
-
+
self.assertRaises(InvalidRRType, RRType, "TYPE");
self.assertRaises(InvalidRRType, RRType, "TYPE-1");
self.assertRaises(InvalidRRType, RRType, "TYPExxx");
diff --git a/src/lib/dns/python/tests/tsig_python_test.py b/src/lib/dns/python/tests/tsig_python_test.py
new file mode 100644
index 0000000..7e5515d
--- /dev/null
+++ b/src/lib/dns/python/tests/tsig_python_test.py
@@ -0,0 +1,554 @@
+# Copyright (C) 2011 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.
+
+import base64, sys, time, unittest
+from pydnspp import *
+from testutil import *
+from pyunittests_util import fix_current_time
+
+# bit-wise constant flags to configure DNS header flags for test
+# messages.
+QR_FLAG = 0x1
+AA_FLAG = 0x2
+RD_FLAG = 0x4
+
+COMMON_EXPECTED_MAC = b"\x22\x70\x26\xad\x29\x7b\xee\xe7\x21\xce\x6c\x6f\xff\x1e\x9e\xf3"
+DUMMY_DATA = b"\xdd" * 100
+
+class TSIGContextTest(unittest.TestCase):
+ tsig_key = TSIGKey('www.example.com:SFuWd/q99SzF8Yzd1QbB9g==')
+
+ def setUp(self):
+ # make sure we don't use faked time unless explicitly do so in tests
+ fix_current_time(None)
+ self.qid = 0x2d65
+ self.test_name = Name("www.example.com")
+ self.tsig_ctx = TSIGContext(self.tsig_key)
+ self.tsig_verify_ctx = TSIGContext(self.tsig_key)
+ self.keyring = TSIGKeyRing()
+ self.message = Message(Message.RENDER)
+ self.renderer = MessageRenderer()
+ self.test_class = RRClass.IN()
+ self.test_ttl = RRTTL(86400)
+ self.secret = base64.b64decode(b"SFuWd/q99SzF8Yzd1QbB9g==")
+ self.tsig_ctx = TSIGContext(TSIGKey(self.test_name,
+ TSIGKey.HMACMD5_NAME,
+ self.secret))
+ self.badkey_name = Name("badkey.example.com")
+ self.dummy_record = TSIGRecord(self.badkey_name,
+ TSIG("hmac-md5.sig-alg.reg.int. " + \
+ "1302890362 300 0 11621 " + \
+ "0 0"))
+
+ def tearDown(self):
+ # reset any faked current time setting (it would affect other tests)
+ fix_current_time(None)
+
+ # Note: intentionally use camelCase so that we can easily copy-paste
+ # corresponding C++ tests.
+ def createMessageAndSign(self, id, qname, ctx, message_flags=RD_FLAG,
+ qtype=RRType.A(), answer_data=None,
+ answer_type=None, add_question=True,
+ rcode=Rcode.NOERROR()):
+ self.message.clear(Message.RENDER)
+ self.message.set_qid(id)
+ self.message.set_opcode(Opcode.QUERY())
+ self.message.set_rcode(rcode)
+ if (message_flags & QR_FLAG) != 0:
+ self.message.set_header_flag(Message.HEADERFLAG_QR)
+ if (message_flags & AA_FLAG) != 0:
+ self.message.set_header_flag(Message.HEADERFLAG_AA)
+ if (message_flags & RD_FLAG) != 0:
+ self.message.set_header_flag(Message.HEADERFLAG_RD)
+ if add_question:
+ self.message.add_question(Question(qname, self.test_class, qtype))
+ if answer_data is not None:
+ if answer_type is None:
+ answer_type = qtype
+ answer_rrset = RRset(qname, self.test_class, answer_type,
+ self.test_ttl)
+ answer_rrset.add_rdata(Rdata(answer_type, self.test_class,
+ answer_data))
+ self.message.add_rrset(Message.SECTION_ANSWER, answer_rrset)
+ self.renderer.clear()
+ self.message.to_wire(self.renderer)
+
+ if ctx.get_state() == TSIGContext.STATE_INIT:
+ expected_new_state = TSIGContext.STATE_SENT_REQUEST
+ else:
+ expected_new_state = TSIGContext.STATE_SENT_RESPONSE
+ tsig = ctx.sign(id, self.renderer.get_data())
+
+ return tsig
+
+ # Note: intentionally use camelCase so that we can easily copy-paste
+ # corresponding C++ tests.
+ def createMessageFromFile(self, file):
+ self.message.clear(Message.PARSE)
+ self.received_data = read_wire_data(file)
+ self.message.from_wire(self.received_data)
+
+ # Note: intentionally use camelCase so that we can easily copy-paste
+ # corresponding C++ tests.
+ def commonSignChecks(self, tsig, expected_qid, expected_timesigned,
+ expected_mac, expected_error=0,
+ expected_otherdata=None,
+ expected_algorithm=TSIGKey.HMACMD5_NAME):
+ tsig_rdata = tsig.get_rdata()
+ self.assertEqual(expected_algorithm, tsig_rdata.get_algorithm())
+ self.assertEqual(expected_timesigned, tsig_rdata.get_timesigned())
+ self.assertEqual(300, tsig_rdata.get_fudge())
+ self.assertEqual(expected_mac, tsig_rdata.get_mac())
+ self.assertEqual(expected_qid, tsig_rdata.get_original_id())
+ self.assertEqual(expected_error, tsig_rdata.get_error())
+ self.assertEqual(expected_otherdata, tsig_rdata.get_other_data())
+
+ def test_initial_state(self):
+ # Until signing or verifying, the state should be INIT
+ self.assertEqual(TSIGContext.STATE_INIT, self.tsig_ctx.get_state())
+
+ # And there should be no error code.
+ self.assertEqual(TSIGError(Rcode.NOERROR()), self.tsig_ctx.get_error())
+
+ # Note: intentionally use camelCase so that we can easily copy-paste
+ # corresponding C++ tests.
+ def commonVerifyChecks(self, ctx, record, data, expected_error,
+ expected_new_state=\
+ TSIGContext.STATE_VERIFIED_RESPONSE):
+ self.assertEqual(expected_error, ctx.verify(record, data))
+ self.assertEqual(expected_error, ctx.get_error())
+ self.assertEqual(expected_new_state, ctx.get_state())
+
+ def test_from_keyring(self):
+ # Construct a TSIG context with an empty key ring. Key shouldn't be
+ # found, and the BAD_KEY error should be recorded.
+ ctx = TSIGContext(self.test_name, TSIGKey.HMACMD5_NAME, self.keyring)
+ self.assertEqual(TSIGContext.STATE_INIT, ctx.get_state())
+ self.assertEqual(TSIGError.BAD_KEY, ctx.get_error())
+ # check get_error() doesn't cause ref leak. Note: we can't
+ # realiably do this check for get_state(), as it returns an integer
+ # object, which could have many references
+ self.assertEqual(1, sys.getrefcount(ctx.get_error()))
+
+ # Add a matching key (we don't use the secret so leave it empty), and
+ # construct it again. This time it should be constructed with a valid
+ # key.
+ self.keyring.add(TSIGKey(self.test_name, TSIGKey.HMACMD5_NAME, b""))
+ ctx = TSIGContext(self.test_name, TSIGKey.HMACMD5_NAME, self.keyring)
+ self.assertEqual(TSIGContext.STATE_INIT, ctx.get_state())
+ self.assertEqual(TSIGError.NOERROR, ctx.get_error())
+
+ # Similar to the first case except that the key ring isn't empty but
+ # it doesn't contain a matching key.
+ ctx = TSIGContext(self.test_name, TSIGKey.HMACSHA1_NAME, self.keyring)
+ self.assertEqual(TSIGContext.STATE_INIT, ctx.get_state())
+ self.assertEqual(TSIGError.BAD_KEY, ctx.get_error())
+
+ ctx = TSIGContext(Name("different-key.example"),
+ TSIGKey.HMACMD5_NAME, self.keyring)
+ self.assertEqual(TSIGContext.STATE_INIT, ctx.get_state())
+ self.assertEqual(TSIGError.BAD_KEY, ctx.get_error())
+
+ # "Unknown" algorithm name will result in BADKEY, too.
+ ctx = TSIGContext(self.test_name, Name("unknown.algorithm"),
+ self.keyring)
+ self.assertEqual(TSIGContext.STATE_INIT, ctx.get_state())
+ self.assertEqual(TSIGError.BAD_KEY, ctx.get_error())
+
+ def test_sign(self):
+ fix_current_time(0x4da8877a)
+ tsig = self.createMessageAndSign(self.qid, self.test_name,
+ self.tsig_ctx)
+ self.commonSignChecks(tsig, self.qid, 0x4da8877a, COMMON_EXPECTED_MAC)
+
+ # Same test as sign, but specifying the key name with upper-case (i.e.
+ # non canonical) characters. The digest must be the same. It should
+ # actually be ensured at the level of TSIGKey, but we confirm that at
+ # this level, too.
+ def test_sign_using_uppercase_keyname(self):
+ fix_current_time(0x4da8877a)
+ cap_ctx = TSIGContext(TSIGKey(Name("WWW.EXAMPLE.COM"),
+ TSIGKey.HMACMD5_NAME, self.secret))
+ tsig = self.createMessageAndSign(self.qid, self.test_name, cap_ctx)
+ self.commonSignChecks(tsig, self.qid, 0x4da8877a, COMMON_EXPECTED_MAC)
+
+ # Same as the previous test, but for the algorithm name.
+ def test_sign_using_uppercase_algorithm_name(self):
+ fix_current_time(0x4da8877a)
+ cap_ctx = TSIGContext(TSIGKey(self.test_name,
+ Name("HMAC-md5.SIG-alg.REG.int"),
+ self.secret))
+ tsig = self.createMessageAndSign(self.qid, self.test_name, cap_ctx)
+ self.commonSignChecks(tsig, self.qid, 0x4da8877a, COMMON_EXPECTED_MAC)
+
+ # Sign the message using the actual time, and check the accuracy of it.
+ # We cannot reasonably predict the expected MAC, so don't bother to
+ # check it.
+ def test_sign_at_actual_time(self):
+ now = int(time.time())
+ tsig = self.createMessageAndSign(self.qid, self.test_name,
+ self.tsig_ctx)
+ tsig_rdata = tsig.get_rdata()
+
+ # Check the resulted time signed is in the range of [now, now + 5]
+ self.assertTrue(now <= tsig_rdata.get_timesigned())
+ self.assertTrue(now + 5 >= tsig_rdata.get_timesigned())
+
+ def test_bad_data(self):
+ self.assertRaises(TypeError, self.tsig_ctx.sign, None, 10)
+
+ def test_verify_bad_data(self):
+ # the data must at least hold the DNS message header and the specified
+ # TSIG.
+ bad_len = 12 + self.dummy_record.get_length() - 1
+ self.assertRaises(InvalidParameter, self.tsig_ctx.verify,
+ self.dummy_record, DUMMY_DATA[:bad_len])
+
+ def test_sign_using_hmacsha1(self):
+ fix_current_time(0x4dae7d5f)
+
+ secret = base64.b64decode(b"MA+QDhXbyqUak+qnMFyTyEirzng=")
+ sha1_ctx = TSIGContext(TSIGKey(self.test_name, TSIGKey.HMACSHA1_NAME,
+ secret))
+ qid = 0x0967
+ expected_mac = b"\x41\x53\x40\xc7\xda\xf8\x24\xed\x68\x4e\xe5\x86" + \
+ b"\xf7\xb5\xa6\x7a\x2f\xeb\xc0\xd3"
+ tsig = self.createMessageAndSign(qid, self.test_name, sha1_ctx)
+ self.commonSignChecks(tsig, qid, 0x4dae7d5f, expected_mac,
+ 0, None, TSIGKey.HMACSHA1_NAME)
+
+ def test_verify_then_sign_response(self):
+ fix_current_time(0x4da8877a)
+
+ self.createMessageFromFile("message_toWire2.wire")
+ self.commonVerifyChecks(self.tsig_verify_ctx,
+ self.message.get_tsig_record(),
+ self.received_data, TSIGError.NOERROR,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ tsig = self.createMessageAndSign(self.qid, self.test_name,
+ self.tsig_verify_ctx,
+ QR_FLAG|AA_FLAG|RD_FLAG,
+ RRType.A(), "192.0.2.1")
+
+ expected_mac = b"\x8f\xcd\xa6\x6a\x7c\xd1\xa3\xb9\x94\x8e\xb1\x86" + \
+ b"\x9d\x38\x4a\x9f"
+ self.commonSignChecks(tsig, self.qid, 0x4da8877a, expected_mac)
+
+ def test_verify_uppercase_names(self):
+ fix_current_time(0x4da8877a)
+
+ self.createMessageFromFile("tsig_verify9.wire")
+ self.commonVerifyChecks(self.tsig_verify_ctx,
+ self.message.get_tsig_record(),
+ self.received_data, TSIGError.NOERROR,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ def test_verify_forward_message(self):
+ fix_current_time(0x4da8877a)
+
+ self.createMessageFromFile("tsig_verify6.wire")
+ self.commonVerifyChecks(self.tsig_verify_ctx,
+ self.message.get_tsig_record(),
+ self.received_data, TSIGError.NOERROR,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ def test_sign_continuation(self):
+ fix_current_time(0x4da8e951)
+
+ axfr_qid = 0x3410
+ zone_name = Name("example.com")
+
+ tsig = self.createMessageAndSign(axfr_qid, zone_name, self.tsig_ctx,
+ 0, RRType.AXFR())
+
+ received_data = read_wire_data("tsig_verify1.wire")
+ self.commonVerifyChecks(self.tsig_verify_ctx, tsig, received_data,
+ TSIGError.NOERROR,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ tsig = self.createMessageAndSign(axfr_qid, zone_name,
+ self.tsig_verify_ctx,
+ AA_FLAG|QR_FLAG, RRType.AXFR(),
+ "ns.example.com. root.example.com." +\
+ " 2011041503 7200 3600 2592000 1200",
+ RRType.SOA())
+
+ received_data = read_wire_data("tsig_verify2.wire")
+ self.commonVerifyChecks(self.tsig_ctx, tsig, received_data,
+ TSIGError.NOERROR)
+
+ expected_mac = b"\x10\x24\x58\xf7\xf6\x2d\xdd\x7d\x63\x8d\x74" +\
+ b"\x60\x34\x13\x09\x68"
+ tsig = self.createMessageAndSign(axfr_qid, zone_name,
+ self.tsig_verify_ctx,
+ AA_FLAG|QR_FLAG, RRType.AXFR(),
+ "ns.example.com.", RRType.NS(),
+ False)
+ self.commonSignChecks(tsig, axfr_qid, 0x4da8e951, expected_mac)
+
+ received_data = read_wire_data("tsig_verify3.wire")
+ self.commonVerifyChecks(self.tsig_ctx, tsig, received_data,
+ TSIGError.NOERROR)
+
+ def test_badtime_response(self):
+ fix_current_time(0x4da8b9d6)
+
+ test_qid = 0x7fc4
+ tsig = self.createMessageAndSign(test_qid, self.test_name,
+ self.tsig_ctx, 0, RRType.SOA())
+
+ # "advance the clock" and try validating, which should fail due to
+ # BADTIME
+ fix_current_time(0x4da8be86)
+ self.commonVerifyChecks(self.tsig_verify_ctx, tsig, DUMMY_DATA,
+ TSIGError.BAD_TIME,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ # make and sign a response in the context of TSIG error.
+ tsig = self.createMessageAndSign(test_qid, self.test_name,
+ self.tsig_verify_ctx,
+ QR_FLAG, RRType.SOA(), None, None,
+ True, Rcode.NOTAUTH())
+
+ expected_otherdata = b"\x00\x00\x4d\xa8\xbe\x86"
+ expected_mac = b"\xd4\xb0\x43\xf6\xf4\x44\x95\xec\x8a\x01\x26" +\
+ b"\x0e\x39\x15\x9d\x76"
+
+ self.commonSignChecks(tsig, self.message.get_qid(), 0x4da8b9d6,
+ expected_mac,
+ 18, # error: BADTIME
+ expected_otherdata)
+
+ def test_badtime_response2(self):
+ fix_current_time(0x4da8b9d6)
+
+ tsig = self.createMessageAndSign(self.qid, self.test_name,
+ self.tsig_ctx, 0, RRType.SOA())
+
+ # "rewind the clock" and try validating, which should fail due to
+ # BADTIME
+ fix_current_time(0x4da8b9d6 - 600)
+ self.commonVerifyChecks(self.tsig_verify_ctx, tsig, DUMMY_DATA,
+ TSIGError.BAD_TIME,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ # Test various boundary conditions. We intentionally use the magic
+ # number of 300 instead of the constant variable for testing.
+ # In the okay cases, signature is not correct, but it's sufficient to
+ # check the error code isn't BADTIME for the purpose of this test.
+ def test_badtime_boundaries(self):
+ fix_current_time(0x4da8b9d6)
+
+ tsig = self.createMessageAndSign(self.qid, self.test_name,
+ self.tsig_ctx, 0, RRType.SOA())
+
+ fix_current_time(0x4da8b9d6 + 301)
+ self.assertEqual(TSIGError.BAD_TIME,
+ self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
+
+ fix_current_time(0x4da8b9d6 + 300)
+ self.assertNotEqual(TSIGError.BAD_TIME,
+ self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
+
+ fix_current_time(0x4da8b9d6 - 301)
+ self.assertEqual(TSIGError.BAD_TIME,
+ self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
+
+ fix_current_time(0x4da8b9d6 - 300)
+ self.assertNotEqual(TSIGError.BAD_TIME,
+ self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
+
+ def test_badtime_overflow(self):
+ fix_current_time(200)
+ tsig = self.createMessageAndSign(self.qid, self.test_name,
+ self.tsig_ctx, 0, RRType.SOA())
+
+ # This should be in the okay range, but since "200 - fudge" overflows
+ # and we compare them as 64-bit unsigned integers, it results in a
+ # false positive (we intentionally accept that).
+ fix_current_time(100)
+ self.assertEqual(TSIGError.BAD_TIME,
+ self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
+
+ def test_badsig_response(self):
+ fix_current_time(0x4da8877a)
+
+ # Try to sign a simple message with bogus secret. It should fail
+ # with BADSIG.
+ self.createMessageFromFile("message_toWire2.wire")
+ bad_ctx = TSIGContext(TSIGKey(self.test_name, TSIGKey.HMACMD5_NAME,
+ DUMMY_DATA))
+ self.commonVerifyChecks(bad_ctx, self.message.get_tsig_record(),
+ self.received_data, TSIGError.BAD_SIG,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ # Sign the same message (which doesn't matter for this test) with the
+ # context of "checked state".
+ tsig = self.createMessageAndSign(self.qid, self.test_name, bad_ctx)
+ self.commonSignChecks(tsig, self.message.get_qid(), 0x4da8877a, None,
+ 16) # 16: BADSIG
+
+ def test_badkey_response(self):
+ # A similar test as badsigResponse but for BADKEY
+ fix_current_time(0x4da8877a)
+ tsig_ctx = TSIGContext(self.badkey_name, TSIGKey.HMACMD5_NAME,
+ self.keyring)
+ self.commonVerifyChecks(tsig_ctx, self.dummy_record, DUMMY_DATA,
+ TSIGError.BAD_KEY,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ sig = self.createMessageAndSign(self.qid, self.test_name, tsig_ctx)
+ self.assertEqual(self.badkey_name, sig.get_name())
+ self.commonSignChecks(sig, self.qid, 0x4da8877a, None, 17) # 17: BADKEY
+
+ def test_badkey_for_response(self):
+ # "BADKEY" case for a response to a signed message
+ self.createMessageAndSign(self.qid, self.test_name, self.tsig_ctx)
+ self.commonVerifyChecks(self.tsig_ctx, self.dummy_record, DUMMY_DATA,
+ TSIGError.BAD_KEY,
+ TSIGContext.STATE_SENT_REQUEST)
+
+ # A similar case with a different algorithm
+ dummy_record = TSIGRecord(self.test_name,
+ TSIG("hmac-sha1. 1302890362 300 0 "
+ "11621 0 0"))
+ self.commonVerifyChecks(self.tsig_ctx, dummy_record, DUMMY_DATA,
+ TSIGError.BAD_KEY,
+ TSIGContext.STATE_SENT_REQUEST)
+
+ # According to RFC2845 4.6, if TSIG verification fails the client
+ # should discard that message and wait for another signed response.
+ # This test emulates that situation.
+ def test_badsig_then_validate(self):
+ fix_current_time(0x4da8877a)
+
+ self.createMessageAndSign(self.qid, self.test_name, self.tsig_ctx)
+ self.createMessageFromFile("tsig_verify4.wire")
+
+ self.commonVerifyChecks(self.tsig_ctx, self.message.get_tsig_record(),
+ self.received_data, TSIGError.BAD_SIG,
+ TSIGContext.STATE_SENT_REQUEST)
+
+ self.createMessageFromFile("tsig_verify5.wire")
+ self.commonVerifyChecks(self.tsig_ctx, self.message.get_tsig_record(),
+ self.received_data, TSIGError.NOERROR,
+ TSIGContext.STATE_VERIFIED_RESPONSE)
+
+ # Similar to the previous test, but the first response doesn't contain
+ # TSIG.
+ def test_nosig_then_validate(self):
+ fix_current_time(0x4da8877a)
+ self.createMessageAndSign(self.qid, self.test_name, self.tsig_ctx)
+
+ self.commonVerifyChecks(self.tsig_ctx, None, DUMMY_DATA,
+ TSIGError.FORMERR, TSIGContext.STATE_SENT_REQUEST)
+
+ self.createMessageFromFile("tsig_verify5.wire")
+ self.commonVerifyChecks(self.tsig_ctx, self.message.get_tsig_record(),
+ self.received_data, TSIGError.NOERROR,
+ TSIGContext.STATE_VERIFIED_RESPONSE)
+
+ # Similar to the previous test, but the first response results in BADTIME.
+ def test_badtime_then_validate(self):
+ fix_current_time(0x4da8877a)
+ tsig = self.createMessageAndSign(self.qid, self.test_name,
+ self.tsig_ctx)
+
+ # "advance the clock" and try validating, which should fail due to
+ # BADTIME
+ fix_current_time(0x4da8877a + 600)
+ self.commonVerifyChecks(self.tsig_ctx, tsig, DUMMY_DATA,
+ TSIGError.BAD_TIME, TSIGContext.STATE_SENT_REQUEST)
+
+ # revert the clock again.
+ fix_current_time(0x4da8877a)
+ self.createMessageFromFile("tsig_verify5.wire")
+ self.commonVerifyChecks(self.tsig_ctx, self.message.get_tsig_record(),
+ self.received_data, TSIGError.NOERROR,
+ TSIGContext.STATE_VERIFIED_RESPONSE)
+
+ # We don't allow empty MAC unless the TSIG error is BADSIG or BADKEY.
+ def test_empty_mac(self):
+ fix_current_time(0x4da8877a)
+
+ self.createMessageFromFile("tsig_verify7.wire")
+
+ self.commonVerifyChecks(self.tsig_verify_ctx,
+ self.message.get_tsig_record(),
+ self.received_data,
+ TSIGError.BAD_SIG,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ # If the empty MAC comes with a BADKEY error, the error is passed
+ # transparently.
+ self.createMessageFromFile("tsig_verify8.wire")
+ self.commonVerifyChecks(self.tsig_verify_ctx,
+ self.message.get_tsig_record(),
+ self.received_data,
+ TSIGError.BAD_KEY,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ # Once the context is used for sending a signed response, it shouldn't
+ # be used for further verification.
+ def test_verify_after_sendresponse(self):
+ fix_current_time(0x4da8877a)
+
+ self.createMessageFromFile("message_toWire2.wire")
+ self.tsig_verify_ctx.verify(self.message.get_tsig_record(),
+ self.received_data)
+ self.assertEqual(TSIGContext.STATE_RECEIVED_REQUEST,
+ self.tsig_verify_ctx.get_state())
+ self.createMessageAndSign(self.qid, self.test_name,
+ self.tsig_verify_ctx,
+ QR_FLAG|AA_FLAG|RD_FLAG, RRType.A(),
+ "192.0.2.1")
+ self.assertEqual(TSIGContext.STATE_SENT_RESPONSE,
+ self.tsig_verify_ctx.get_state())
+
+ # Now trying further verification.
+ self.createMessageFromFile("message_toWire2.wire")
+ self.assertRaises(TSIGContextError, self.tsig_verify_ctx.verify,
+ self.message.get_tsig_record(), self.received_data)
+
+ # Likewise, once the context verifies a response, it shouldn't for
+ # signing any more.
+ def test_sign_after_verified(self):
+ fix_current_time(0x4da8877a)
+
+ self.createMessageAndSign(self.qid, self.test_name, self.tsig_ctx)
+ self.createMessageFromFile("tsig_verify5.wire")
+ self.tsig_ctx.verify(self.message.get_tsig_record(),
+ self.received_data)
+ self.assertEqual(TSIGContext.STATE_VERIFIED_RESPONSE,
+ self.tsig_ctx.get_state())
+
+ # Now trying further signing.
+ self.assertRaises(TSIGContextError, self.createMessageAndSign,
+ self.qid, self.test_name, self.tsig_ctx)
+
+ # Too short MAC should be rejected.
+ # Note: when we implement RFC4635-based checks, the error code will
+ # (probably) be FORMERR.
+ def test_too_short_mac(self):
+ fix_current_time(0x4da8877a)
+ self.createMessageFromFile("tsig_verify10.wire")
+ self.commonVerifyChecks(self.tsig_verify_ctx,
+ self.message.get_tsig_record(),
+ self.received_data, TSIGError.BAD_SIG,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/lib/dns/python/tests/tsig_rdata_python_test.py b/src/lib/dns/python/tests/tsig_rdata_python_test.py
new file mode 100644
index 0000000..7b861d6
--- /dev/null
+++ b/src/lib/dns/python/tests/tsig_rdata_python_test.py
@@ -0,0 +1,30 @@
+# Copyright (C) 2011 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.
+
+import unittest
+import sys
+from pydnspp import *
+
+class TSIGRdataTest(unittest.TestCase):
+ VALID_TEXT1 = "hmac-md5.sig-alg.reg.int. 1286779327 300 0 16020 BADKEY 0"
+ def test_from_string(self):
+ tsig = TSIG(self.VALID_TEXT1)
+ self.assertEqual(Name("hmac-md5.sig-alg.reg.int"),
+ tsig.get_algorithm())
+ # check there's no leak in creating the name object:
+ self.assertEqual(1, sys.getrefcount(tsig.get_algorithm()))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/lib/dns/python/tests/tsigerror_python_test.py b/src/lib/dns/python/tests/tsigerror_python_test.py
new file mode 100644
index 0000000..a968b6b
--- /dev/null
+++ b/src/lib/dns/python/tests/tsigerror_python_test.py
@@ -0,0 +1,97 @@
+# Copyright (C) 2011 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.
+
+import unittest
+import sys
+from pydnspp import *
+
+class TSIGErrorTest(unittest.TestCase):
+ def test_from_code(self):
+ self.assertEqual(0, TSIGError(0).get_code())
+ self.assertEqual(18, TSIGError(18).get_code())
+ self.assertEqual(65535, TSIGError(65535).get_code())
+ self.assertRaises(ValueError, TSIGError, 65536)
+ self.assertRaises(ValueError, TSIGError, -1)
+ self.assertRaises(TypeError, TSIGError, "not yet supported")
+
+ def test_from_rcode(self):
+ # We use RCODE for code values from 0-15.
+ self.assertEqual(0, TSIGError(Rcode.NOERROR()).get_code())
+ self.assertEqual(15, TSIGError(Rcode(15)).get_code())
+
+ # From error code 16 TSIG errors define a separate space, so passing
+ # corresponding RCODE for such code values should be prohibited.
+ self.assertRaises(ValueError, TSIGError, Rcode(16))
+
+ def test_constants(self):
+ # We'll only test arbitrarily chosen subsets of the codes.
+ # This class is quite simple, so it should be suffice.
+ self.assertEqual(TSIGError.BAD_SIG_CODE, TSIGError(16).get_code())
+ self.assertEqual(TSIGError.BAD_KEY_CODE, TSIGError(17).get_code())
+ self.assertEqual(TSIGError.BAD_TIME_CODE, TSIGError(18).get_code())
+
+ self.assertEqual(0, TSIGError.NOERROR.get_code())
+ self.assertEqual(9, TSIGError.NOTAUTH.get_code())
+ self.assertEqual(14, TSIGError.RESERVED14.get_code())
+ self.assertEqual(TSIGError.BAD_SIG_CODE, TSIGError.BAD_SIG.get_code())
+ self.assertEqual(TSIGError.BAD_KEY_CODE, TSIGError.BAD_KEY.get_code())
+ self.assertEqual(TSIGError.BAD_TIME_CODE, TSIGError.BAD_TIME.get_code())
+
+ def test_equal(self):
+ self.assertTrue(TSIGError.NOERROR == TSIGError(Rcode.NOERROR()))
+ self.assertTrue(TSIGError(Rcode.NOERROR()) == TSIGError.NOERROR)
+
+ self.assertTrue(TSIGError.BAD_SIG == TSIGError(16))
+ self.assertTrue(TSIGError(16) == TSIGError.BAD_SIG)
+
+ def test_nequal(self):
+ self.assertTrue(TSIGError.BAD_KEY != TSIGError(Rcode.NOERROR()))
+ self.assertTrue(TSIGError(Rcode.NOERROR()) != TSIGError.BAD_KEY)
+
+ def test_to_text(self):
+ # TSIGError derived from the standard Rcode
+ self.assertEqual("NOERROR", TSIGError(Rcode.NOERROR()).to_text())
+
+ # Well known TSIG errors
+ self.assertEqual("BADSIG", TSIGError.BAD_SIG.to_text())
+ self.assertEqual("BADKEY", TSIGError.BAD_KEY.to_text())
+ self.assertEqual("BADTIME", TSIGError.BAD_TIME.to_text())
+
+ # Unknown (or not yet supported) codes. Simply converted as numeric.
+ self.assertEqual("19", TSIGError(19).to_text());
+ self.assertEqual("65535", TSIGError(65535).to_text());
+
+ # also check str() works same way
+ self.assertEqual("NOERROR", str(TSIGError(Rcode.NOERROR())))
+ self.assertEqual("BADSIG", str(TSIGError.BAD_SIG))
+
+ def test_to_rcode(self):
+ # TSIGError derived from the standard Rcode
+ self.assertEqual(Rcode.NOERROR(), TSIGError(Rcode.NOERROR()).to_rcode())
+
+ # Well known TSIG errors
+ self.assertEqual(Rcode.NOTAUTH(), TSIGError.BAD_SIG.to_rcode())
+ self.assertEqual(Rcode.NOTAUTH(), TSIGError.BAD_KEY.to_rcode())
+ self.assertEqual(Rcode.NOTAUTH(), TSIGError.BAD_TIME.to_rcode())
+
+ # Unknown (or not yet supported) codes are treated as SERVFAIL.
+ self.assertEqual(Rcode.SERVFAIL(), TSIGError(19).to_rcode())
+ self.assertEqual(Rcode.SERVFAIL(), TSIGError(65535).to_rcode())
+
+ # Check there's no redundant refcount (which would cause leak)
+ self.assertEqual(1, sys.getrefcount(TSIGError.BAD_SIG.to_rcode()))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/lib/dns/python/tests/tsigkey_python_test.py b/src/lib/dns/python/tests/tsigkey_python_test.py
index 06e6868..516bea4 100644
--- a/src/lib/dns/python/tests/tsigkey_python_test.py
+++ b/src/lib/dns/python/tests/tsigkey_python_test.py
@@ -25,6 +25,9 @@ class TSIGKeyTest(unittest.TestCase):
TSIGKey.HMACMD5_NAME)
self.assertEqual(Name('hmac-sha1'), TSIGKey.HMACSHA1_NAME)
self.assertEqual(Name('hmac-sha256'), TSIGKey.HMACSHA256_NAME)
+ self.assertEqual(Name('hmac-sha224'), TSIGKey.HMACSHA224_NAME)
+ self.assertEqual(Name('hmac-sha384'), TSIGKey.HMACSHA384_NAME)
+ self.assertEqual(Name('hmac-sha512'), TSIGKey.HMACSHA512_NAME)
def test_init(self):
key = TSIGKey(self.key_name, TSIGKey.HMACMD5_NAME, self.secret)
@@ -44,8 +47,33 @@ class TSIGKeyTest(unittest.TestCase):
TSIGKey.HMACMD5_NAME,
'should be binary') # signature mismatch
+ def test_str(self):
+ k1 = TSIGKey('test.example:CwsLCwsLCwsLCwsLCwsLCw==:hmac-md5.sig-alg.reg.int')
+ self.assertEqual(Name('test.example.'), k1.get_key_name())
+ self.assertEqual(Name('hmac-md5.sig-alg.reg.int.'), k1.get_algorithm_name())
+ self.assertEqual(b'\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b',
+ k1.get_secret())
+ self.assertEqual('test.example.:CwsLCwsLCwsLCwsLCwsLCw==:hmac-md5.sig-alg.reg.int.',
+ k1.to_text())
+
+ self.assertRaises(InvalidParameter, TSIGKey,
+ 'test.example:CwsLCwsLCwsLCwsLCwsLCw==:unsupported')
+ self.assertRaises(InvalidParameter, TSIGKey,
+ '::')
+ self.assertRaises(InvalidParameter, TSIGKey,
+ 'test.example:')
+ self.assertRaises(InvalidParameter, TSIGKey,
+ 'test.example:%bad_base_64%')
+ self.assertRaises(InvalidParameter, TSIGKey,
+ 'test.example:CwsLCwsLCwsLCwsLCwsLCw==:')
+ self.assertRaises(InvalidParameter, TSIGKey,
+ 'test.:example:CwsLCwsLCwsLCwsLCwsLCw==')
+
class TSIGKeyRingTest(unittest.TestCase):
key_name = Name('example.com')
+ md5_name = Name('hmac-md5.sig-alg.reg.int')
+ sha1_name = Name('hmac-sha1')
+ sha256_name = Name('hmac-sha256')
secret = b'someRandomData'
def setUp(self):
@@ -130,18 +158,26 @@ class TSIGKeyRingTest(unittest.TestCase):
def test_find(self):
self.assertEqual((TSIGKeyRing.NOTFOUND, None),
- self.keyring.find(self.key_name))
+ self.keyring.find(self.key_name, self.md5_name))
self.assertEqual(TSIGKeyRing.SUCCESS,
self.keyring.add(TSIGKey(self.key_name,
- TSIGKey.HMACSHA256_NAME,
+ self.sha256_name,
self.secret)))
- (code, key) = self.keyring.find(self.key_name)
+ (code, key) = self.keyring.find(self.key_name, self.sha256_name)
self.assertEqual(TSIGKeyRing.SUCCESS, code)
self.assertEqual(self.key_name, key.get_key_name())
self.assertEqual(TSIGKey.HMACSHA256_NAME, key.get_algorithm_name())
self.assertEqual(self.secret, key.get_secret())
+ (code, key) = self.keyring.find(Name('different-key.example'),
+ self.sha256_name)
+ self.assertEqual(TSIGKeyRing.NOTFOUND, code)
+ self.assertEqual(None, key)
+ (code, key) = self.keyring.find(self.key_name, self.md5_name)
+ self.assertEqual(TSIGKeyRing.NOTFOUND, code)
+ self.assertEqual(None, key)
+
self.assertRaises(TypeError, self.keyring.find, 1)
self.assertRaises(TypeError, self.keyring.find, 'should be a name')
self.assertRaises(TypeError, self.keyring.find, self.key_name, 0)
@@ -149,24 +185,28 @@ class TSIGKeyRingTest(unittest.TestCase):
def test_find_from_some(self):
self.assertEqual(TSIGKeyRing.SUCCESS,
self.keyring.add(TSIGKey(self.key_name,
- TSIGKey.HMACSHA256_NAME,
+ self.sha256_name,
self.secret)))
self.assertEqual(TSIGKeyRing.SUCCESS,
self.keyring.add(TSIGKey(Name('another.example'),
- TSIGKey.HMACMD5_NAME,
+ self.md5_name,
self.secret)))
self.assertEqual(TSIGKeyRing.SUCCESS,
self.keyring.add(TSIGKey(Name('more.example'),
- TSIGKey.HMACSHA1_NAME,
+ self.sha1_name,
self.secret)))
- (code, key) = self.keyring.find(Name('another.example'))
+ (code, key) = self.keyring.find(Name('another.example'), self.md5_name)
self.assertEqual(TSIGKeyRing.SUCCESS, code)
self.assertEqual(Name('another.example'), key.get_key_name())
self.assertEqual(TSIGKey.HMACMD5_NAME, key.get_algorithm_name())
self.assertEqual((TSIGKeyRing.NOTFOUND, None),
- self.keyring.find(Name('noexist.example')))
+ self.keyring.find(Name('noexist.example'),
+ self.sha1_name))
+ self.assertEqual((TSIGKeyRing.NOTFOUND, None),
+ self.keyring.find(Name('another.example'),
+ self.sha1_name))
if __name__ == '__main__':
unittest.main()
diff --git a/src/lib/dns/python/tests/tsigrecord_python_test.py b/src/lib/dns/python/tests/tsigrecord_python_test.py
new file mode 100644
index 0000000..813a23b
--- /dev/null
+++ b/src/lib/dns/python/tests/tsigrecord_python_test.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2011 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.
+
+import unittest
+import sys
+from pydnspp import *
+
+class TSIGRecordTest(unittest.TestCase):
+ def setUp(self):
+ self.test_name = Name("www.example.com")
+ self.test_rdata = TSIG("hmac-md5.sig-alg.reg.int. 1302890362 " + \
+ "300 16 2tra2tra2tra2tra2tra2g== " + \
+ "11621 0 0")
+ self.test_record = TSIGRecord(self.test_name, self.test_rdata)
+
+ def test_getname(self):
+ self.assertEqual(self.test_name, self.test_record.get_name())
+ self.assertEqual(1, sys.getrefcount(self.test_record.get_name()))
+
+ def test_get_length(self):
+ # see the C++ test for the magic number
+ self.assertEqual(85, self.test_record.get_length())
+
+ def test_to_text(self):
+ expected_text = "www.example.com. 0 ANY TSIG " + \
+ "hmac-md5.sig-alg.reg.int. 1302890362 300 16 " + \
+ "2tra2tra2tra2tra2tra2g== 11621 NOERROR 0\n"
+ self.assertEqual(expected_text, self.test_record.to_text())
+ self.assertEqual(expected_text, str(self.test_record))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc
new file mode 100644
index 0000000..db93a08
--- /dev/null
+++ b/src/lib/dns/python/tsig_python.cc
@@ -0,0 +1,364 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#define PY_SSIZE_T_CLEAN // need for "y#" below
+#include <Python.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <exceptions/exceptions.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <dns/tsig.h>
+
+#include "pydnspp_common.h"
+#include "name_python.h"
+#include "tsigkey_python.h"
+#include "tsigerror_python.h"
+#include "tsigrecord_python.h"
+#include "tsig_python.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::util::python;
+using namespace isc::dns;
+using namespace isc::dns::python;
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+//
+// TSIGContext
+//
+
+// Trivial constructor.
+s_TSIGContext::s_TSIGContext() : cppobj(NULL) {
+}
+
+namespace {
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_TSIGContext, TSIGContext> TSIGContextContainer;
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+
+// General creation and destruction
+int TSIGContext_init(s_TSIGContext* self, PyObject* args);
+void TSIGContext_destroy(s_TSIGContext* self);
+
+// Class specific methods
+PyObject* TSIGContext_getState(s_TSIGContext* self);
+PyObject* TSIGContext_getError(s_TSIGContext* self);
+PyObject* TSIGContext_sign(s_TSIGContext* self, PyObject* args);
+PyObject* TSIGContext_verify(s_TSIGContext* self, PyObject* args);
+
+// These are the functions we export
+// For a minimal support, we don't need them.
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef TSIGContext_methods[] = {
+ { "get_state", reinterpret_cast<PyCFunction>(TSIGContext_getState),
+ METH_NOARGS,
+ "Return the current state of the context (mainly for tests)" },
+ { "get_error", reinterpret_cast<PyCFunction>(TSIGContext_getError),
+ METH_NOARGS,
+ "Return the TSIG error as a result of the latest verification" },
+ { "sign",
+ reinterpret_cast<PyCFunction>(TSIGContext_sign), METH_VARARGS,
+ "Sign a DNS message." },
+ { "verify",
+ reinterpret_cast<PyCFunction>(TSIGContext_verify), METH_VARARGS,
+ "Verify a DNS message." },
+ { NULL, NULL, 0, NULL }
+};
+
+int
+TSIGContext_init(s_TSIGContext* self, PyObject* args) {
+ try {
+ // "From key" constructor
+ const s_TSIGKey* tsigkey_obj;
+ if (PyArg_ParseTuple(args, "O!", &tsigkey_type, &tsigkey_obj)) {
+ self->cppobj = new TSIGContext(*tsigkey_obj->cppobj);
+ return (0);
+ }
+
+ // "From key param + keyring" constructor
+ PyErr_Clear();
+ const s_Name* keyname_obj;
+ const s_Name* algname_obj;
+ const s_TSIGKeyRing* keyring_obj;
+ if (PyArg_ParseTuple(args, "O!O!O!", &name_type, &keyname_obj,
+ &name_type, &algname_obj, &tsigkeyring_type,
+ &keyring_obj)) {
+ self->cppobj = new TSIGContext(*keyname_obj->cppobj,
+ *algname_obj->cppobj,
+ *keyring_obj->cppobj);
+ return (0);
+ }
+ } catch (const exception& ex) {
+ const string ex_what = "Failed to construct TSIGContext object: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (-1);
+ } catch (...) {
+ PyErr_SetString(po_IscException,
+ "Unexpected exception in constructing TSIGContext");
+ return (-1);
+ }
+
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to TSIGContext constructor");
+
+ return (-1);
+}
+
+void
+TSIGContext_destroy(s_TSIGContext* const self) {
+ delete self->cppobj;
+ self->cppobj = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+TSIGContext_getState(s_TSIGContext* self) {
+ return (Py_BuildValue("I", self->cppobj->getState()));
+}
+
+PyObject*
+TSIGContext_getError(s_TSIGContext* self) {
+ try {
+ PyObjectContainer container(createTSIGErrorObject(
+ self->cppobj->getError()));
+ return (Py_BuildValue("O", container.get()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpectedly failed to get TSIGContext error: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(po_IscException,
+ "Unexpected exception in TSIGContext.get_error");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIGContext_sign(s_TSIGContext* self, PyObject* args) {
+ long qid = 0;
+ const char* mac;
+ Py_ssize_t mac_size;
+
+ if (PyArg_ParseTuple(args, "ly#", &qid, &mac, &mac_size)) {
+ if (qid < 0 || qid > 0xffff) {
+ PyErr_SetString(PyExc_ValueError,
+ "TSIGContext.sign: QID out of range");
+ return (NULL);
+ }
+
+ try {
+ ConstTSIGRecordPtr record = self->cppobj->sign(qid, mac, mac_size);
+ return (createTSIGRecordObject(*record));
+ } catch (const TSIGContextError& ex) {
+ PyErr_SetString(po_TSIGContextError, ex.what());
+ } catch (const exception& ex) {
+ const string ex_what = "Unexpected failure in TSIG sign: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in TSIG sign");
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to TSIGContext.sign");
+ }
+
+ return (NULL);
+}
+
+PyObject*
+TSIGContext_verify(s_TSIGContext* self, PyObject* args) {
+ const char* data;
+ Py_ssize_t data_len;
+ s_TSIGRecord* py_record;
+ PyObject* py_maybe_none;
+ TSIGRecord* record;
+
+ if (PyArg_ParseTuple(args, "O!y#", &tsigrecord_type, &py_record,
+ &data, &data_len)) {
+ record = py_record->cppobj;
+ } else if (PyArg_ParseTuple(args, "Oy#", &py_maybe_none, &data,
+ &data_len)) {
+ record = NULL;
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to TSIGContext.verify");
+ return (NULL);
+ }
+ PyErr_Clear();
+
+ try {
+ const TSIGError error = self->cppobj->verify(record, data, data_len);
+ return (createTSIGErrorObject(error));
+ } catch (const TSIGContextError& ex) {
+ PyErr_SetString(po_TSIGContextError, ex.what());
+ } catch (const InvalidParameter& ex) {
+ PyErr_SetString(po_InvalidParameter, ex.what());
+ } catch (const exception& ex) {
+ const string ex_what = "Unexpected failure in TSIG verify: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in TSIG verify");
+ }
+
+ return (NULL);
+}
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+// Definition of class specific exception(s)
+PyObject* po_TSIGContextError;
+
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_TSIGContext
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject tsigcontext_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp.TSIGContext",
+ sizeof(s_TSIGContext), // tp_basicsize
+ 0, // tp_itemsize
+ reinterpret_cast<destructor>(TSIGContext_destroy), // tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ NULL, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+
+ // We allow the python version of TSIGContext to act as a base class.
+ // From pure design point of view, this is wrong because it's not intended
+ // to be inherited. However, cryptographic operations are generally
+ // difficult to test, so it would be very advantageous if we can define
+ // a mock context class.
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, // tp_flags
+
+ "The TSIGContext class objects is...(COMPLETE THIS)",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ TSIGContext_methods, // tp_methods
+ NULL, // tp_members
+ NULL, // tp_getset
+ NULL, // tp_base
+ NULL, // tp_dict
+ NULL, // tp_descr_get
+ NULL, // tp_descr_set
+ 0, // tp_dictoffset
+ reinterpret_cast<initproc>(TSIGContext_init), // tp_init
+ NULL, // tp_alloc
+ PyType_GenericNew, // tp_new
+ NULL, // tp_free
+ NULL, // tp_is_gc
+ NULL, // tp_bases
+ NULL, // tp_mro
+ NULL, // tp_cache
+ NULL, // tp_subclasses
+ NULL, // tp_weaklist
+ NULL, // tp_del
+ 0 // tp_version_tag
+};
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_TSIGContext(PyObject* mod) {
+ // We initialize the static description object with PyType_Ready(),
+ // then add it to the module. This is not just a check! (leaving
+ // this out results in segmentation faults)
+ if (PyType_Ready(&tsigcontext_type) < 0) {
+ return (false);
+ }
+ void* p = &tsigcontext_type;
+ if (PyModule_AddObject(mod, "TSIGContext",
+ static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+ Py_INCREF(&tsigcontext_type);
+
+ try {
+ // Class specific exceptions
+ po_TSIGContextError = PyErr_NewException("pydnspp.TSIGContextError",
+ po_IscException, NULL);
+ PyObjectContainer(po_TSIGContextError).installToModule(
+ mod, "TSIGContextError");
+
+ // Constant class variables
+ installClassVariable(tsigcontext_type, "STATE_INIT",
+ Py_BuildValue("I", TSIGContext::INIT));
+ installClassVariable(tsigcontext_type, "STATE_SENT_REQUEST",
+ Py_BuildValue("I", TSIGContext::SENT_REQUEST));
+ installClassVariable(tsigcontext_type, "STATE_RECEIVED_REQUEST",
+ Py_BuildValue("I", TSIGContext::RECEIVED_REQUEST));
+ installClassVariable(tsigcontext_type, "STATE_SENT_RESPONSE",
+ Py_BuildValue("I", TSIGContext::SENT_RESPONSE));
+ installClassVariable(tsigcontext_type, "STATE_VERIFIED_RESPONSE",
+ Py_BuildValue("I",
+ TSIGContext::VERIFIED_RESPONSE));
+
+ installClassVariable(tsigcontext_type, "DEFAULT_FUDGE",
+ Py_BuildValue("H", TSIGContext::DEFAULT_FUDGE));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpected failure in TSIGContext initialization: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in TSIGContext initialization");
+ return (false);
+ }
+
+ return (true);
+}
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/tsig_python.h b/src/lib/dns/python/tsig_python.h
new file mode 100644
index 0000000..f9b4f7b
--- /dev/null
+++ b/src/lib/dns/python/tsig_python.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_TSIGCONTEXT_H
+#define __PYTHON_TSIGCONTEXT_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class TSIGContext;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_TSIGContext : public PyObject {
+public:
+ s_TSIGContext();
+ TSIGContext* cppobj;
+};
+
+extern PyTypeObject tsigcontext_type;
+
+// Class specific exceptions
+extern PyObject* po_TSIGContextError;
+
+bool initModulePart_TSIGContext(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_TSIGCONTEXT_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/tsig_rdata_python.cc b/src/lib/dns/python/tsig_rdata_python.cc
new file mode 100644
index 0000000..4e4f287
--- /dev/null
+++ b/src/lib/dns/python/tsig_rdata_python.cc
@@ -0,0 +1,369 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <Python.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <dns/rdataclass.h>
+
+#include "pydnspp_common.h"
+#include "pydnspp_towire.h"
+#include "name_python.h"
+#include "tsig_rdata_python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::dns::python;
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+//
+// TSIG RDATA
+//
+
+// Trivial constructor.
+s_TSIG::s_TSIG() : cppobj(NULL) {
+}
+
+namespace {
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_TSIG, any::TSIG> TSIGContainer;
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+
+// General creation and destruction
+int TSIG_init(s_TSIG* self, PyObject* args);
+void TSIG_destroy(s_TSIG* self);
+
+// These are the functions we export
+// ADD/REMOVE/MODIFY THE FOLLOWING AS APPROPRIATE FOR THE ACTUAL CLASS.
+//
+PyObject* TSIG_toText(const s_TSIG* const self);
+PyObject* TSIG_getAlgorithm(const s_TSIG* const self);
+PyObject* TSIG_getTimeSigned(const s_TSIG* const self);
+PyObject* TSIG_getFudge(const s_TSIG* const self);
+PyObject* TSIG_getOriginalID(const s_TSIG* const self);
+PyObject* TSIG_getError(const s_TSIG* const self);
+PyObject* TSIG_getMAC(const s_TSIG* const self);
+PyObject* TSIG_getOtherData(const s_TSIG* const self);
+PyObject* TSIG_str(PyObject* self);
+PyObject* TSIG_richcmp(const s_TSIG* const self,
+ const s_TSIG* const other, int op);
+PyObject* TSIG_toWire(const s_TSIG* self, PyObject* args);
+
+// These are the functions we export
+// For a minimal support, we don't need them.
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef TSIG_methods[] = {
+ { "get_algorithm", reinterpret_cast<PyCFunction>(TSIG_getAlgorithm),
+ METH_NOARGS,
+ "Return the algorithm name." },
+ { "get_timesigned", reinterpret_cast<PyCFunction>(TSIG_getTimeSigned),
+ METH_NOARGS,
+ "Return the value of the Time Signed field. "
+ "The returned value does not exceed 2^48-1."
+ },
+ { "get_fudge", reinterpret_cast<PyCFunction>(TSIG_getFudge),
+ METH_NOARGS,
+ "Return the value of the Fudge field." },
+ { "get_original_id", reinterpret_cast<PyCFunction>(TSIG_getOriginalID),
+ METH_NOARGS,
+ "Return the value of the Original ID field." },
+ { "get_error", reinterpret_cast<PyCFunction>(TSIG_getError),
+ METH_NOARGS,
+ "Return the value of the Error field." },
+ { "get_mac", reinterpret_cast<PyCFunction>(TSIG_getMAC),
+ METH_NOARGS,
+ "Return the value of the MAC field."
+ "If it's empty, return None." },
+ { "get_other_data", reinterpret_cast<PyCFunction>(TSIG_getOtherData),
+ METH_NOARGS,
+ "Return the value of the Other Data field."
+ "If it's empty, return None." },
+ { "to_text", reinterpret_cast<PyCFunction>(TSIG_toText), METH_NOARGS,
+ "Returns the text representation" },
+ { "to_wire", reinterpret_cast<PyCFunction>(TSIG_toWire), METH_VARARGS,
+ "Converts the TSIG object to wire format.\n"
+ "The argument can be either a MessageRenderer or an object that "
+ "implements the sequence interface. If the object is mutable "
+ "(for instance a bytearray()), the wire data is added in-place.\n"
+ "If it is not (for instance a bytes() object), a new object is "
+ "returned" },
+ { NULL, NULL, 0, NULL }
+};
+
+int
+TSIG_init(s_TSIG* self, PyObject* args) {
+ try {
+ // constructor from string
+ const char* rdata_str;
+ if (PyArg_ParseTuple(args, "s", &rdata_str)) {
+ self->cppobj = new any::TSIG(string(rdata_str));
+ return (0);
+ }
+ } catch (const exception& ex) {
+ const string ex_what = "Failed to construct TSIG object: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (-1);
+ } catch (...) {
+ PyErr_SetString(po_IscException,
+ "Unexpected exception in constructing TSIG");
+ return (-1);
+ }
+
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to TSIG constructor");
+
+ return (-1);
+}
+
+void
+TSIG_destroy(s_TSIG* const self) {
+ delete self->cppobj;
+ self->cppobj = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+TSIG_getAlgorithm(const s_TSIG* const self) {
+ try {
+ return (createNameObject(self->cppobj->getAlgorithm()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to get TSIG algorithm: " + string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "getting TSIG algorithm");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIG_getTimeSigned(const s_TSIG* const self) {
+ return (Py_BuildValue("K", self->cppobj->getTimeSigned()));
+}
+
+PyObject*
+TSIG_getFudge(const s_TSIG* const self) {
+ return (Py_BuildValue("H", self->cppobj->getFudge()));
+}
+
+PyObject*
+TSIG_getOriginalID(const s_TSIG* const self) {
+ return (Py_BuildValue("H", self->cppobj->getOriginalID()));
+}
+
+PyObject*
+TSIG_getError(const s_TSIG* const self) {
+ return (Py_BuildValue("H", self->cppobj->getError()));
+}
+
+PyObject*
+TSIG_getMAC(const s_TSIG* const self) {
+ return (Py_BuildValue("y#", self->cppobj->getMAC(),
+ self->cppobj->getMACSize()));
+}
+
+PyObject*
+TSIG_getOtherData(const s_TSIG* const self) {
+ return (Py_BuildValue("y#", self->cppobj->getOtherData(),
+ self->cppobj->getOtherLen()));
+}
+
+PyObject*
+TSIG_toText(const s_TSIG* const self) {
+ try {
+ // toText() could throw, so we need to catch any exceptions below.
+ return (Py_BuildValue("s", self->cppobj->toText().c_str()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to convert TSIG object to text: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "converting TSIG object to text");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIG_str(PyObject* self) {
+ // Simply call the to_text method we already defined
+ return (PyObject_CallMethod(self, const_cast<char*>("to_text"),
+ const_cast<char*>("")));
+}
+
+PyObject*
+TSIG_toWire(const s_TSIG* const self, PyObject* args) {
+ typedef any::TSIG TSIGRdata;
+ return (toWireWrapper<s_TSIG, TSIGRdata, ToWireCallVoid<const TSIGRdata> >(
+ self, args));
+}
+
+PyObject*
+TSIG_richcmp(const s_TSIG* const self,
+ const s_TSIG* const other,
+ const int op)
+{
+ bool c = false;
+
+ // Check for null and if the types match. If different type,
+ // simply return False
+ if (other == NULL || (self->ob_type != other->ob_type)) {
+ Py_RETURN_FALSE;
+ }
+
+ // Only equals and not equals here, unorderable type
+ const int cmp = self->cppobj->compare(*other->cppobj);
+ switch (op) {
+ case Py_EQ:
+ c = (cmp == 0);
+ break;
+ case Py_NE:
+ c = (cmp != 0);
+ break;
+ case Py_GT:
+ c = (cmp > 0);
+ break;
+ case Py_GE:
+ c = (cmp >= 0);
+ break;
+ case Py_LT:
+ c = (cmp < 0);
+ break;
+ case Py_LE:
+ c = (cmp <= 0);
+ break;
+ default:
+ PyErr_SetString(PyExc_IndexError,
+ "Unhandled rich comparison operator for TSIG");
+ return (NULL);
+ }
+ if (c) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_TSIG
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject tsig_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp.TSIG",
+ sizeof(s_TSIG), // tp_basicsize
+ 0, // tp_itemsize
+ reinterpret_cast<destructor>(TSIG_destroy), // tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ TSIG_str, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "The TSIG class objects represents the TSIG RDATA as defined in RFC2845.",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ reinterpret_cast<richcmpfunc>(TSIG_richcmp), // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ TSIG_methods, // tp_methods
+ NULL, // tp_members
+ NULL, // tp_getset
+ // At the moment, we leave tp_base NULL as we won't use this class
+ // in a polymorphic way for our immediate need.
+ NULL, // tp_base
+ NULL, // tp_dict
+ NULL, // tp_descr_get
+ NULL, // tp_descr_set
+ 0, // tp_dictoffset
+ reinterpret_cast<initproc>(TSIG_init), // tp_init
+ NULL, // tp_alloc
+ PyType_GenericNew, // tp_new
+ NULL, // tp_free
+ NULL, // tp_is_gc
+ NULL, // tp_bases
+ NULL, // tp_mro
+ NULL, // tp_cache
+ NULL, // tp_subclasses
+ NULL, // tp_weaklist
+ NULL, // tp_del
+ 0 // tp_version_tag
+};
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_TSIG(PyObject* mod) {
+ // We initialize the static description object with PyType_Ready(),
+ // then add it to the module. This is not just a check! (leaving
+ // this out results in segmentation faults)
+ if (PyType_Ready(&tsig_type) < 0) {
+ return (false);
+ }
+ void* p = &tsig_type;
+ if (PyModule_AddObject(mod, "TSIG", static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+ Py_INCREF(&tsig_type);
+
+ return (true);
+}
+
+PyObject*
+createTSIGObject(const any::TSIG& source) {
+ TSIGContainer container = PyObject_New(s_TSIG, &tsig_type);
+ container.set(new any::TSIG(source));
+ return (container.release());
+}
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/tsig_rdata_python.h b/src/lib/dns/python/tsig_rdata_python.h
new file mode 100644
index 0000000..e5e0c6c
--- /dev/null
+++ b/src/lib/dns/python/tsig_rdata_python.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_TSIG_H
+#define __PYTHON_TSIG_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+namespace rdata {
+namespace any {
+class TSIG;
+}
+}
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_TSIG : public PyObject {
+public:
+ s_TSIG();
+ const rdata::any::TSIG* cppobj;
+};
+
+extern PyTypeObject tsig_type;
+
+bool initModulePart_TSIG(PyObject* mod);
+
+/// This is A simple shortcut to create a python TSIG object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called with in a try block
+/// followed by necessary setup for python exception.
+PyObject* createTSIGObject(const rdata::any::TSIG& source);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_TSIG_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/tsigerror_python.cc b/src/lib/dns/python/tsigerror_python.cc
new file mode 100644
index 0000000..0ad4716
--- /dev/null
+++ b/src/lib/dns/python/tsigerror_python.cc
@@ -0,0 +1,370 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <Python.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <dns/tsigerror.h>
+
+#include "pydnspp_common.h"
+#include "rcode_python.h"
+#include "tsigerror_python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::dns;
+using namespace isc::dns::python;
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+//
+// TSIGError
+//
+
+// Trivial constructor.
+s_TSIGError::s_TSIGError() : cppobj(NULL) {
+}
+
+// Import pydoc text
+#include "tsigerror_python_inc.cc"
+
+namespace {
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_TSIGError, TSIGError> TSIGErrorContainer;
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+
+// General creation and destruction
+int TSIGError_init(s_TSIGError* self, PyObject* args);
+void TSIGError_destroy(s_TSIGError* self);
+
+// These are the functions we export
+PyObject* TSIGError_getCode(const s_TSIGError* const self);
+PyObject* TSIGError_toText(const s_TSIGError* const self);
+PyObject* TSIGError_toRcode(const s_TSIGError* const self);
+PyObject* TSIGError_str(PyObject* self);
+PyObject* TSIGError_richcmp(const s_TSIGError* const self,
+ const s_TSIGError* const other, int op);
+
+// These are the functions we export
+// For a minimal support, we don't need them.
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef TSIGError_methods[] = {
+ { "get_code", reinterpret_cast<PyCFunction>(TSIGError_getCode),
+ METH_NOARGS,
+ TSIGError_getCode_doc },
+ { "to_text", reinterpret_cast<PyCFunction>(TSIGError_toText), METH_NOARGS,
+ TSIGError_toText_doc },
+ { "to_rcode", reinterpret_cast<PyCFunction>(TSIGError_toRcode),
+ METH_NOARGS,
+ TSIGError_toRcode_doc },
+ { NULL, NULL, 0, NULL }
+};
+
+int
+TSIGError_init(s_TSIGError* self, PyObject* args) {
+ try {
+ // Constructor from the code value
+ long code = 0;
+ if (PyArg_ParseTuple(args, "l", &code)) {
+ if (code < 0 || code > 0xffff) {
+ PyErr_SetString(PyExc_ValueError, "TSIG error out of range");
+ return (-1);
+ }
+ self->cppobj = new TSIGError(code);
+ return (0);
+ }
+
+ // Constructor from Rcode
+ PyErr_Clear();
+ s_Rcode* py_rcode;
+ if (PyArg_ParseTuple(args, "O!", &rcode_type, &py_rcode)) {
+ self->cppobj = new TSIGError(*py_rcode->cppobj);
+ return (0);
+ }
+ } catch (const isc::OutOfRange& ex) {
+ const string ex_what = "Failed to construct TSIGError object: " +
+ string(ex.what());
+ PyErr_SetString(PyExc_ValueError, ex_what.c_str());
+ return (-1);
+ } catch (const exception& ex) {
+ const string ex_what = "Failed to construct TSIGError object: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (-1);
+ } catch (...) {
+ PyErr_SetString(po_IscException,
+ "Unexpected exception in constructing TSIGError");
+ return (-1);
+ }
+
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to TSIGError constructor");
+
+ return (-1);
+}
+
+void
+TSIGError_destroy(s_TSIGError* const self) {
+ delete self->cppobj;
+ self->cppobj = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+TSIGError_getCode(const s_TSIGError* const self) {
+ return (Py_BuildValue("I", self->cppobj->getCode()));
+}
+
+PyObject*
+TSIGError_toText(const s_TSIGError* const self) {
+ try {
+ // toText() could throw, so we need to catch any exceptions below.
+ return (Py_BuildValue("s", self->cppobj->toText().c_str()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to convert TSIGError object to text: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "converting TSIGError object to text");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIGError_str(PyObject* self) {
+ // Simply call the to_text method we already defined
+ return (PyObject_CallMethod(self, const_cast<char*>("to_text"),
+ const_cast<char*>("")));
+}
+
+PyObject*
+TSIGError_toRcode(const s_TSIGError* const self) {
+ typedef CPPPyObjectContainer<s_Rcode, Rcode> RcodePyObjectContainer;
+
+ try {
+ RcodePyObjectContainer rcode_container(PyObject_New(s_Rcode,
+ &rcode_type));
+ rcode_container.set(new Rcode(self->cppobj->toRcode()));
+ return (rcode_container.release());
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to convert TSIGError to Rcode: " + string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "converting TSIGError to Rcode");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIGError_richcmp(const s_TSIGError* const self,
+ const s_TSIGError* const other,
+ const int op)
+{
+ bool c = false;
+
+ // Check for null and if the types match. If different type,
+ // simply return False
+ if (other == NULL || (self->ob_type != other->ob_type)) {
+ Py_RETURN_FALSE;
+ }
+
+ // Only equals and not equals here, unorderable type
+ switch (op) {
+ case Py_LT:
+ PyErr_SetString(PyExc_TypeError, "Unorderable type; TSIGError");
+ return (NULL);
+ case Py_LE:
+ PyErr_SetString(PyExc_TypeError, "Unorderable type; TSIGError");
+ return (NULL);
+ case Py_EQ:
+ c = (*self->cppobj == *other->cppobj);
+ break;
+ case Py_NE:
+ c = (*self->cppobj != *other->cppobj);
+ break;
+ case Py_GT:
+ PyErr_SetString(PyExc_TypeError, "Unorderable type; TSIGError");
+ return (NULL);
+ case Py_GE:
+ PyErr_SetString(PyExc_TypeError, "Unorderable type; TSIGError");
+ return (NULL);
+ }
+ if (c) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_TSIGError
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject tsigerror_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp.TSIGError",
+ sizeof(s_TSIGError), // tp_basicsize
+ 0, // tp_itemsize
+ reinterpret_cast<destructor>(TSIGError_destroy), // tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ // THIS MAY HAVE TO BE CHANGED TO NULL:
+ TSIGError_str, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ TSIGError_doc,
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ // THIS MAY HAVE TO BE CHANGED TO NULL:
+ reinterpret_cast<richcmpfunc>(TSIGError_richcmp), // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ TSIGError_methods, // tp_methods
+ NULL, // tp_members
+ NULL, // tp_getset
+ NULL, // tp_base
+ NULL, // tp_dict
+ NULL, // tp_descr_get
+ NULL, // tp_descr_set
+ 0, // tp_dictoffset
+ reinterpret_cast<initproc>(TSIGError_init), // tp_init
+ NULL, // tp_alloc
+ PyType_GenericNew, // tp_new
+ NULL, // tp_free
+ NULL, // tp_is_gc
+ NULL, // tp_bases
+ NULL, // tp_mro
+ NULL, // tp_cache
+ NULL, // tp_subclasses
+ NULL, // tp_weaklist
+ NULL, // tp_del
+ 0 // tp_version_tag
+};
+
+namespace {
+// Trivial shortcut to create and install TSIGError constants.
+inline void
+installTSIGErrorConstant(const char* name, const TSIGError& val) {
+ TSIGErrorContainer container(PyObject_New(s_TSIGError, &tsigerror_type));
+ container.installAsClassVariable(tsigerror_type, name, new TSIGError(val));
+}
+}
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_TSIGError(PyObject* mod) {
+ // We initialize the static description object with PyType_Ready(),
+ // then add it to the module. This is not just a check! (leaving
+ // this out results in segmentation faults)
+ if (PyType_Ready(&tsigerror_type) < 0) {
+ return (false);
+ }
+ void* p = &tsigerror_type;
+ if (PyModule_AddObject(mod, "TSIGError", static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+ Py_INCREF(&tsigerror_type);
+
+ try {
+ // Constant class variables
+ // Error codes (bare values)
+ installClassVariable(tsigerror_type, "BAD_SIG_CODE",
+ Py_BuildValue("H", TSIGError::BAD_SIG_CODE));
+ installClassVariable(tsigerror_type, "BAD_KEY_CODE",
+ Py_BuildValue("H", TSIGError::BAD_KEY_CODE));
+ installClassVariable(tsigerror_type, "BAD_TIME_CODE",
+ Py_BuildValue("H", TSIGError::BAD_TIME_CODE));
+
+ // Error codes (constant objects)
+ installTSIGErrorConstant("NOERROR", TSIGError::NOERROR());
+ installTSIGErrorConstant("FORMERR", TSIGError::FORMERR());
+ installTSIGErrorConstant("SERVFAIL", TSIGError::SERVFAIL());
+ installTSIGErrorConstant("NXDOMAIN", TSIGError::NXDOMAIN());
+ installTSIGErrorConstant("NOTIMP", TSIGError::NOTIMP());
+ installTSIGErrorConstant("REFUSED", TSIGError::REFUSED());
+ installTSIGErrorConstant("YXDOMAIN", TSIGError::YXDOMAIN());
+ installTSIGErrorConstant("YXRRSET", TSIGError::YXRRSET());
+ installTSIGErrorConstant("NXRRSET", TSIGError::NXRRSET());
+ installTSIGErrorConstant("NOTAUTH", TSIGError::NOTAUTH());
+ installTSIGErrorConstant("NOTZONE", TSIGError::NOTZONE());
+ installTSIGErrorConstant("RESERVED11", TSIGError::RESERVED11());
+ installTSIGErrorConstant("RESERVED12", TSIGError::RESERVED12());
+ installTSIGErrorConstant("RESERVED13", TSIGError::RESERVED13());
+ installTSIGErrorConstant("RESERVED14", TSIGError::RESERVED14());
+ installTSIGErrorConstant("RESERVED15", TSIGError::RESERVED15());
+ installTSIGErrorConstant("BAD_SIG", TSIGError::BAD_SIG());
+ installTSIGErrorConstant("BAD_KEY", TSIGError::BAD_KEY());
+ installTSIGErrorConstant("BAD_TIME", TSIGError::BAD_TIME());
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpected failure in TSIGError initialization: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in TSIGError initialization");
+ return (false);
+ }
+
+ return (true);
+}
+
+PyObject*
+createTSIGErrorObject(const TSIGError& source) {
+ TSIGErrorContainer container = PyObject_New(s_TSIGError, &tsigerror_type);
+ container.set(new TSIGError(source));
+ return (container.release());
+}
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/tsigerror_python.h b/src/lib/dns/python/tsigerror_python.h
new file mode 100644
index 0000000..735a480
--- /dev/null
+++ b/src/lib/dns/python/tsigerror_python.h
@@ -0,0 +1,52 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_TSIGERROR_H
+#define __PYTHON_TSIGERROR_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class TSIGError;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_TSIGError : public PyObject {
+public:
+ s_TSIGError();
+ const TSIGError* cppobj;
+};
+
+extern PyTypeObject tsigerror_type;
+
+bool initModulePart_TSIGError(PyObject* mod);
+
+/// This is A simple shortcut to create a python TSIGError object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called with in a try block
+/// followed by necessary setup for python exception.
+PyObject* createTSIGErrorObject(const TSIGError& source);
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_TSIGERROR_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/tsigerror_python_inc.cc b/src/lib/dns/python/tsigerror_python_inc.cc
new file mode 100644
index 0000000..ed3b605
--- /dev/null
+++ b/src/lib/dns/python/tsigerror_python_inc.cc
@@ -0,0 +1,83 @@
+namespace {
+const char* const TSIGError_doc = "\n\
+TSIG errors.\n\
+\n\
+\n\
+The TSIGError class objects represent standard errors related to TSIG\n\
+protocol operations as defined in related specifications, mainly in\n\
+RFC2845.\n\
+\n\
+TSIGError(error_code)\n\
+\n\
+Constructor from the code value.\n\
+\n\
+Exceptions:\n\
+ None: \n\
+\n\
+Parameters:\n\
+ error_code: The underlying 16-bit error code value of the TSIGError.\n\
+\n\
+TSIGError(rcode)\n\
+\n\
+Constructor from Rcode.\n\
+\n\
+As defined in RFC2845, error code values from 0 to 15 (inclusive) are\n\
+derived from the DNS RCODEs, which are represented via the Rcode class\n\
+in this library. This constructor works as a converter from these\n\
+RCODEs to corresponding TSIGError objects.\n\
+\n\
+Exceptions:\n\
+ ValueError: Given rcode is not convertible to TSIGErrors.\n\
+\n\
+Parameters:\n\
+ rcode: the Rcode from which the TSIGError should be derived.\n\
+\n\
+";
+const char* const TSIGError_getCode_doc = "get_code() -> integer\n\
+\n\
+Returns the TSIGCode error code value.\n\
+\n\
+Exceptions:\n\
+ None: \n\
+\n\
+Return Value(s):\n\
+ The underlying code value corresponding to the TSIGError.\n\
+";
+const char* const TSIGError_toText_doc = "to_text() -> string\n\
+\n\
+Convert the TSIGError to a string.\n\
+\n\
+For codes derived from RCODEs up to 15, this method returns the same\n\
+string as Rcode.to_text() for the corresponding code. For other pre-\n\
+defined code values (see TSIGError.CodeValue), this method returns a\n\
+string representation of the \"mnemonic' used for the enum and\n\
+constant objects as defined in RFC2845. For example, the string for\n\
+code value 16 is \"BADSIG\", etc. For other code values it returns a\n\
+string representation of the decimal number of the value, e.g. \"32\",\n\
+\"100\", etc.\n\
+\n\
+Exceptions:\n\
+ None\n\
+\n\
+Return Value(s):\n\
+ A string representation of the TSIGError.\n\
+";
+const char* const TSIGError_toRcode_doc = "to_rcode() -> Rcode\n\
+\n\
+Convert the TSIGError to a Rcode.\n\
+\n\
+This method returns an Rcode object that is corresponding to the TSIG\n\
+error. The returned Rcode is expected to be used by a verifying server\n\
+to specify the RCODE of a response when TSIG verification fails.\n\
+\n\
+Specifically, this method returns Rcode.NOTAUTH() for the TSIG\n\
+specific errors, BADSIG, BADKEY, BADTIME, as described in RFC2845. For\n\
+errors derived from the standard Rcode (code 0-15), it returns the\n\
+corresponding Rcode. For others, this method returns Rcode.SERVFAIL()\n\
+as a last resort.\n\
+\n\
+Exceptions:\n\
+ None: \n\
+\n\
+";
+}
diff --git a/src/lib/dns/python/tsigkey_python.cc b/src/lib/dns/python/tsigkey_python.cc
index aa87909..f0906cb 100644
--- a/src/lib/dns/python/tsigkey_python.cc
+++ b/src/lib/dns/python/tsigkey_python.cc
@@ -12,12 +12,24 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <new>
+#include <Python.h>
+#include <stdexcept>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <dns/name.h>
#include <dns/tsigkey.h>
+#include <dns/rdata.h>
+#include "pydnspp_common.h"
+#include "name_python.h"
+#include "tsigkey_python.h"
+
+using namespace std;
+using namespace isc::util::python;
using namespace isc::dns;
-using namespace isc::dns::rdata;
+using namespace isc::dns::python;
//
// Definition of the classes
@@ -27,19 +39,15 @@ using namespace isc::dns::rdata;
// and static wrappers around the methods we export), a list of methods,
// and a type description
-namespace {
//
// TSIGKey
//
// The s_* Class simply covers one instantiation of the object
-class s_TSIGKey : public PyObject {
-public:
- s_TSIGKey() : tsigkey(NULL) {}
- TSIGKey* tsigkey;
-};
+s_TSIGKey::s_TSIGKey() : cppobj(NULL) {}
+namespace {
//
// We declare the functions here, the definitions are below
// the type definition of the object, since both can use the other
@@ -55,6 +63,7 @@ void TSIGKey_destroy(s_TSIGKey* self);
PyObject* TSIGKey_getKeyName(const s_TSIGKey* self);
PyObject* TSIGKey_getAlgorithmName(const s_TSIGKey* self);
PyObject* TSIGKey_getSecret(const s_TSIGKey* self);
+PyObject* TSIGKey_toText(const s_TSIGKey* self);
// This list contains the actual set of functions we have in
// python. Each entry has
@@ -72,15 +81,110 @@ PyMethodDef TSIGKey_methods[] = {
{ "get_secret",
reinterpret_cast<PyCFunction>(TSIGKey_getSecret), METH_NOARGS,
"Return the value of the TSIG secret." },
+ { "to_text", reinterpret_cast<PyCFunction>(TSIGKey_toText), METH_NOARGS,
+ "Returns the string representation (name:secret:algorithm)" },
{ NULL, NULL, 0, NULL }
};
+int
+TSIGKey_init(s_TSIGKey* self, PyObject* args) {
+ try {
+ const char* str;
+ if (PyArg_ParseTuple(args, "s", &str)) {
+ self->cppobj = new TSIGKey(str);
+ return (0);
+ }
+
+ PyErr_Clear();
+ const s_Name* key_name;
+ const s_Name* algorithm_name;
+ PyObject* bytes_obj;
+ const char* secret;
+ Py_ssize_t secret_len;
+ if (PyArg_ParseTuple(args, "O!O!O", &name_type, &key_name,
+ &name_type, &algorithm_name, &bytes_obj) &&
+ PyObject_AsCharBuffer(bytes_obj, &secret, &secret_len) == 0) {
+ if (secret_len == 0) {
+ secret = NULL;
+ }
+ self->cppobj = new TSIGKey(*key_name->cppobj,
+ *algorithm_name->cppobj,
+ secret, secret_len);
+ return (0);
+ }
+ } catch (const isc::InvalidParameter& ex) {
+ PyErr_SetString(po_InvalidParameter, ex.what());
+ return (-1);
+ } catch (...) {
+ PyErr_SetString(po_IscException, "Unexpected exception");
+ return (-1);
+ }
+
+ PyErr_Clear();
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to TSIGKey constructor");
+
+ return (-1);
+}
+
+void
+TSIGKey_destroy(s_TSIGKey* const self) {
+ delete self->cppobj;
+ self->cppobj = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+TSIGKey_getKeyName(const s_TSIGKey* const self) {
+ try {
+ return (createNameObject(self->cppobj->getKeyName()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to get key name of TSIGKey: " + string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "getting key name of TSIGKey");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIGKey_getAlgorithmName(const s_TSIGKey* const self) {
+ try {
+ return (createNameObject(self->cppobj->getAlgorithmName()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to get algorithm name of TSIGKey: " + string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "getting algorithm name of TSIGKey");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIGKey_getSecret(const s_TSIGKey* const self) {
+ return (Py_BuildValue("y#", self->cppobj->getSecret(),
+ self->cppobj->getSecretLength()));
+}
+
+PyObject*
+TSIGKey_toText(const s_TSIGKey* self) {
+ return (Py_BuildValue("s", self->cppobj->toText().c_str()));
+}
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
// This defines the complete type for reflection in python and
// parsing of PyObject* to s_EDNS
// Most of the functions are not actually implemented and NULL here.
PyTypeObject tsigkey_type = {
PyVarObject_HEAD_INIT(NULL, 0)
- "libdns_python.TSIGKey",
+ "pydnspp.TSIGKey",
sizeof(s_TSIGKey), // tp_basicsize
0, // tp_itemsize
(destructor)TSIGKey_destroy, // tp_dealloc
@@ -129,78 +233,6 @@ PyTypeObject tsigkey_type = {
0 // tp_version_tag
};
-// A helper function to build a python "Name" object with error handling
-// encapsulated.
-s_Name*
-createNameObject(const Name& source) {
- s_Name* name = PyObject_New(s_Name, &name_type);
- if (name == NULL) {
- return (NULL);
- }
- name->name = new(nothrow) Name(source);
- if (name->name == NULL) {
- Py_DECREF(name);
- PyErr_SetString(po_IscException, "Allocating Name object failed");
- return (NULL);
- }
- return (name);
-}
-
-int
-TSIGKey_init(s_TSIGKey* self, PyObject* args) {
- const s_Name* key_name;
- const s_Name* algorithm_name;
- PyObject* bytes_obj;
- const char* secret;
- Py_ssize_t secret_len;
-
- if (PyArg_ParseTuple(args, "O!O!O", &name_type, &key_name,
- &name_type, &algorithm_name, &bytes_obj) &&
- PyObject_AsCharBuffer(bytes_obj, &secret, &secret_len) != -1) {
- try {
- self->tsigkey = new TSIGKey(*key_name->name,
- *algorithm_name->name,
- secret, secret_len);
- } catch (const isc::InvalidParameter& ex) {
- PyErr_SetString(po_InvalidParameter, ex.what());
- return (-1);
- } catch (...) {
- PyErr_SetString(po_IscException, "Unexpected exception");
- return (-1);
- }
- return (0);
- }
-
- PyErr_Clear();
- PyErr_SetString(PyExc_TypeError,
- "Invalid arguments to TSIGKey constructor");
-
- return (-1);
-}
-
-void
-TSIGKey_destroy(s_TSIGKey* const self) {
- delete self->tsigkey;
- self->tsigkey = NULL;
- Py_TYPE(self)->tp_free(self);
-}
-
-PyObject*
-TSIGKey_getKeyName(const s_TSIGKey* const self) {
- return (createNameObject(self->tsigkey->getKeyName()));
-}
-
-PyObject*
-TSIGKey_getAlgorithmName(const s_TSIGKey* const self) {
- return (createNameObject(self->tsigkey->getAlgorithmName()));
-}
-
-PyObject*
-TSIGKey_getSecret(const s_TSIGKey* const self) {
- return (Py_BuildValue("y#", self->tsigkey->getSecret(),
- self->tsigkey->getSecretLength()));
-}
-
// Module Initialization, all statics are initialized here
bool
initModulePart_TSIGKey(PyObject* mod) {
@@ -210,33 +242,43 @@ initModulePart_TSIGKey(PyObject* mod) {
if (PyType_Ready(&tsigkey_type) < 0) {
return (false);
}
- Py_INCREF(&tsigkey_type);
void* p = &tsigkey_type;
if (PyModule_AddObject(mod, "TSIGKey", static_cast<PyObject*>(p)) != 0) {
- Py_DECREF(&tsigkey_type);
return (false);
}
+ Py_INCREF(&tsigkey_type);
- s_Name* name;
- if ((name = createNameObject(TSIGKey::HMACMD5_NAME())) == NULL) {
- goto cleanup;
- }
- addClassVariable(tsigkey_type, "HMACMD5_NAME", name);
- if ((name = createNameObject(TSIGKey::HMACSHA1_NAME())) == NULL) {
- goto cleanup;
- }
- addClassVariable(tsigkey_type, "HMACSHA1_NAME", name);
- if ((name = createNameObject(TSIGKey::HMACSHA256_NAME())) == NULL) {
- goto cleanup;
+ try {
+ // Constant class variables
+ installClassVariable(tsigkey_type, "HMACMD5_NAME",
+ createNameObject(TSIGKey::HMACMD5_NAME()));
+ installClassVariable(tsigkey_type, "HMACSHA1_NAME",
+ createNameObject(TSIGKey::HMACSHA1_NAME()));
+ installClassVariable(tsigkey_type, "HMACSHA256_NAME",
+ createNameObject(TSIGKey::HMACSHA256_NAME()));
+ installClassVariable(tsigkey_type, "HMACSHA224_NAME",
+ createNameObject(TSIGKey::HMACSHA224_NAME()));
+ installClassVariable(tsigkey_type, "HMACSHA384_NAME",
+ createNameObject(TSIGKey::HMACSHA384_NAME()));
+ installClassVariable(tsigkey_type, "HMACSHA512_NAME",
+ createNameObject(TSIGKey::HMACSHA512_NAME()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpected failure in TSIGKey initialization: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in TSIGKey initialization");
+ return (false);
}
- addClassVariable(tsigkey_type, "HMACSHA256_NAME", name);
return (true);
-
- cleanup:
- Py_DECREF(&tsigkey_type);
- return (false);
}
+} // namespace python
+} // namespace dns
+} // namespace isc
//
// End of TSIGKey
//
@@ -249,12 +291,9 @@ initModulePart_TSIGKey(PyObject* mod) {
// The s_* Class simply covers one instantiation of the object
-class s_TSIGKeyRing : public PyObject {
-public:
- s_TSIGKeyRing() : keyring(NULL) {}
- TSIGKeyRing* keyring;
-};
+s_TSIGKeyRing::s_TSIGKeyRing() : cppobj(NULL) {}
+namespace {
//
// We declare the functions here, the definitions are below
// the type definition of the object, since both can use the other
@@ -282,56 +321,6 @@ PyMethodDef TSIGKeyRing_methods[] = {
{ NULL, NULL, 0, NULL }
};
-PyTypeObject tsigkeyring_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "libdns_python.TSIGKeyRing",
- sizeof(s_TSIGKeyRing), // tp_basicsize
- 0, // tp_itemsize
- (destructor)TSIGKeyRing_destroy, // tp_dealloc
- NULL, // tp_print
- NULL, // tp_getattr
- NULL, // tp_setattr
- NULL, // tp_reserved
- NULL, // tp_repr
- NULL, // tp_as_number
- NULL, // tp_as_sequence
- NULL, // tp_as_mapping
- NULL, // tp_hash
- NULL, // tp_call
- NULL, // tp_str
- NULL, // tp_getattro
- NULL, // tp_setattro
- NULL, // tp_as_buffer
- Py_TPFLAGS_DEFAULT, // tp_flags
- "A simple repository of a set of TSIGKey objects.",
- NULL, // tp_traverse
- NULL, // tp_clear
- NULL, // tp_richcompare
- 0, // tp_weaklistoffset
- NULL, // tp_iter
- NULL, // tp_iternext
- TSIGKeyRing_methods, // tp_methods
- NULL, // tp_members
- NULL, // tp_getset
- NULL, // tp_base
- NULL, // tp_dict
- NULL, // tp_descr_get
- NULL, // tp_descr_set
- 0, // tp_dictoffset
- (initproc)TSIGKeyRing_init, // tp_init
- NULL, // tp_alloc
- PyType_GenericNew, // tp_new
- NULL, // tp_free
- NULL, // tp_is_gc
- NULL, // tp_bases
- NULL, // tp_mro
- NULL, // tp_cache
- NULL, // tp_subclasses
- NULL, // tp_weaklist
- NULL, // tp_del
- 0 // tp_version_tag
-};
-
int
TSIGKeyRing_init(s_TSIGKeyRing* self, PyObject* args) {
if (!PyArg_ParseTuple(args, "")) {
@@ -341,8 +330,8 @@ TSIGKeyRing_init(s_TSIGKeyRing* self, PyObject* args) {
return (-1);
}
- self->keyring = new(nothrow) TSIGKeyRing();
- if (self->keyring == NULL) {
+ self->cppobj = new(nothrow) TSIGKeyRing();
+ if (self->cppobj == NULL) {
PyErr_SetString(po_IscException, "Allocating TSIGKeyRing failed");
return (-1);
}
@@ -352,14 +341,14 @@ TSIGKeyRing_init(s_TSIGKeyRing* self, PyObject* args) {
void
TSIGKeyRing_destroy(s_TSIGKeyRing* self) {
- delete self->keyring;
- self->keyring = NULL;
+ delete self->cppobj;
+ self->cppobj = NULL;
Py_TYPE(self)->tp_free(self);
}
PyObject*
TSIGKeyRing_size(const s_TSIGKeyRing* const self) {
- return (Py_BuildValue("I", self->keyring->size()));
+ return (Py_BuildValue("I", self->cppobj->size()));
}
PyObject*
@@ -369,7 +358,7 @@ TSIGKeyRing_add(const s_TSIGKeyRing* const self, PyObject* args) {
if (PyArg_ParseTuple(args, "O!", &tsigkey_type, &tsigkey)) {
try {
const TSIGKeyRing::Result result =
- self->keyring->add(*tsigkey->tsigkey);
+ self->cppobj->add(*tsigkey->cppobj);
return (Py_BuildValue("I", result));
} catch (...) {
PyErr_SetString(po_IscException, "Unexpected exception");
@@ -389,7 +378,7 @@ TSIGKeyRing_remove(const s_TSIGKeyRing* self, PyObject* args) {
if (PyArg_ParseTuple(args, "O!", &name_type, &key_name)) {
const TSIGKeyRing::Result result =
- self->keyring->remove(*key_name->name);
+ self->cppobj->remove(*key_name->cppobj);
return (Py_BuildValue("I", result));
}
@@ -402,17 +391,19 @@ TSIGKeyRing_remove(const s_TSIGKeyRing* self, PyObject* args) {
PyObject*
TSIGKeyRing_find(const s_TSIGKeyRing* self, PyObject* args) {
s_Name* key_name;
+ s_Name* algorithm_name;
- if (PyArg_ParseTuple(args, "O!", &name_type, &key_name)) {
+ if (PyArg_ParseTuple(args, "O!O!", &name_type, &key_name,
+ &name_type, &algorithm_name)) {
const TSIGKeyRing::FindResult result =
- self->keyring->find(*key_name->name);
+ self->cppobj->find(*key_name->cppobj, *algorithm_name->cppobj);
if (result.key != NULL) {
s_TSIGKey* key = PyObject_New(s_TSIGKey, &tsigkey_type);
if (key == NULL) {
return (NULL);
}
- key->tsigkey = new(nothrow) TSIGKey(*result.key);
- if (key->tsigkey == NULL) {
+ key->cppobj = new(nothrow) TSIGKey(*result.key);
+ if (key->cppobj == NULL) {
Py_DECREF(key);
PyErr_SetString(po_IscException,
"Allocating TSIGKey object failed");
@@ -426,6 +417,60 @@ TSIGKeyRing_find(const s_TSIGKeyRing* self, PyObject* args) {
return (NULL);
}
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+PyTypeObject tsigkeyring_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp.TSIGKeyRing",
+ sizeof(s_TSIGKeyRing), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)TSIGKeyRing_destroy, // tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ NULL, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A simple repository of a set of TSIGKey objects.",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ TSIGKeyRing_methods, // tp_methods
+ NULL, // tp_members
+ NULL, // tp_getset
+ NULL, // tp_base
+ NULL, // tp_dict
+ NULL, // tp_descr_get
+ NULL, // tp_descr_set
+ 0, // tp_dictoffset
+ (initproc)TSIGKeyRing_init, // tp_init
+ NULL, // tp_alloc
+ PyType_GenericNew, // tp_new
+ NULL, // tp_free
+ NULL, // tp_is_gc
+ NULL, // tp_bases
+ NULL, // tp_mro
+ NULL, // tp_cache
+ NULL, // tp_subclasses
+ NULL, // tp_weaklist
+ NULL, // tp_del
+ 0 // tp_version_tag
+};
bool
initModulePart_TSIGKeyRing(PyObject* mod) {
@@ -449,5 +494,6 @@ initModulePart_TSIGKeyRing(PyObject* mod) {
return (true);
}
-
-} // end of unnamed namespace
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/tsigkey_python.h b/src/lib/dns/python/tsigkey_python.h
new file mode 100644
index 0000000..51b3ae7
--- /dev/null
+++ b/src/lib/dns/python/tsigkey_python.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_TSIGKEY_H
+#define __PYTHON_TSIGKEY_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class TSIGKey;
+class TSIGKeyRing;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_TSIGKey : public PyObject {
+public:
+ s_TSIGKey();
+ TSIGKey* cppobj;
+};
+
+class s_TSIGKeyRing : public PyObject {
+public:
+ s_TSIGKeyRing();
+ TSIGKeyRing* cppobj;
+};
+
+extern PyTypeObject tsigkey_type;
+extern PyTypeObject tsigkeyring_type;
+
+bool initModulePart_TSIGKey(PyObject* mod);
+bool initModulePart_TSIGKeyRing(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_TSIGKEY_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/tsigrecord_python.cc b/src/lib/dns/python/tsigrecord_python.cc
new file mode 100644
index 0000000..8a78b5e
--- /dev/null
+++ b/src/lib/dns/python/tsigrecord_python.cc
@@ -0,0 +1,311 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <Python.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <dns/tsigrecord.h>
+
+#include "pydnspp_common.h"
+#include "pydnspp_towire.h"
+#include "name_python.h"
+#include "tsig_rdata_python.h"
+#include "tsigrecord_python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::dns;
+using namespace isc::dns::python;
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+//
+// TSIGRecord
+//
+
+// Trivial constructor.
+s_TSIGRecord::s_TSIGRecord() : cppobj(NULL) {
+}
+
+namespace {
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_TSIGRecord, TSIGRecord> TSIGRecordContainer;
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+
+// General creation and destruction
+int TSIGRecord_init(s_TSIGRecord* self, PyObject* args);
+void TSIGRecord_destroy(s_TSIGRecord* self);
+PyObject* TSIGRecord_toText(const s_TSIGRecord* const self);
+PyObject* TSIGRecord_str(PyObject* self);
+PyObject* TSIGRecord_toWire(const s_TSIGRecord* self, PyObject* args);
+PyObject* TSIGRecord_getName(const s_TSIGRecord* self);
+PyObject* TSIGRecord_getLength(const s_TSIGRecord* self);
+PyObject* TSIGRecord_getRdata(const s_TSIGRecord* self);
+
+// These are the functions we export
+// For a minimal support, we don't need them.
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef TSIGRecord_methods[] = {
+ { "get_name", reinterpret_cast<PyCFunction>(TSIGRecord_getName),
+ METH_NOARGS,
+ "Return the owner name of the TSIG RR, which is the TSIG key name" },
+ { "get_length", reinterpret_cast<PyCFunction>(TSIGRecord_getLength),
+ METH_NOARGS,
+ "Return the length of the TSIG record" },
+ { "get_rdata", reinterpret_cast<PyCFunction>(TSIGRecord_getRdata),
+ METH_NOARGS,
+ "Return the RDATA of the TSIG RR" },
+ { "to_text", reinterpret_cast<PyCFunction>(TSIGRecord_toText), METH_NOARGS,
+ "Returns the text representation" },
+ { "to_wire", reinterpret_cast<PyCFunction>(TSIGRecord_toWire),
+ METH_VARARGS,
+ "Converts the TSIGRecord object to wire format.\n"
+ "The argument can be either a MessageRenderer or an object that "
+ "implements the sequence interface. If the object is mutable "
+ "(for instance a bytearray()), the wire data is added in-place.\n"
+ "If it is not (for instance a bytes() object), a new object is "
+ "returned" },
+ { NULL, NULL, 0, NULL }
+};
+
+int
+TSIGRecord_init(s_TSIGRecord* self, PyObject* args) {
+ try {
+ const s_Name* py_name;
+ const s_TSIG* py_tsig;
+ if (PyArg_ParseTuple(args, "O!O!", &name_type, &py_name,
+ &tsig_type, &py_tsig)) {
+ self->cppobj = new TSIGRecord(*py_name->cppobj, *py_tsig->cppobj);
+ return (0);
+ }
+ } catch (const exception& ex) {
+ const string ex_what = "Failed to construct TSIGRecord object: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (-1);
+ } catch (...) {
+ PyErr_SetString(po_IscException,
+ "Unexpected exception in constructing TSIGRecord");
+ return (-1);
+ }
+
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to TSIGRecord constructor");
+
+ return (-1);
+}
+
+// This is a template of typical code logic of python object destructor.
+// In many cases you can use it without modification, but check that carefully.
+void
+TSIGRecord_destroy(s_TSIGRecord* const self) {
+ delete self->cppobj;
+ self->cppobj = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+// This should be able to be used without modification as long as the
+// underlying C++ class has toText().
+PyObject*
+TSIGRecord_toText(const s_TSIGRecord* const self) {
+ try {
+ // toText() could throw, so we need to catch any exceptions below.
+ return (Py_BuildValue("s", self->cppobj->toText().c_str()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to convert TSIGRecord object to text: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "converting TSIGRecord object to text");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIGRecord_str(PyObject* self) {
+ // Simply call the to_text method we already defined
+ return (PyObject_CallMethod(self, const_cast<char*>("to_text"),
+ const_cast<char*>("")));
+}
+
+PyObject*
+TSIGRecord_toWire(const s_TSIGRecord* const self, PyObject* args) {
+ typedef ToWireCallInt<const TSIGRecord> ToWireCall;
+ PyObject* (*towire_fn)(const s_TSIGRecord* const, PyObject*) =
+ toWireWrapper<s_TSIGRecord, TSIGRecord, ToWireCall>;
+ return (towire_fn(self, args));
+}
+
+PyObject*
+TSIGRecord_getName(const s_TSIGRecord* const self) {
+ try {
+ return (createNameObject(self->cppobj->getName()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to get TSIGRecord name: " + string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "getting TSIGRecord name");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIGRecord_getLength(const s_TSIGRecord* const self) {
+ return (Py_BuildValue("H", self->cppobj->getLength()));
+}
+
+PyObject*
+TSIGRecord_getRdata(const s_TSIGRecord* const self) {
+ try {
+ return (createTSIGObject(self->cppobj->getRdata()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to get TSIGRecord RDATA: " + string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "getting TSIGRecord RDATA");
+ }
+ return (NULL);
+}
+
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_TSIGRecord
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject tsigrecord_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp.TSIGRecord",
+ sizeof(s_TSIGRecord), // tp_basicsize
+ 0, // tp_itemsize
+ reinterpret_cast<destructor>(TSIGRecord_destroy), // tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ TSIGRecord_str, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "The TSIGRecord class objects is...(COMPLETE THIS)",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ TSIGRecord_methods, // tp_methods
+ NULL, // tp_members
+ NULL, // tp_getset
+ NULL, // tp_base
+ NULL, // tp_dict
+ NULL, // tp_descr_get
+ NULL, // tp_descr_set
+ 0, // tp_dictoffset
+ reinterpret_cast<initproc>(TSIGRecord_init), // tp_init
+ NULL, // tp_alloc
+ PyType_GenericNew, // tp_new
+ NULL, // tp_free
+ NULL, // tp_is_gc
+ NULL, // tp_bases
+ NULL, // tp_mro
+ NULL, // tp_cache
+ NULL, // tp_subclasses
+ NULL, // tp_weaklist
+ NULL, // tp_del
+ 0 // tp_version_tag
+};
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_TSIGRecord(PyObject* mod) {
+ // We initialize the static description object with PyType_Ready(),
+ // then add it to the module. This is not just a check! (leaving
+ // this out results in segmentation faults)
+ if (PyType_Ready(&tsigrecord_type) < 0) {
+ return (false);
+ }
+ void* p = &tsigrecord_type;
+ if (PyModule_AddObject(mod, "TSIGRecord", static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+ Py_INCREF(&tsigrecord_type);
+
+ // The following template is the typical procedure for installing class
+ // variables. If the class doesn't have a class variable, remove the
+ // entire try-catch clauses.
+ try {
+ // Constant class variables
+ installClassVariable(tsigrecord_type, "TSIG_TTL",
+ Py_BuildValue("I", 0));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpected failure in TSIGRecord initialization: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in TSIGRecord initialization");
+ return (false);
+ }
+
+ return (true);
+}
+
+PyObject*
+createTSIGRecordObject(const TSIGRecord& source) {
+ TSIGRecordContainer container = PyObject_New(s_TSIGRecord,
+ &tsigrecord_type);
+ container.set(new TSIGRecord(source));
+ return (container.release());
+}
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/tsigrecord_python.h b/src/lib/dns/python/tsigrecord_python.h
new file mode 100644
index 0000000..e0a3526
--- /dev/null
+++ b/src/lib/dns/python/tsigrecord_python.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_TSIGRECORD_H
+#define __PYTHON_TSIGRECORD_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class TSIGRecord;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_TSIGRecord : public PyObject {
+public:
+ s_TSIGRecord();
+ TSIGRecord* cppobj;
+};
+
+extern PyTypeObject tsigrecord_type;
+
+bool initModulePart_TSIGRecord(PyObject* mod);
+
+/// This is A simple shortcut to create a python TSIGRecord object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called with in a try block
+/// followed by necessary setup for python exception.
+PyObject* createTSIGRecordObject(const TSIGRecord& source);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_TSIGRECORD_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/question.cc b/src/lib/dns/question.cc
index 3e14c78..96e2a9c 100644
--- a/src/lib/dns/question.cc
+++ b/src/lib/dns/question.cc
@@ -15,7 +15,7 @@
#include <iostream>
#include <string>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/name.h>
#include <dns/question.h>
@@ -23,6 +23,7 @@
#include <dns/rrtype.h>
using namespace std;
+using namespace isc::util;
namespace isc {
namespace dns {
@@ -55,7 +56,7 @@ Question::toWire(OutputBuffer& buffer) const {
}
unsigned int
-Question::toWire(MessageRenderer& renderer) const {
+Question::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeName(name_);
rrtype_.toWire(renderer);
rrclass_.toWire(renderer);
diff --git a/src/lib/dns/question.h b/src/lib/dns/question.h
index 520207b..b3f3d98 100644
--- a/src/lib/dns/question.h
+++ b/src/lib/dns/question.h
@@ -25,10 +25,14 @@
#include <dns/rrtype.h>
namespace isc {
+namespace util {
+class InputBuffer;
+class OutputBuffer;
+}
+
namespace dns {
-class InputBuffer;
-class MessageRenderer;
+class AbstractMessageRenderer;
class Question;
/// \brief A pointer-like type pointing to an \c Question object.
@@ -118,7 +122,7 @@ public:
/// classes fails.
///
/// \param buffer A buffer storing the wire format data.
- Question(InputBuffer& buffer);
+ Question(isc::util::InputBuffer& buffer);
/// \brief Constructor from fixed parameters of the \c Question.
///
@@ -214,17 +218,17 @@ public:
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer and name compression information.
/// \return 1
- unsigned int toWire(MessageRenderer& renderer) const;
+ unsigned int toWire(AbstractMessageRenderer& renderer) const;
/// \brief Render the Question in the wire format without name compression.
///
/// This method behaves like the render version except it doesn't compress
/// the owner name.
- /// See \c toWire(MessageRenderer& renderer)const.
+ /// See \c toWire(AbstractMessageRenderer& renderer)const.
///
/// \param buffer An output buffer to store the wire data.
/// \return 1
- unsigned int toWire(OutputBuffer& buffer) const;
+ unsigned int toWire(isc::util::OutputBuffer& buffer) const;
//@}
///
diff --git a/src/lib/dns/rdata.cc b/src/lib/dns/rdata.cc
index 17fee25..27496a2 100644
--- a/src/lib/dns/rdata.cc
+++ b/src/lib/dns/rdata.cc
@@ -27,7 +27,7 @@
#include <boost/lexical_cast.hpp>
#include <boost/shared_ptr.hpp>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
@@ -36,6 +36,7 @@
using namespace std;
using namespace boost;
+using namespace isc::util;
namespace isc {
namespace dns {
@@ -53,7 +54,7 @@ createRdata(const RRType& rrtype, const RRClass& rrclass,
RdataPtr
createRdata(const RRType& rrtype, const RRClass& rrclass,
- InputBuffer& buffer, size_t len)
+ isc::util::InputBuffer& buffer, size_t len)
{
if (len > MAX_RDLENGTH) {
isc_throw(InvalidRdataLength, "RDLENGTH too large");
@@ -66,7 +67,8 @@ createRdata(const RRType& rrtype, const RRClass& rrclass,
len);
if (buffer.getPosition() - old_pos != len) {
- isc_throw(InvalidRdataLength, "RDLENGTH mismatch");
+ isc_throw(InvalidRdataLength, "RDLENGTH mismatch: " <<
+ buffer.getPosition() - old_pos << " != " << len);
}
return (rdata);
@@ -104,7 +106,7 @@ struct GenericImpl {
vector<uint8_t> data_;
};
-Generic::Generic(InputBuffer& buffer, size_t rdata_len) {
+Generic::Generic(isc::util::InputBuffer& buffer, size_t rdata_len) {
if (rdata_len > MAX_RDLENGTH) {
isc_throw(InvalidRdataLength, "RDLENGTH too large");
}
@@ -223,12 +225,12 @@ Generic::toText() const {
}
void
-Generic::toWire(OutputBuffer& buffer) const {
+Generic::toWire(isc::util::OutputBuffer& buffer) const {
buffer.writeData(&impl_->data_[0], impl_->data_.size());
}
void
-Generic::toWire(MessageRenderer& renderer) const {
+Generic::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeData(&impl_->data_[0], impl_->data_.size());
}
diff --git a/src/lib/dns/rdata.h b/src/lib/dns/rdata.h
index 8eaedd3..afcf4b3 100644
--- a/src/lib/dns/rdata.h
+++ b/src/lib/dns/rdata.h
@@ -22,10 +22,12 @@
#include <exceptions/exceptions.h>
namespace isc {
-namespace dns {
+namespace util {
class InputBuffer;
class OutputBuffer;
-class MessageRenderer;
+}
+namespace dns {
+class AbstractMessageRenderer;
class RRType;
class RRClass;
class Name;
@@ -161,6 +163,7 @@ public:
///
/// \return A string representation of \c Rdata.
virtual std::string toText() const = 0;
+
/// \brief Render the \c Rdata in the wire format into a buffer.
///
/// This is a pure virtual method without the definition; the actual
@@ -168,7 +171,8 @@ public:
/// should be explicitly defined in the derived class.
///
/// \param buffer An output buffer to store the wire data.
- virtual void toWire(OutputBuffer& buffer) const = 0;
+ virtual void toWire(isc::util::OutputBuffer& buffer) const = 0;
+
/// \brief Render the \c Rdata in the wire format into a
/// \c MessageRenderer object.
///
@@ -178,7 +182,7 @@ public:
///
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer in which the \c Rdata is to be stored.
- virtual void toWire(MessageRenderer& renderer) const = 0;
+ virtual void toWire(AbstractMessageRenderer& renderer) const = 0;
//@}
///
@@ -251,6 +255,7 @@ public:
/// \param rdata_string A string of textual representation of generic
/// RDATA.
explicit Generic(const std::string& rdata_string);
+
///
/// \brief Constructor from wire-format data.
///
@@ -272,7 +277,8 @@ public:
/// \param buffer A reference to an \c InputBuffer object storing the
/// \c Rdata to parse.
/// \param rdata_len The length in buffer of the \c Rdata. In bytes.
- Generic(InputBuffer& buffer, size_t rdata_len);
+ Generic(isc::util::InputBuffer& buffer, size_t rdata_len);
+
///
/// \brief The destructor.
virtual ~Generic();
@@ -284,6 +290,7 @@ public:
///
/// \param source A reference to a \c generic::Generic object to copy from.
Generic(const Generic& source);
+
///
/// \brief The assignment operator.
///
@@ -293,6 +300,7 @@ public:
/// \param source A reference to a \c generic::Generic object to copy from.
Generic& operator=(const Generic& source);
//@}
+
///
/// \name Converter methods
///
@@ -307,6 +315,7 @@ public:
///
/// \return A string representation of \c generic::Generic.
virtual std::string toText() const;
+
///
/// \brief Render the \c generic::Generic in the wire format into a buffer.
///
@@ -316,7 +325,8 @@ public:
/// be thrown.
///
/// \param buffer An output buffer to store the wire data.
- virtual void toWire(OutputBuffer& buffer) const;
+ virtual void toWire(isc::util::OutputBuffer& buffer) const;
+
/// \brief Render the \c generic::Generic in the wire format into a
/// \c MessageRenderer object.
///
@@ -327,8 +337,9 @@ public:
///
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer in which the \c Generic object is to be stored.
- virtual void toWire(MessageRenderer& renderer) const;
+ virtual void toWire(AbstractMessageRenderer& renderer) const;
//@}
+
///
/// \name Comparison method
///
@@ -421,6 +432,7 @@ std::ostream& operator<<(std::ostream& os, const Generic& rdata);
/// object.
RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass,
const std::string& rdata_string);
+
/// \brief Create RDATA of a given pair of RR type and class from
/// wire-format data.
///
@@ -443,7 +455,8 @@ RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass,
/// \return An \c RdataPtr object pointing to the created \c Rdata
/// object.
RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass,
- InputBuffer& buffer, size_t len);
+ isc::util::InputBuffer& buffer, size_t len);
+
/// \brief Create RDATA of a given pair of RR type and class, copying
/// of another RDATA of same kind.
///
diff --git a/src/lib/dns/rdata/any_255/tsig_250.cc b/src/lib/dns/rdata/any_255/tsig_250.cc
index e025ce4..2557965 100644
--- a/src/lib/dns/rdata/any_255/tsig_250.cc
+++ b/src/lib/dns/rdata/any_255/tsig_250.cc
@@ -18,15 +18,18 @@
#include <boost/lexical_cast.hpp>
-#include <dns/buffer.h>
+#include <util/buffer.h>
+#include <util/encode/base64.h>
+
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
-
-#include <dns/util/base64.h>
+#include <dns/tsigerror.h>
using namespace std;
using namespace boost;
+using namespace isc::util;
+using namespace isc::util::encode;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -68,7 +71,7 @@ getToken(istringstream& iss, const string& full_input) {
string token;
iss >> token;
if (iss.bad() || iss.fail()) {
- isc_throw(InvalidRdataText, "Invalid TSIG text: parse error" <<
+ isc_throw(InvalidRdataText, "Invalid TSIG text: parse error " <<
full_input);
}
return (token);
@@ -310,15 +313,7 @@ TSIG::toText() const {
result += encodeBase64(impl_->mac_) + " ";
}
result += lexical_cast<string>(impl_->original_id_) + " ";
- if (impl_->error_ == 16) { // XXX: we'll soon introduce generic converter.
- result += "BADSIG ";
- } else if (impl_->error_ == 17) {
- result += "BADKEY ";
- } else if (impl_->error_ == 18) {
- result += "BADTIME ";
- } else {
- result += lexical_cast<string>(impl_->error_) + " ";
- }
+ result += TSIGError(impl_->error_).toText() + " ";
result += lexical_cast<string>(impl_->other_data_.size());
if (impl_->other_data_.size() > 0) {
result += " " + encodeBase64(impl_->other_data_);
@@ -377,9 +372,9 @@ TSIG::toWire(OutputBuffer& buffer) const {
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer and name compression information.
void
-TSIG::toWire(MessageRenderer& renderer) const {
+TSIG::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeName(impl_->algorithm_, false);
- impl_->toWireCommon<MessageRenderer>(renderer);
+ impl_->toWireCommon<AbstractMessageRenderer>(renderer);
}
// A helper function commonly used for TSIG::compare().
diff --git a/src/lib/dns/rdata/ch_3/a_1.cc b/src/lib/dns/rdata/ch_3/a_1.cc
index 376cbdd..65378a1 100644
--- a/src/lib/dns/rdata/ch_3/a_1.cc
+++ b/src/lib/dns/rdata/ch_3/a_1.cc
@@ -16,12 +16,13 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
using namespace std;
+using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -44,7 +45,7 @@ A::toWire(OutputBuffer&) const {
}
void
-A::toWire(MessageRenderer&) const {
+A::toWire(AbstractMessageRenderer&) const {
// TBD
}
diff --git a/src/lib/dns/rdata/generic/cname_5.cc b/src/lib/dns/rdata/generic/cname_5.cc
index c7f97d0..5bb0aea 100644
--- a/src/lib/dns/rdata/generic/cname_5.cc
+++ b/src/lib/dns/rdata/generic/cname_5.cc
@@ -16,13 +16,14 @@
#include <string>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
using namespace std;
+using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -52,7 +53,7 @@ CNAME::toWire(OutputBuffer& buffer) const {
}
void
-CNAME::toWire(MessageRenderer& renderer) const {
+CNAME::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeName(cname_);
}
diff --git a/src/lib/dns/rdata/generic/dname_39.cc b/src/lib/dns/rdata/generic/dname_39.cc
index b88720e..165c4a6 100644
--- a/src/lib/dns/rdata/generic/dname_39.cc
+++ b/src/lib/dns/rdata/generic/dname_39.cc
@@ -16,13 +16,14 @@
#include <string>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
using namespace std;
+using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -52,7 +53,7 @@ DNAME::toWire(OutputBuffer& buffer) const {
}
void
-DNAME::toWire(MessageRenderer& renderer) const {
+DNAME::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeName(dname_);
}
diff --git a/src/lib/dns/rdata/generic/dnskey_48.cc b/src/lib/dns/rdata/generic/dnskey_48.cc
index 16a748e..e0f5461 100644
--- a/src/lib/dns/rdata/generic/dnskey_48.cc
+++ b/src/lib/dns/rdata/generic/dnskey_48.cc
@@ -20,8 +20,8 @@
#include <boost/lexical_cast.hpp>
#include <boost/foreach.hpp>
-#include <dns/util/base64.h>
-#include <dns/buffer.h>
+#include <util/encode/base64.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/name.h>
#include <dns/rdata.h>
@@ -31,6 +31,8 @@
#include <time.h>
using namespace std;
+using namespace isc::util;
+using namespace isc::util::encode;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -134,7 +136,7 @@ DNSKEY::toWire(OutputBuffer& buffer) const {
}
void
-DNSKEY::toWire(MessageRenderer& renderer) const {
+DNSKEY::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeUint16(impl_->flags_);
renderer.writeUint8(impl_->protocol_);
renderer.writeUint8(impl_->algorithm_);
diff --git a/src/lib/dns/rdata/generic/ds_43.cc b/src/lib/dns/rdata/generic/ds_43.cc
index cca19ae..1b48456 100644
--- a/src/lib/dns/rdata/generic/ds_43.cc
+++ b/src/lib/dns/rdata/generic/ds_43.cc
@@ -19,8 +19,9 @@
#include <boost/lexical_cast.hpp>
-#include <dns/buffer.h>
-#include <dns/util/hex.h>
+#include <util/buffer.h>
+#include <util/encode/hex.h>
+
#include <dns/messagerenderer.h>
#include <dns/name.h>
#include <dns/rdata.h>
@@ -30,6 +31,8 @@
#include <time.h>
using namespace std;
+using namespace isc::util;
+using namespace isc::util::encode;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -130,7 +133,7 @@ DS::toWire(OutputBuffer& buffer) const {
}
void
-DS::toWire(MessageRenderer& renderer) const {
+DS::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeUint16(impl_->tag_);
renderer.writeUint8(impl_->algorithm_);
renderer.writeUint8(impl_->digest_type_);
diff --git a/src/lib/dns/rdata/generic/mx_15.cc b/src/lib/dns/rdata/generic/mx_15.cc
index 0ae2251..4765222 100644
--- a/src/lib/dns/rdata/generic/mx_15.cc
+++ b/src/lib/dns/rdata/generic/mx_15.cc
@@ -20,7 +20,7 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
@@ -28,6 +28,7 @@
using namespace std;
using namespace boost;
+using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -71,7 +72,7 @@ MX::toWire(OutputBuffer& buffer) const {
}
void
-MX::toWire(MessageRenderer& renderer) const {
+MX::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeUint16(preference_);
renderer.writeName(mxname_);
}
diff --git a/src/lib/dns/rdata/generic/ns_2.cc b/src/lib/dns/rdata/generic/ns_2.cc
index 0e56911..631da9d 100644
--- a/src/lib/dns/rdata/generic/ns_2.cc
+++ b/src/lib/dns/rdata/generic/ns_2.cc
@@ -16,13 +16,14 @@
#include <string>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
using namespace std;
+using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -48,7 +49,7 @@ NS::toWire(OutputBuffer& buffer) const {
}
void
-NS::toWire(MessageRenderer& renderer) const {
+NS::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeName(nsname_);
}
diff --git a/src/lib/dns/rdata/generic/nsec3_50.cc b/src/lib/dns/rdata/generic/nsec3_50.cc
index fbe6612..3bd0bb2 100644
--- a/src/lib/dns/rdata/generic/nsec3_50.cc
+++ b/src/lib/dns/rdata/generic/nsec3_50.cc
@@ -20,10 +20,11 @@
#include <boost/lexical_cast.hpp>
-#include <dns/util/base32hex.h>
-#include <dns/buffer.h>
+#include <util/encode/base32hex.h>
+#include <util/encode/hex.h>
+#include <util/buffer.h>
+
#include <dns/exceptions.h>
-#include <dns/util/hex.h>
#include <dns/messagerenderer.h>
#include <dns/name.h>
#include <dns/rrtype.h>
@@ -37,6 +38,8 @@
using namespace std;
using namespace isc::dns::rdata::generic::detail::nsec;
+using namespace isc::util::encode;
+using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -259,7 +262,7 @@ NSEC3::toWire(OutputBuffer& buffer) const {
}
void
-NSEC3::toWire(MessageRenderer& renderer) const {
+NSEC3::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeUint8(impl_->hashalg_);
renderer.writeUint8(impl_->flags_);
renderer.writeUint16(impl_->iterations_);
diff --git a/src/lib/dns/rdata/generic/nsec3param_51.cc b/src/lib/dns/rdata/generic/nsec3param_51.cc
index 639feed..49f666b 100644
--- a/src/lib/dns/rdata/generic/nsec3param_51.cc
+++ b/src/lib/dns/rdata/generic/nsec3param_51.cc
@@ -19,8 +19,8 @@
#include <boost/lexical_cast.hpp>
-#include <dns/buffer.h>
-#include <dns/util/hex.h>
+#include <util/buffer.h>
+#include <util/encode/hex.h>
#include <dns/messagerenderer.h>
#include <dns/name.h>
#include <dns/rdata.h>
@@ -30,6 +30,8 @@
#include <time.h>
using namespace std;
+using namespace isc::util;
+using namespace isc::util::encode;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -134,7 +136,7 @@ NSEC3PARAM::toWire(OutputBuffer& buffer) const {
}
void
-NSEC3PARAM::toWire(MessageRenderer& renderer) const {
+NSEC3PARAM::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeUint8(impl_->hashalg_);
renderer.writeUint8(impl_->flags_);
renderer.writeUint16(impl_->iterations_);
diff --git a/src/lib/dns/rdata/generic/nsec_47.cc b/src/lib/dns/rdata/generic/nsec_47.cc
index 72eb946..93b8b5f 100644
--- a/src/lib/dns/rdata/generic/nsec_47.cc
+++ b/src/lib/dns/rdata/generic/nsec_47.cc
@@ -17,8 +17,8 @@
#include <sstream>
#include <vector>
-#include <dns/util/base64.h>
-#include <dns/buffer.h>
+#include <util/encode/base64.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/name.h>
@@ -32,6 +32,8 @@
#include <time.h>
using namespace std;
+using namespace isc::util;
+using namespace isc::util::encode;
using namespace isc::dns::rdata::generic::detail::nsec;
// BEGIN_ISC_NAMESPACE
@@ -171,7 +173,7 @@ NSEC::toWire(OutputBuffer& buffer) const {
}
void
-NSEC::toWire(MessageRenderer& renderer) const {
+NSEC::toWire(AbstractMessageRenderer& renderer) const {
impl_->nextname_.toWire(renderer);
renderer.writeData(&impl_->typebits_[0], impl_->typebits_.size());
}
diff --git a/src/lib/dns/rdata/generic/opt_41.cc b/src/lib/dns/rdata/generic/opt_41.cc
index 1aae810..62cfc17 100644
--- a/src/lib/dns/rdata/generic/opt_41.cc
+++ b/src/lib/dns/rdata/generic/opt_41.cc
@@ -16,12 +16,13 @@
#include <string>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
using namespace std;
+using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -56,7 +57,7 @@ OPT::toWire(OutputBuffer&) const {
}
void
-OPT::toWire(MessageRenderer&) const {
+OPT::toWire(AbstractMessageRenderer&) const {
// nothing to do, as this simple version doesn't support any options.
}
diff --git a/src/lib/dns/rdata/generic/ptr_12.cc b/src/lib/dns/rdata/generic/ptr_12.cc
index dc656b8..86ddeb4 100644
--- a/src/lib/dns/rdata/generic/ptr_12.cc
+++ b/src/lib/dns/rdata/generic/ptr_12.cc
@@ -16,13 +16,14 @@
#include <string>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
using namespace std;
+using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -53,7 +54,7 @@ PTR::toWire(OutputBuffer& buffer) const {
}
void
-PTR::toWire(MessageRenderer& renderer) const {
+PTR::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeName(ptr_name_);
}
diff --git a/src/lib/dns/rdata/generic/rp_17.cc b/src/lib/dns/rdata/generic/rp_17.cc
new file mode 100644
index 0000000..b8b2ba2
--- /dev/null
+++ b/src/lib/dns/rdata/generic/rp_17.cc
@@ -0,0 +1,125 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+#include <sstream>
+
+#include <util/buffer.h>
+
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+
+using namespace std;
+using namespace isc::dns;
+
+// BEGIN_ISC_NAMESPACE
+// BEGIN_RDATA_NAMESPACE
+
+/// \brief Constructor from string.
+///
+/// \c rp_str must be formatted as follows:
+/// \code <mailbox name> <text name>
+/// \endcode
+/// where both fields must represent a valid domain name.
+///
+/// \exception InvalidRdataText The number of RDATA fields (must be 2) is
+/// incorrect.
+/// \exception Other The constructor of the \c Name class will throw if the
+/// given name is invalid.
+/// \exception std::bad_alloc Memory allocation for names fails.
+RP::RP(const std::string& rp_str) :
+ // We cannot construct both names in the initialization list due to the
+ // necessary text processing, so we have to initialize them with a dummy
+ // name and replace them later.
+ mailbox_(Name::ROOT_NAME()), text_(Name::ROOT_NAME())
+{
+ istringstream iss(rp_str);
+ string mailbox_str, text_str;
+ iss >> mailbox_str >> text_str;
+
+ // Validation: A valid RP RR must have exactly two fields.
+ if (iss.bad() || iss.fail()) {
+ isc_throw(InvalidRdataText, "Invalid RP text: " << rp_str);
+ }
+ if (!iss.eof()) {
+ isc_throw(InvalidRdataText, "Invalid RP text (redundant field): "
+ << rp_str);
+ }
+
+ mailbox_ = Name(mailbox_str);
+ text_ = Name(text_str);
+}
+
+/// \brief Constructor from wire-format data.
+///
+/// This constructor doesn't check the validity of the second parameter (rdata
+/// length) for parsing.
+/// If necessary, the caller will check consistency.
+///
+/// \exception std::bad_alloc Memory allocation for names fails.
+/// \exception Other The constructor of the \c Name class will throw if the
+/// names in the wire is invalid.
+RP::RP(InputBuffer& buffer, size_t) : mailbox_(buffer), text_(buffer) {
+}
+
+/// \brief Copy constructor.
+///
+/// \exception std::bad_alloc Memory allocation fails in copying internal
+/// member variables (this should be very rare).
+RP::RP(const RP& other) :
+ Rdata(), mailbox_(other.mailbox_), text_(other.text_)
+{}
+
+/// \brief Convert the \c RP to a string.
+///
+/// The output of this method is formatted as described in the "from string"
+/// constructor (\c RP(const std::string&))).
+///
+/// \exception std::bad_alloc Internal resource allocation fails.
+///
+/// \return A \c string object that represents the \c RP object.
+std::string
+RP::toText() const {
+ return (mailbox_.toText() + " " + text_.toText());
+}
+
+void
+RP::toWire(OutputBuffer& buffer) const {
+ mailbox_.toWire(buffer);
+ text_.toWire(buffer);
+}
+
+void
+RP::toWire(AbstractMessageRenderer& renderer) const {
+ // Type RP is not "well-known", and name compression must be disabled
+ // per RFC3597.
+ renderer.writeName(mailbox_, false);
+ renderer.writeName(text_, false);
+}
+
+int
+RP::compare(const Rdata& other) const {
+ const RP& other_rp = dynamic_cast<const RP&>(other);
+
+ const int cmp = compareNames(mailbox_, other_rp.mailbox_);
+ if (cmp != 0) {
+ return (cmp);
+ }
+ return (compareNames(text_, other_rp.text_));
+}
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE
diff --git a/src/lib/dns/rdata/generic/rp_17.h b/src/lib/dns/rdata/generic/rp_17.h
new file mode 100644
index 0000000..a90a530
--- /dev/null
+++ b/src/lib/dns/rdata/generic/rp_17.h
@@ -0,0 +1,88 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// BEGIN_HEADER_GUARD
+
+#include <string>
+
+#include <dns/name.h>
+#include <dns/rdata.h>
+
+// BEGIN_ISC_NAMESPACE
+
+// BEGIN_COMMON_DECLARATIONS
+// END_COMMON_DECLARATIONS
+
+// BEGIN_RDATA_NAMESPACE
+
+/// \brief \c rdata::generic::RP class represents the RP RDATA as defined in
+/// RFC1183.
+///
+/// This class implements the basic interfaces inherited from the abstract
+/// \c rdata::Rdata class, and provides trivial accessors specific to the
+/// RP RDATA.
+class RP : public Rdata {
+public:
+ // BEGIN_COMMON_MEMBERS
+ // END_COMMON_MEMBERS
+
+ /// We use the default copy constructor and assignment operator.
+
+ /// \brief Constructor from RDATA field parameters.
+ ///
+ /// The parameters are a straightforward mapping of %RP RDATA
+ /// fields as defined in RFC1183.
+ RP(const Name& mailbox, const Name& text) :
+ mailbox_(mailbox), text_(text)
+ {}
+
+ /// \brief Return the value of the mailbox field.
+ ///
+ /// This method normally does not throw an exception, but if resource
+ /// allocation for the returned \c Name object fails, a corresponding
+ /// standard exception will be thrown.
+ ///
+ /// \note
+ /// Unlike the case of some other RDATA classes (such as
+ /// \c NS::getNSName()), this method constructs a new \c Name object
+ /// and returns it, instead of returning a reference to a \c Name object
+ /// internally maintained in the class (which is a private member).
+ /// This is based on the observation that this method will be rarely used
+ /// and even when it's used it will not be in a performance context
+ /// (for example, a recursive resolver won't need this field in its
+ /// resolution process). By returning a new object we have flexibility of
+ /// changing the internal representation without the risk of changing
+ /// the interface or method property.
+ /// The same note applies to the \c getText() method.
+ Name getMailbox() const { return (mailbox_); }
+
+ /// \brief Return the value of the text field.
+ ///
+ /// This method normally does not throw an exception, but if resource
+ /// allocation for the returned \c Name object fails, a corresponding
+ /// standard exception will be thrown.
+ Name getText() const { return (text_); }
+
+private:
+ Name mailbox_;
+ Name text_;
+};
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE
+// END_HEADER_GUARD
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/rdata/generic/rrsig_46.cc b/src/lib/dns/rdata/generic/rrsig_46.cc
index c9d1e52..0c82406 100644
--- a/src/lib/dns/rdata/generic/rrsig_46.cc
+++ b/src/lib/dns/rdata/generic/rrsig_46.cc
@@ -20,9 +20,9 @@
#include <boost/lexical_cast.hpp>
-#include <dns/util/base64.h>
-#include <dns/buffer.h>
-#include <dns/dnssectime.h>
+#include <util/encode/base64.h>
+#include <util/buffer.h>
+#include <util/time_utilities.h>
#include <dns/messagerenderer.h>
#include <dns/name.h>
#include <dns/rrtype.h>
@@ -34,6 +34,8 @@
#include <time.h>
using namespace std;
+using namespace isc::util;
+using namespace isc::util::encode;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -182,7 +184,7 @@ RRSIG::toWire(OutputBuffer& buffer) const {
}
void
-RRSIG::toWire(MessageRenderer& renderer) const {
+RRSIG::toWire(AbstractMessageRenderer& renderer) const {
impl_->covered_.toWire(renderer);
renderer.writeUint8(impl_->algorithm_);
renderer.writeUint8(impl_->labels_);
diff --git a/src/lib/dns/rdata/generic/soa_6.cc b/src/lib/dns/rdata/generic/soa_6.cc
index c4b87c6..7ecd84f 100644
--- a/src/lib/dns/rdata/generic/soa_6.cc
+++ b/src/lib/dns/rdata/generic/soa_6.cc
@@ -20,7 +20,7 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
@@ -28,6 +28,7 @@
using namespace std;
using namespace boost;
+using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -99,7 +100,7 @@ SOA::toWire(OutputBuffer& buffer) const {
}
void
-SOA::toWire(MessageRenderer& renderer) const {
+SOA::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeName(mname_);
renderer.writeName(rname_);
renderer.writeData(numdata_, sizeof(numdata_));
diff --git a/src/lib/dns/rdata/generic/txt_16.cc b/src/lib/dns/rdata/generic/txt_16.cc
index 0e20f4e..ac2ba8a 100644
--- a/src/lib/dns/rdata/generic/txt_16.cc
+++ b/src/lib/dns/rdata/generic/txt_16.cc
@@ -18,13 +18,14 @@
#include <string>
#include <vector>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
using namespace std;
+using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -101,7 +102,7 @@ TXT::toWire(OutputBuffer& buffer) const {
}
void
-TXT::toWire(MessageRenderer& renderer) const {
+TXT::toWire(AbstractMessageRenderer& renderer) const {
for (vector<vector<uint8_t> >::const_iterator it = string_list_.begin();
it != string_list_.end();
++it)
diff --git a/src/lib/dns/rdata/hs_4/a_1.cc b/src/lib/dns/rdata/hs_4/a_1.cc
index 376cbdd..65378a1 100644
--- a/src/lib/dns/rdata/hs_4/a_1.cc
+++ b/src/lib/dns/rdata/hs_4/a_1.cc
@@ -16,12 +16,13 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
using namespace std;
+using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -44,7 +45,7 @@ A::toWire(OutputBuffer&) const {
}
void
-A::toWire(MessageRenderer&) const {
+A::toWire(AbstractMessageRenderer&) const {
// TBD
}
diff --git a/src/lib/dns/rdata/in_1/a_1.cc b/src/lib/dns/rdata/in_1/a_1.cc
index ddd03f8..fa46f90 100644
--- a/src/lib/dns/rdata/in_1/a_1.cc
+++ b/src/lib/dns/rdata/in_1/a_1.cc
@@ -22,13 +22,14 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
using namespace std;
+using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -69,7 +70,7 @@ A::toWire(OutputBuffer& buffer) const {
}
void
-A::toWire(MessageRenderer& renderer) const {
+A::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeData(&addr_, sizeof(addr_));
}
diff --git a/src/lib/dns/rdata/in_1/aaaa_28.cc b/src/lib/dns/rdata/in_1/aaaa_28.cc
index 45c4682..e9fc122 100644
--- a/src/lib/dns/rdata/in_1/aaaa_28.cc
+++ b/src/lib/dns/rdata/in_1/aaaa_28.cc
@@ -22,13 +22,14 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
using namespace std;
+using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -66,7 +67,7 @@ AAAA::toWire(OutputBuffer& buffer) const {
}
void
-AAAA::toWire(MessageRenderer& renderer) const {
+AAAA::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeData(&addr_, sizeof(addr_));
}
diff --git a/src/lib/dns/rdata/template.cc b/src/lib/dns/rdata/template.cc
index 0e0bf46..d9f08ee 100644
--- a/src/lib/dns/rdata/template.cc
+++ b/src/lib/dns/rdata/template.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -14,12 +14,13 @@
#include <string>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
using namespace std;
+using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
@@ -50,7 +51,7 @@ MyType::toWire(OutputBuffer& buffer) const {
}
void
-MyType::toWire(MessageRenderer& renderer) const {
+MyType::toWire(AbstractMessageRenderer& renderer) const {
}
int
diff --git a/src/lib/dns/rdata/template.h b/src/lib/dns/rdata/template.h
index e85a839..9e84cc3 100644
--- a/src/lib/dns/rdata/template.h
+++ b/src/lib/dns/rdata/template.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
diff --git a/src/lib/dns/rdatafields.cc b/src/lib/dns/rdatafields.cc
new file mode 100644
index 0000000..d3f282b
--- /dev/null
+++ b/src/lib/dns/rdatafields.cc
@@ -0,0 +1,223 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+
+#include <cassert>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdatafields.h>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using isc::util::OutputBuffer;
+using isc::util::InputBuffer;
+
+namespace isc {
+namespace dns {
+namespace rdata {
+
+/// This is a helper class for \c RdataFields.
+///
+/// It manages a local storage for the data when \c RdataFields is constructed
+/// from an \c Rdata.
+/// To minimize construction overhead in the other case, an instance of
+/// this class is instantiated only when necessary - we don't need the vectors
+/// when only rendering.
+struct RdataFields::RdataFieldsDetail {
+ RdataFieldsDetail(const vector<FieldSpec>& fields,
+ const uint8_t* data, size_t data_length) :
+ allocated_fields_(fields),
+ allocated_data_(data, data + data_length)
+ {}
+ const vector<FieldSpec> allocated_fields_;
+ const vector<uint8_t> allocated_data_;
+};
+
+namespace {
+// This class is used to divide the content of RDATA into \c RdataField
+// fields via message rendering logic.
+// The idea is to identify domain name fields in the writeName() method,
+// and determine whether they are compressible using the "compress"
+// parameter.
+// Other types of data are simply copied into the internal buffer, and
+// consecutive such fields are combined into a single \c RdataField field.
+//
+// Technically, this use of inheritance may be considered a violation of
+// Liskov Substitution Principle in that it doesn't actually compress domain
+// names, and some of the methods are not expected to be used.
+// In fact, skip() or trim() may not be make much sense in this context.
+// Nevertheless we keep this idea at the moment. Since the usage is limited
+// (it's only used within this file, and only used with \c Rdata variants),
+// it's hopefully an acceptable practice.
+class RdataFieldComposer : public AbstractMessageRenderer {
+public:
+ RdataFieldComposer(OutputBuffer& buffer) :
+ AbstractMessageRenderer(buffer),
+ truncated_(false), length_limit_(65535),
+ mode_(CASE_INSENSITIVE), last_data_pos_(0)
+ {}
+ virtual ~RdataFieldComposer() {}
+ virtual bool isTruncated() const { return (truncated_); }
+ virtual size_t getLengthLimit() const { return (length_limit_); }
+ virtual CompressMode getCompressMode() const { return (mode_); }
+ virtual void setTruncated() { truncated_ = true; }
+ virtual void setLengthLimit(size_t len) { length_limit_ = len; }
+ virtual void setCompressMode(CompressMode mode) { mode_ = mode; }
+ virtual void writeName(const Name& name, bool compress) {
+ extendData();
+ const RdataFields::Type field_type =
+ compress ? RdataFields::COMPRESSIBLE_NAME :
+ RdataFields::INCOMPRESSIBLE_NAME;
+ // TODO: When we get rid of need for getBuffer, we can output the name
+ // to a buffer and then write the buffer inside
+ name.toWire(getBuffer());
+ fields_.push_back(RdataFields::FieldSpec(field_type,
+ name.getLength()));
+ last_data_pos_ = getLength();
+ }
+
+ virtual void clear() {
+ isc_throw(Unexpected, "unexpected clear() for RdataFieldComposer");
+ }
+ bool truncated_;
+ size_t length_limit_;
+ CompressMode mode_;
+ vector<RdataFields::FieldSpec> fields_;
+ vector<RdataFields::FieldSpec>& getFields() {
+ extendData();
+ return (fields_);
+ }
+ // We use generict write* methods, with the exception of writeName.
+ // So new data can arrive without us knowing it, this considers all new
+ // data to be just data and extends the fields to take it into account.
+ size_t last_data_pos_;
+ void extendData() {
+ // No news, return to work
+ if (getLength() == last_data_pos_) {
+ return;
+ }
+ // The new bytes are just ordinary uninteresting data
+ if (fields_.empty() || fields_.back().type != RdataFields::DATA) {
+ fields_.push_back(RdataFields::FieldSpec(RdataFields::DATA, 0));
+ }
+ // We added this much data from last time
+ fields_.back().len += getLength() - last_data_pos_;
+ last_data_pos_ = getLength();
+ }
+};
+
+}
+
+RdataFields::RdataFields(const Rdata& rdata) {
+ OutputBuffer buffer(0);
+ RdataFieldComposer field_composer(buffer);
+ rdata.toWire(field_composer);
+ nfields_ = field_composer.getFields().size();
+ data_length_ = field_composer.getLength();
+ if (nfields_ > 0) {
+ assert(data_length_ > 0);
+ detail_ = new RdataFieldsDetail(field_composer.getFields(),
+ static_cast<const uint8_t*>
+ (field_composer.getData()),
+ field_composer.getLength());
+ data_ = &detail_->allocated_data_[0];
+ fields_ = &detail_->allocated_fields_[0];
+ } else {
+ assert(data_length_ == 0);
+ detail_ = NULL;
+ data_ = NULL;
+ fields_ = NULL;
+ }
+}
+
+RdataFields::RdataFields(const void* fields, const unsigned int fields_length,
+ const void* data, const size_t data_length) :
+ fields_(static_cast<const FieldSpec*>(fields)),
+ nfields_(fields_length / sizeof(*fields_)),
+ data_(static_cast<const uint8_t*>(data)),
+ data_length_(data_length),
+ detail_(NULL)
+{
+ if ((fields_ == NULL && nfields_ > 0) ||
+ (fields_ != NULL && nfields_ == 0)) {
+ isc_throw(InvalidParameter,
+ "Inconsistent parameters for RdataFields: fields_length ("
+ << fields_length << ") and fields conflict each other");
+ }
+ if ((data_ == NULL && data_length_ > 0) ||
+ (data_ != NULL && data_length_ == 0)) {
+ isc_throw(InvalidParameter,
+ "Inconsistent parameters for RdataFields: data length ("
+ << data_length_ << ") and data conflict each other");
+ }
+
+ size_t total_length = 0;
+ for (int i = 0; i < nfields_; ++i) {
+ total_length += fields_[i].len;
+ }
+ if (total_length != data_length_) {
+ isc_throw(InvalidParameter,
+ "Inconsistent parameters for RdataFields; "
+ "fields len: " << total_length <<
+ " data len: " << data_length_);
+ }
+}
+
+RdataFields::~RdataFields() {
+ delete detail_;
+}
+
+RdataFields::FieldSpec
+RdataFields::getFieldSpec(const unsigned int field_id) const {
+ if (field_id >= nfields_) {
+ isc_throw(OutOfRange, "Rdata field ID is out of range: " << field_id);
+ }
+ return (fields_[field_id]);
+}
+
+void
+RdataFields::toWire(AbstractMessageRenderer& renderer) const {
+ size_t offset = 0;
+
+ for (int i = 0; i < nfields_; ++i) {
+ if (fields_[i].type == DATA) {
+ renderer.writeData(data_ + offset, fields_[i].len);
+ } else {
+ // XXX: this is inefficient. Even if it's quite likely the
+ // data is a valid wire representation of a name we parse
+ // it to construct the Name object in the generic mode.
+ // This should be improved in a future version.
+ InputBuffer buffer(data_ + offset, fields_[i].len);
+ renderer.writeName(Name(buffer),
+ fields_[i].type == COMPRESSIBLE_NAME);
+ }
+ offset += fields_[i].len;
+ }
+}
+
+void
+RdataFields::toWire(OutputBuffer& buffer) const {
+ buffer.writeData(data_, data_length_);
+}
+} // end of namespace rdata
+} // end of namespace dns
+} // end of namespace isc
diff --git a/src/lib/dns/rdatafields.h b/src/lib/dns/rdatafields.h
new file mode 100644
index 0000000..e33bcd7
--- /dev/null
+++ b/src/lib/dns/rdatafields.h
@@ -0,0 +1,427 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __RDATAFIELDS_H
+#define __RDATAFIELDS_H 1
+
+#include <stdint.h>
+
+#include <cstddef>
+
+namespace isc {
+namespace util {
+class OutputBuffer;
+}
+namespace dns {
+class AbstractMessageRenderer;
+
+namespace rdata {
+class Rdata;
+
+/// A low-level, RR type-independent representation of DNS RDATA.
+///
+/// <b>Purpose of the Class</b>
+///
+/// This class intends to help "serialization" of the content of RDATA
+/// in a space-efficient manner. Specific derived classes of \c Rdata
+/// focus on the convenience of accessing RDATA fields for RR type-specific
+/// protocol operations, and can be inefficient in terms of space.
+/// For example, a DNS character string may be internally represented as a
+/// \c std::string object with all of the overhead of the richer class.
+/// If an application needs to maintain a very large number of RRs and it
+/// does not have to perform RR specific operation so often, it may make more
+/// sense to store the data in memory in a lower-level but space efficient
+/// form.
+///
+/// Another purpose of this class is to improve rendering performance for
+/// RDATA. If the only requirement were space efficiency, it would be just
+/// sufficient to convert the \c RDATA into a binary sequence in the wire
+/// format. However, to render the data in a DNS message, we'd have to
+/// re-construct a corresponding \c Rdata object in the case where name
+/// compression is necessary. This is not desirable, and this class is
+/// provided to avoid such unnecessary overhead.
+///
+/// <b>Data Format</b>
+///
+/// To meet these goals, this class helps convert an \c Rdata object into
+/// two pieces of information: Wire-format representation of the \c Rdata
+/// and associated meta information for efficient rendering.
+///
+/// Specifically, it maintains the wire-format data as a sequence of typed
+/// fields. The types are:
+/// - Compressible name: a domain name as an RDATA field that can be compressed
+/// - Incompressible name: a domain name as an RDATA field that cannot be
+/// compressed
+/// - Other data: any other fields of RDATA, which should be treated as opaque
+///
+/// (See also the description of \c RdataFields::Type)
+/// Whether a name can or cannot be compressed is determined according to
+/// RFC3597.
+///
+/// A "other data" field may not always correspond to a single RDATA field.
+/// A \c RdataFields field (of other data) is just a contiguous region of the
+/// wire-format data that does not involve name compression.
+/// For example, the SOA RDATA begins with two "compressible" names followed
+/// by 5 32-bit fields.
+/// In \c RdataFields the last 5 fields would be considered a single 20-byte
+/// field.
+///
+/// Each \c RdataFields field is identified by the \c FieldSpec structure,
+/// which provides the type and length of the field.
+/// An \c RdataFields object internally maintains a sequence of \c FieldSpec
+/// objects in a form of plain C-style array, which can be referenced via
+/// a pointer returned by the \c getFieldSpecData() method.
+/// The \c \c FieldSpec for a specific field can also be retrieved by the
+/// \c getFieldSpec() method.
+///
+/// The following diagram shows the internal memory representation of
+/// an SOA RDATA in the form of \c RdataFields object and how an application
+/// can get access to the memory region.
+/** \verbatim
+accessible via |0 getDataLength() bytes
+getData()----------> <MNAME><RNAME><Rest of the data>
+ <---------- 3 * sizeof(FieldSpec) bytes ------------->
+getFieldSpecData()-> { compressible name { compressible name { other data
+ len: MNAME-len } len: RNAME-len } len: 20 }
+\endverbatim
+ */
+/// where MNAME and RNAME are wire format representations of the MNAME and
+/// RNAME fields of the SOA RDATA, respectively, and "Rest of the data"
+/// encodes the remaining 20 bytes of the RDATA in network byte order.
+///
+/// <b>Usage of the Class</b>
+///
+/// One major and common use case of the \c RdataFields class is to convert
+/// a \c Rdata object (possibly given from a DNS message or some configuration
+/// source such as a zone file) in the serialized format and store a copy of
+/// the data somewhere in memory. The following code sample implements this
+/// scenario:
+/// \code // assume "rdata" is a reference type to Rdata
+/// const RdataFields fields(rdata);
+/// const unsigned int fields_size = fields.getFieldDataSize();
+/// memcpy(some_place, fields.getFieldSpecData(), fields_size);
+/// const size_t data_length = fields.getDataLength();
+/// memcpy(other_place, fields.getData(), data_length);
+/// // (fields_size and data_length should be stored somewhere, too)
+/// \endcode
+///
+/// Another typical usage is to render the stored data in the wire format
+/// as efficiently as possible. The following code is an example of such
+/// usage:
+/// \code // assume "renderer" is of type MessageRenderer
+/// // retrieve data_length and fields_size from the storage
+/// RdataFields(some_place, fields_size, other_place,
+/// data_length).toWire(renderer);
+/// \endcode
+///
+/// <b>Notes to Users</b>
+///
+/// The main purposes of this class is to help efficient operation
+/// for some (limited classes of) performance sensitive application.
+/// For this reason the interface and implementation rely on relatively
+/// lower-level, riskier primitives such as passing around bare pointers.
+///
+/// It is therefore discouraged to use this class for general purpose
+/// applications that do not need to maximize performance in terms of either
+/// memory footprint or rendering speed.
+/// All functionality provided by this class can be achieved via higher level
+/// interfaces such as the \c Rdata class variants.
+/// Normal applications should use those interfaces.
+///
+/// The data format is public information so that an application can examine
+/// and use selected parts of data. For example, an application may want to
+/// encode domain names in RDATA in a different way while storing the other
+/// data in a separate place.
+/// However, at this moment the format is still in flux, and it may not
+/// be compatible with future versions (see below).
+///
+/// <b>Development Notes</b>
+///
+/// We should conduct benchmark tests to measure rendering performance.
+///
+/// The current implementation needs to re-construct name objects from
+/// compressible and incompressible name fields as wire-format data.
+/// This is not efficient, and we'll probably want to improve this in a
+/// future version. One possibility is to store offset information as well
+/// as the name data (at the cost of increasing memory footprint), and
+/// to use the pair of data for faster rendering.
+class RdataFields {
+public:
+ /// Types of \c RdataFields fields.
+ ///
+ /// \c COMPRESSIBLE_NAME and \c INCOMPRESSIBLE_NAME represent a domain
+ /// name used as a field of an RDATA that can and cannot be compressed
+ /// per RFC3597.
+ /// \c DATA means all other types of fields.
+ enum Type {
+ DATA, ///< Plain data.
+ COMPRESSIBLE_NAME, ///< A domain name subject to name compression.
+ INCOMPRESSIBLE_NAME ///< A domain name that shouldn't be compressed.
+ };
+
+ /// Structure that specifies a single \c RdataFields field.
+ ///
+ /// This is a straightforward pair of the type and length of a single
+ /// \c RdataFields field.
+ ///
+ /// In some cases an application may want to do deeper inspection of
+ /// some \c RdataFields field(s). For example, an application may want
+ /// to construct a \c Name object for each domain name field of an RDATA
+ /// and use it for some special purpose.
+ /// The \c FieldSpec structure provides necessary parameters to get access
+ /// to a specific \c RdataFields field.
+ ///
+ /// The following code snippet implements the above example scenario:
+ /// \code // assume "fields" is of type RdataFields
+ /// size_t offset = 0;
+ /// for (int i = 0; i < fields.getFieldCount(); ++i) {
+ /// const FieldSpec spec = fields.getFieldSpec(i);
+ /// if (spec.type == RdataFields::COMPRESSIBLE_NAME ||
+ /// spec.type == RdataFields::INCOMPRESSIBLE_NAME) {
+ /// InputBuffer ibuffer(fields.getData() + offset, spec.len);
+ /// Name name(ibuffer);
+ /// // do something with name
+ /// }
+ /// offset += spec.len;
+ /// } \endcode
+ ///
+ /// Note that the offset is not included in \c FieldSpec.
+ /// This is because such deeper inspection would be a relatively rare
+ /// operation while it is desirable to keep this structure as small as
+ /// possible for the purpose of space efficiency.
+ /// Also, if and when an application wants to look into a specific field,
+ /// it would be quite likely that the application iterates over all fields
+ /// and does something special for selected fields like the above example.
+ /// In that case the application can easily and efficiently identify the
+ /// necessary offset, again, as shown in the above code example.
+ ///
+ /// \todo We might find that 16bits per field is generally too much and
+ /// squeeze the two bit type into it as well, having 14bit length
+ /// (in the rare case of having too long field, it could be split into
+ /// multiple ones). That would save 2 bytes per item (one for the type,
+ /// one for padding).
+ struct FieldSpec {
+ FieldSpec(Type type_param, uint16_t len_param) :
+ type(type_param), len(len_param)
+ {}
+ Type type; ///< The type of the field.
+ uint16_t len; ///< The length of the field in bytes.
+ };
+
+ ///
+ /// \name Constructors and Destructor.
+ ///
+ /// \b Note:
+ /// The copy constructor and the assignment operator are intentionally
+ /// defined as private, making this class non copyable.
+ //@{
+private:
+ RdataFields(const RdataFields& source);
+ RdataFields& operator=(const RdataFields& source);
+
+public:
+ /// Constructor from Rdata.
+ ///
+ /// This constructor converts the data of a given \c Rdata object into
+ /// an \c RdataFields object so that the resulting data can be stored
+ /// in memory in a space-efficient way.
+ ///
+ /// It makes a local copy of the original data and dynamically allocates
+ /// necessary memory, so is not very efficient.
+ /// The basic idea is to perform the expensive conversion once and keep
+ /// using the result as long as possible to improve overall performance
+ /// in a longer term.
+ ///
+ /// If the internal resource allocation fails, a corresponding standard
+ /// exception will be thrown.
+ /// The current implementation of this constructor internally calls
+ /// the <code>Rdata::toWire(AbstractMessageRenderer&) const</code> method
+ /// for the conversion.
+ /// If that method throws an exception it will be propagated to the caller
+ /// of this constructor.
+ ///
+ /// \param rdata The RDATA for which the \c RdataFields to be constructed.
+ RdataFields(const Rdata& rdata);
+
+ /// Constructor from field parameters.
+ ///
+ /// The intended usage of this version of constructor is to form a
+ /// structured representation of \c RDATA encoded by the other
+ /// constructor so that the resulting object can be used for subsequent
+ /// operations such as rendering in the wire format.
+ /// This version is intended to be efficient by not making any copy
+ /// of variable length data or expensive data inspection.
+ ///
+ /// This constructor is basically exception free, except against bogus
+ /// input parameters.
+ /// Specifically, the parameters must meet the following conditions;
+ /// otherwise an exception of class \c InvalidParameter will be thrown.
+ /// - \c fields can be \c NULL if and only if \c nfields is 0
+ /// - \c data can be \c NULL if and only if \c data_length is 0
+ /// - the sum of the lengths of \c fields entries must be equal to
+ /// \c data_length
+ ///
+ /// This constructor assumes that the memory region pointed by \c data (if
+ /// non \c NULL) is encoded as a sequence of valid \c RdataFields fields,
+ /// and does not perform deep inspection on each field.
+ /// In particular, for fields of type \c COMPRESSIBLE_NAME or
+ /// \c INCOMPRESSIBLE_NAME, this constructor assumes the corresponding
+ /// memory region is a valid representation of domain name.
+ /// Otherwise, a subsequent method call such as
+ /// <code>toWire(AbstractMessageRenderer&) const</code>
+ /// may trigger an unexpected exception. It also expects the fields reside
+ /// on address that is valid for them (eg. it has valid alignment), see
+ /// getFieldSpecData() for details.
+ ///
+ /// It is the caller's responsibility to ensure this assumption.
+ /// In general, this constructor is expected to be used for serialized data
+ /// generated by the other constructor from a valid \c Rdata.
+ /// The result is not guaranteed if the data is generated in any other
+ /// ways.
+ ///
+ /// The resulting \c RdataFields object does not maintain a copy of
+ /// \c fields or \c data. It is the caller's responsibility to ensure
+ /// the memory regions pointed to by these parameters are valid and intact
+ /// as long as the \c RdataFields object is used.
+ ///
+ /// \param fields An array of \c FieldSpec entries. This can be \c NULL.
+ /// \param nfields The number of entries of \c fields.
+ /// \param data A pointer to memory region for the entire RDATA. This can
+ /// be NULL.
+ /// \param data_length The length of \c data in bytes.
+ RdataFields(const void* fields, const unsigned int fields_length,
+ const void* data, const size_t data_length);
+
+ /// The destructor.
+ ~RdataFields();
+ //@}
+
+ ///
+ /// \name Getter Methods
+ ///
+ //@{
+ /// \brief Return the length of the entire RDATA encoded in the
+ /// \c RdataFields in bytes.
+ ///
+ /// This method never throws an exception.
+ unsigned int getDataLength() const { return (data_length_); }
+
+ /// \brief Return a pointer to the RDATA encoded in the \c RdataFields.
+ ///
+ /// The RdataFields holds ownership of the data.
+ ///
+ /// This method never throws an exception.
+ const void* getData() const { return (data_); }
+
+ /// \brief Return the number of bytes the buffer returned by
+ /// getFieldSpecData() will occupy.
+ ///
+ /// This method never throws an exception.
+ unsigned int getFieldSpecDataSize() const { return (nfields_ *
+ sizeof (*fields_)); }
+
+ /// \brief Return the number of specs fields.
+ ///
+ /// It specifies the range of parameter for getFieldSpec().
+ ///
+ /// This method never throws.
+ unsigned int getFieldCount() const { return (nfields_); }
+
+ /// \brief Return a pointer to a sequence of \c FieldSpec for the
+ /// \c RdataFields.
+ ///
+ /// This should be treated as an opaque internal representation you can
+ /// just store off somewhere and use it to construct a new RdataFields.
+ /// from it. If you are really interested, you can typecast it to
+ /// FieldSpec * (which is what it really is internally).
+ ///
+ /// The RdataFields holds ownership of the data.
+ ///
+ /// \note You should, however, be aware of alignment issues. The pointer
+ /// you pass to the constructor must be an address where the FieldSpec
+ /// can live. If you store it at a wrong address (eg. even one with
+ /// current implementation on most architectures), it might lead bad
+ /// things from slow access to SIGBUS. The easiest way is not to
+ /// interleave the fields with data from getData(). It is OK to place
+ /// all the fields first (even from multiple RdataFields) and then
+ /// place all the data after them.
+ ///
+ /// This method never throws an exception.
+ const void* getFieldSpecData() const {
+ return (fields_);
+ }
+
+ /// \brief Return the specification of the field identified by the given
+ /// index.
+ ///
+ /// \c field_id is the field index, which must be in the range of
+ /// <code>[0, getFieldCount())</code>. 0 means the first field, and
+ /// <code>getFieldCount()-1</code> means the last.
+ ///
+ /// If the given index is not in the valid range, an exception of class
+ /// \c OutOfRange will be thrown.
+ /// This method never throws an exception otherwise.
+ ///
+ /// \param field_id The index of an \c RdataFields field to be returned.
+ /// \return A \c FieldSpec structure that contains the information of
+ /// the \c field_id-th field.
+ FieldSpec getFieldSpec(const unsigned int field_id) const;
+ //@}
+
+ ///
+ /// \name Converter Methods
+ ///
+ //@{
+ /// \brief Render the RdataFields in the wire format with name compression.
+ ///
+ /// This method may require resource allocation in \c renderer.
+ /// If it fails, a corresponding standard exception will be thrown.
+ /// It should not throw any other exception as long as the \c RdataFields
+ /// object was constructed from valid parameters (see the description of
+ /// constructors). The result is not guaranteed if it's constructed in
+ /// any other ways.
+ ///
+ /// \param renderer DNS message rendering context that encapsulates the
+ /// output buffer and name compression information.
+ void toWire(AbstractMessageRenderer& renderer) const;
+
+ /// \brief Render the RdataFields in the wire format without name
+ /// compression.
+ ///
+ /// This method may require resource allocation in \c buffer.
+ /// If it fails, a corresponding standard exception will be thrown.
+ ///
+ /// \param buffer An output buffer to store the wire data.
+ void toWire(isc::util::OutputBuffer& buffer) const;
+ //@}
+
+private:
+ const FieldSpec* fields_;
+ unsigned int nfields_;
+ const uint8_t* data_;
+ size_t data_length_;
+
+ // hide further details within the implementation and don't create vectors
+ // every time we don't need them.
+ struct RdataFieldsDetail;
+ RdataFieldsDetail* detail_;
+};
+}
+}
+}
+#endif // __RDATAFIELDS_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/rrclass-placeholder.h b/src/lib/dns/rrclass-placeholder.h
index ce9a141..80035d8 100644
--- a/src/lib/dns/rrclass-placeholder.h
+++ b/src/lib/dns/rrclass-placeholder.h
@@ -23,12 +23,15 @@
#include <exceptions/exceptions.h>
namespace isc {
+namespace util {
+class InputBuffer;
+class OutputBuffer;
+}
+
namespace dns {
// forward declarations
-class InputBuffer;
-class OutputBuffer;
-class MessageRenderer;
+class AbstractMessageRenderer;
///
/// \brief A standard DNS module exception that is thrown if an RRClass object
@@ -132,7 +135,7 @@ public:
/// an exception of class \c IncompleteRRClass will be thrown.
///
/// \param buffer A buffer storing the wire format data.
- explicit RRClass(InputBuffer& buffer);
+ explicit RRClass(isc::util::InputBuffer& buffer);
///
/// We use the default copy constructor intentionally.
//@}
@@ -166,7 +169,7 @@ public:
/// standard exception will be thrown.
///
/// \param buffer An output buffer to store the wire data.
- void toWire(MessageRenderer& renderer) const;
+ void toWire(AbstractMessageRenderer& renderer) const;
/// \brief Render the \c RRClass in the wire format.
///
/// This method renders the class code in network byte order into the
@@ -177,7 +180,7 @@ public:
///
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer in which the RRClass is to be stored.
- void toWire(OutputBuffer& buffer) const;
+ void toWire(isc::util::OutputBuffer& buffer) const;
//@}
///
diff --git a/src/lib/dns/rrclass.cc b/src/lib/dns/rrclass.cc
index 04ff59c..a28e5cf 100644
--- a/src/lib/dns/rrclass.cc
+++ b/src/lib/dns/rrclass.cc
@@ -18,13 +18,14 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rrparamregistry.h>
#include <dns/rrclass.h>
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
namespace isc {
namespace dns {
@@ -51,7 +52,7 @@ RRClass::toWire(OutputBuffer& buffer) const {
}
void
-RRClass::toWire(MessageRenderer& renderer) const {
+RRClass::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeUint16(classcode_);
}
diff --git a/src/lib/dns/rrparamregistry-placeholder.cc b/src/lib/dns/rrparamregistry-placeholder.cc
index 19363a3..5058ca3 100644
--- a/src/lib/dns/rrparamregistry-placeholder.cc
+++ b/src/lib/dns/rrparamregistry-placeholder.cc
@@ -36,6 +36,7 @@
using namespace std;
using namespace boost;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace isc {
diff --git a/src/lib/dns/rrparamregistry.h b/src/lib/dns/rrparamregistry.h
index a856423..ae41b5f 100644
--- a/src/lib/dns/rrparamregistry.h
+++ b/src/lib/dns/rrparamregistry.h
@@ -102,7 +102,7 @@ public:
/// \c Rdata to parse.
/// \param rdata_len The length in buffer of the \c Rdata. In bytes.
/// \return An \c RdataPtr object pointing to the created \c Rdata object.
- virtual RdataPtr create(InputBuffer& buffer, size_t rdata_len) const = 0;
+ virtual RdataPtr create(isc::util::InputBuffer& buffer, size_t rdata_len) const = 0;
///
/// \brief Create RDATA from another \c Rdata object of the same type.
///
@@ -473,7 +473,7 @@ public:
/// \return An \c rdata::RdataPtr object pointing to the created \c Rdata
/// object.
rdata::RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass,
- InputBuffer& buffer, size_t len);
+ isc::util::InputBuffer& buffer, size_t len);
/// \brief Create RDATA of a given pair of RR type and class, copying
/// of another RDATA of same kind.
///
diff --git a/src/lib/dns/rrset.cc b/src/lib/dns/rrset.cc
index b931bec..776d49f 100644
--- a/src/lib/dns/rrset.cc
+++ b/src/lib/dns/rrset.cc
@@ -18,7 +18,7 @@
#include <boost/shared_ptr.hpp>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/name.h>
#include <dns/rrclass.h>
@@ -28,6 +28,7 @@
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace isc {
@@ -103,8 +104,8 @@ AbstractRRset::toWire(OutputBuffer& buffer) const {
}
unsigned int
-AbstractRRset::toWire(MessageRenderer& renderer) const {
- const unsigned int rrs_written = rrsetToWire<MessageRenderer>(
+AbstractRRset::toWire(AbstractMessageRenderer& renderer) const {
+ const unsigned int rrs_written = rrsetToWire<AbstractMessageRenderer>(
*this, renderer, renderer.getLengthLimit());
if (getRdataCount() > rrs_written) {
renderer.setTruncated();
@@ -201,7 +202,7 @@ BasicRRset::toWire(OutputBuffer& buffer) const {
}
unsigned int
-BasicRRset::toWire(MessageRenderer& renderer) const {
+BasicRRset::toWire(AbstractMessageRenderer& renderer) const {
return (AbstractRRset::toWire(renderer));
}
diff --git a/src/lib/dns/rrset.h b/src/lib/dns/rrset.h
index 926a58f..6c15b53 100644
--- a/src/lib/dns/rrset.h
+++ b/src/lib/dns/rrset.h
@@ -26,6 +26,10 @@
#include <dns/rrtype.h>
namespace isc {
+namespace util {
+class OututBuffer;
+}
+
namespace dns {
///
@@ -43,8 +47,7 @@ class Name;
class RRType;
class RRClass;
class RRTTL;
-class OututBuffer;
-class MessageRenderer;
+class AbstractMessageRenderer;
class AbstractRRset;
class BasicRRset;
class RdataIterator;
@@ -308,7 +311,7 @@ public:
/// \return The number of RRs rendered. If the truncation is necessary
/// this value may be different from the number of RDATA objects contained
/// in the RRset.
- virtual unsigned int toWire(MessageRenderer& renderer) const = 0;
+ virtual unsigned int toWire(AbstractMessageRenderer& renderer) const = 0;
/// \brief Render the RRset in the wire format without any compression.
///
@@ -316,7 +319,7 @@ public:
///
/// \param buffer An output buffer to store the wire data.
/// \return The number of RRs rendered.
- virtual unsigned int toWire(OutputBuffer& buffer) const = 0;
+ virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const = 0;
//@}
///
@@ -614,13 +617,13 @@ public:
///
/// This method simply uses the default implementation.
/// See \c AbstractRRset::toWire(MessageRenderer&)const.
- virtual unsigned int toWire(MessageRenderer& renderer) const;
+ virtual unsigned int toWire(AbstractMessageRenderer& renderer) const;
/// \brief Render the RRset in the wire format without any compression.
///
/// This method simply uses the default implementation.
/// See \c AbstractRRset::toWire(OutputBuffer&)const.
- virtual unsigned int toWire(OutputBuffer& buffer) const;
+ virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const;
//@}
///
diff --git a/src/lib/dns/rrttl.cc b/src/lib/dns/rrttl.cc
index 78bb355..ecd8cc6 100644
--- a/src/lib/dns/rrttl.cc
+++ b/src/lib/dns/rrttl.cc
@@ -17,12 +17,13 @@
#include <sstream>
#include <ostream>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rrttl.h>
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
namespace isc {
namespace dns {
@@ -62,7 +63,7 @@ RRTTL::toWire(OutputBuffer& buffer) const {
}
void
-RRTTL::toWire(MessageRenderer& renderer) const {
+RRTTL::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeUint32(ttlval_);
}
diff --git a/src/lib/dns/rrttl.h b/src/lib/dns/rrttl.h
index 695306a..bf23295 100644
--- a/src/lib/dns/rrttl.h
+++ b/src/lib/dns/rrttl.h
@@ -20,12 +20,15 @@
#include <exceptions/exceptions.h>
namespace isc {
+namespace util {
+class InputBuffer;
+class OutputBuffer;
+}
+
namespace dns {
// forward declarations
-class InputBuffer;
-class OutputBuffer;
-class MessageRenderer;
+class AbstractMessageRenderer;
///
/// \brief A standard DNS module exception that is thrown if an RRTTL object
@@ -91,7 +94,7 @@ public:
/// an exception of class \c IncompleteRRTTL will be thrown.
///
/// \param buffer A buffer storing the wire format data.
- explicit RRTTL(InputBuffer& buffer);
+ explicit RRTTL(isc::util::InputBuffer& buffer);
///
//@}
@@ -120,7 +123,7 @@ public:
///
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer in which the RRTTL is to be stored.
- void toWire(MessageRenderer& renderer) const;
+ void toWire(AbstractMessageRenderer& renderer) const;
/// \brief Render the \c RRTTL in the wire format.
///
/// This method renders the TTL value in network byte order into the
@@ -130,7 +133,7 @@ public:
/// standard exception will be thrown.
///
/// \param buffer An output buffer to store the wire data.
- void toWire(OutputBuffer& buffer) const;
+ void toWire(isc::util::OutputBuffer& buffer) const;
//@}
///
diff --git a/src/lib/dns/rrtype-placeholder.h b/src/lib/dns/rrtype-placeholder.h
index 76cb29d..1cb028c 100644
--- a/src/lib/dns/rrtype-placeholder.h
+++ b/src/lib/dns/rrtype-placeholder.h
@@ -23,12 +23,15 @@
#include <exceptions/exceptions.h>
namespace isc {
+namespace util {
+class InputBuffer;
+class OutputBuffer;
+}
+
namespace dns {
// forward declarations
-class InputBuffer;
-class OutputBuffer;
-class MessageRenderer;
+class AbstractMessageRenderer;
///
/// \brief A standard DNS module exception that is thrown if an RRType object
@@ -145,7 +148,7 @@ public:
/// an exception of class \c IncompleteRRType will be thrown.
///
/// \param buffer A buffer storing the wire format data.
- explicit RRType(InputBuffer& buffer);
+ explicit RRType(isc::util::InputBuffer& buffer);
///
/// We use the default copy constructor intentionally.
//@}
@@ -178,7 +181,7 @@ public:
/// standard exception will be thrown.
///
/// \param buffer An output buffer to store the wire data.
- void toWire(MessageRenderer& renderer) const;
+ void toWire(AbstractMessageRenderer& renderer) const;
/// \brief Render the \c RRType in the wire format.
///
/// This method renders the type code in network byte order into the
@@ -189,7 +192,7 @@ public:
///
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer in which the RRType is to be stored.
- void toWire(OutputBuffer& buffer) const;
+ void toWire(isc::util::OutputBuffer& buffer) const;
//@}
///
diff --git a/src/lib/dns/rrtype.cc b/src/lib/dns/rrtype.cc
index 44377f5..af077d4 100644
--- a/src/lib/dns/rrtype.cc
+++ b/src/lib/dns/rrtype.cc
@@ -19,14 +19,14 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rrparamregistry.h>
#include <dns/rrtype.h>
using namespace std;
+using namespace isc::util;
using isc::dns::RRType;
-using isc::dns::OutputBuffer;
namespace isc {
namespace dns {
@@ -53,7 +53,7 @@ RRType::toWire(OutputBuffer& buffer) const {
}
void
-RRType::toWire(MessageRenderer& renderer) const {
+RRType::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeUint16(typecode_);
}
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index 246adb7..3a249c1 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -3,6 +3,7 @@ SUBDIRS = testdata .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
AM_CPPFLAGS += -DTEST_DATA_SRCDIR=\"$(srcdir)/testdata\"
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/dns/tests/testdata\"
AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -17,15 +18,15 @@ TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = unittest_util.h unittest_util.cc
-run_unittests_SOURCES += buffer_unittest.cc name_unittest.cc
run_unittests_SOURCES += edns_unittest.cc
run_unittests_SOURCES += messagerenderer_unittest.cc
+run_unittests_SOURCES += name_unittest.cc
run_unittests_SOURCES += rrclass_unittest.cc rrtype_unittest.cc
run_unittests_SOURCES += rrttl_unittest.cc
-run_unittests_SOURCES += dnssectime_unittest.cc
run_unittests_SOURCES += opcode_unittest.cc
run_unittests_SOURCES += rcode_unittest.cc
run_unittests_SOURCES += rdata_unittest.h rdata_unittest.cc
+run_unittests_SOURCES += rdatafields_unittest.cc
run_unittests_SOURCES += rdata_in_a_unittest.cc rdata_in_aaaa_unittest.cc
run_unittests_SOURCES += rdata_ns_unittest.cc rdata_soa_unittest.cc
run_unittests_SOURCES += rdata_txt_unittest.cc rdata_mx_unittest.cc
@@ -39,22 +40,28 @@ run_unittests_SOURCES += rdata_nsec3_unittest.cc
run_unittests_SOURCES += rdata_nsecbitmap_unittest.cc
run_unittests_SOURCES += rdata_nsec3param_unittest.cc
run_unittests_SOURCES += rdata_rrsig_unittest.cc
+run_unittests_SOURCES += rdata_rp_unittest.cc
run_unittests_SOURCES += rdata_tsig_unittest.cc
run_unittests_SOURCES += rrset_unittest.cc rrsetlist_unittest.cc
run_unittests_SOURCES += question_unittest.cc
run_unittests_SOURCES += rrparamregistry_unittest.cc
run_unittests_SOURCES += masterload_unittest.cc
run_unittests_SOURCES += message_unittest.cc
-run_unittests_SOURCES += base32hex_unittest.cc
-run_unittests_SOURCES += base64_unittest.cc
-run_unittests_SOURCES += hex_unittest.cc
-run_unittests_SOURCES += sha1_unittest.cc
+run_unittests_SOURCES += tsig_unittest.cc
+run_unittests_SOURCES += tsigerror_unittest.cc
run_unittests_SOURCES += tsigkey_unittest.cc
+run_unittests_SOURCES += tsigrecord_unittest.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+# We shouldn't need to include BOTAN_LDFLAGS here, but there
+# is one test system where the path for GTEST_LDFLAGS contains
+# an older version of botan, and somehow that version gets
+# linked if we don't
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(BOTAN_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/dns/tests/base32hex_unittest.cc b/src/lib/dns/tests/base32hex_unittest.cc
deleted file mode 100644
index 253d310..0000000
--- a/src/lib/dns/tests/base32hex_unittest.cc
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <stdint.h>
-
-#include <cctype>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <exceptions/exceptions.h>
-
-#include <dns/util/base32hex.h>
-
-#include <gtest/gtest.h>
-
-using namespace std;
-using namespace isc;
-using namespace isc::dns;
-
-namespace {
-
-typedef pair<string, string> StringPair;
-
-class Base32HexTest : public ::testing::Test {
-protected:
- Base32HexTest() : encoding_chars("0123456789ABCDEFGHIJKLMNOPQRSTUV=") {
- // test vectors from RFC4648
- test_sequence.push_back(StringPair("", ""));
- test_sequence.push_back(StringPair("f", "CO======"));
- test_sequence.push_back(StringPair("fo", "CPNG===="));
- test_sequence.push_back(StringPair("foo", "CPNMU==="));
- test_sequence.push_back(StringPair("foob", "CPNMUOG="));
- test_sequence.push_back(StringPair("fooba", "CPNMUOJ1"));
- test_sequence.push_back(StringPair("foobar", "CPNMUOJ1E8======"));
-
- // the same data, encoded using lower case chars (testable only
- // for the decode side)
- test_sequence_lower.push_back(StringPair("f", "co======"));
- test_sequence_lower.push_back(StringPair("fo", "cpng===="));
- test_sequence_lower.push_back(StringPair("foo", "cpnmu==="));
- test_sequence_lower.push_back(StringPair("foob", "cpnmuog="));
- test_sequence_lower.push_back(StringPair("fooba", "cpnmuoj1"));
- test_sequence_lower.push_back(StringPair("foobar",
- "cpnmuoj1e8======"));
- }
- vector<StringPair> test_sequence;
- vector<StringPair> test_sequence_lower;
- vector<uint8_t> decoded_data;
- const string encoding_chars;
-};
-
-void
-decodeCheck(const string& input_string, vector<uint8_t>& output,
- const string& expected)
-{
- decodeBase32Hex(input_string, output);
- EXPECT_EQ(expected, string(&output[0], &output[0] + output.size()));
-}
-
-TEST_F(Base32HexTest, decode) {
- for (vector<StringPair>::const_iterator it = test_sequence.begin();
- it != test_sequence.end();
- ++it) {
- decodeCheck((*it).second, decoded_data, (*it).first);
- }
-
- // whitespace should be allowed
- decodeCheck("CP NM\tUOG=", decoded_data, "foob");
- decodeCheck("CPNMU===\n", decoded_data, "foo");
-
- // invalid number of padding characters
- EXPECT_THROW(decodeBase32Hex("CPNMU0==", decoded_data), BadValue);
- EXPECT_THROW(decodeBase32Hex("CO0=====", decoded_data), BadValue);
- EXPECT_THROW(decodeBase32Hex("CO=======", decoded_data), // too many ='s
- BadValue);
-
- // intermediate padding isn't allowed
- EXPECT_THROW(decodeBase32Hex("CPNMUOG=CPNMUOG=", decoded_data), BadValue);
-
- // Non canonical form isn't allowed.
- // P => 25(11001), so the padding byte would be 01000000
- EXPECT_THROW(decodeBase32Hex("0P======", decoded_data), BadValue);
-}
-
-TEST_F(Base32HexTest, decodeLower) {
- for (vector<StringPair>::const_iterator it = test_sequence_lower.begin();
- it != test_sequence_lower.end();
- ++it) {
- decodeCheck((*it).second, decoded_data, (*it).first);
- }
-}
-
-TEST_F(Base32HexTest, encode) {
- for (vector<StringPair>::const_iterator it = test_sequence.begin();
- it != test_sequence.end();
- ++it) {
- decoded_data.assign((*it).first.begin(), (*it).first.end());
- EXPECT_EQ((*it).second, encodeBase32Hex(decoded_data));
- }
-}
-
-// For Base32Hex we use handmade mappings, so it's prudent to test the
-// entire mapping table explicitly.
-TEST_F(Base32HexTest, decodeMap) {
- string input(8, '0'); // input placeholder
-
- // We're going to populate an input string with only the last character
- // not equal to the zero character ('0') for each valid base32hex encoding
- // character. Decoding that input should result in a data stream with
- // the last byte equal to the numeric value represented by the that
- // character. For example, we'll generate and confirm the following:
- // "00000000" => should be 0 (as a 40bit integer)
- // "00000001" => should be 1 (as a 40bit integer)
- // ...
- // "0000000V" => should be 31 (as a 40bit integer)
- // We also check the use of an invalid character for the last character
- // surely fails. '=' should be accepted as a valid padding in this
- // context; space characters shouldn't be allowed in this context.
-
- for (int i = 0; i < 256; ++i) {
- input[7] = i;
-
- const char ch = toupper(i);
- const size_t pos = encoding_chars.find(ch);
- if (pos == string::npos) {
- EXPECT_THROW(decodeBase32Hex(input, decoded_data), BadValue);
- } else {
- decodeBase32Hex(input, decoded_data);
- if (ch == '=') {
- EXPECT_EQ(4, decoded_data.size());
- } else {
- EXPECT_EQ(5, decoded_data.size());
- EXPECT_EQ(pos, decoded_data[4]);
- }
- }
- }
-}
-
-TEST_F(Base32HexTest, encodeMap) {
- for (int i = 0; i < 32; ++i) {
- decoded_data.assign(4, 0);
- decoded_data.push_back(i);
- EXPECT_EQ(encoding_chars[i], encodeBase32Hex(decoded_data)[7]);
- }
-}
-
-}
diff --git a/src/lib/dns/tests/base64_unittest.cc b/src/lib/dns/tests/base64_unittest.cc
deleted file mode 100644
index 7333793..0000000
--- a/src/lib/dns/tests/base64_unittest.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <exceptions/exceptions.h>
-
-#include <dns/util/base64.h>
-
-#include <gtest/gtest.h>
-
-using namespace std;
-using namespace isc;
-using namespace isc::dns;
-
-namespace {
-
-typedef pair<string, string> StringPair;
-
-class Base64Test : public ::testing::Test {
-protected:
- Base64Test()
- {
- // test vectors from RFC4648
- test_sequence.push_back(StringPair("", ""));
- test_sequence.push_back(StringPair("f", "Zg=="));
- test_sequence.push_back(StringPair("fo", "Zm8="));
- test_sequence.push_back(StringPair("foo", "Zm9v"));
- test_sequence.push_back(StringPair("foob", "Zm9vYg=="));
- test_sequence.push_back(StringPair("fooba", "Zm9vYmE="));
- test_sequence.push_back(StringPair("foobar", "Zm9vYmFy"));
- }
- vector<StringPair> test_sequence;
- vector<uint8_t> decoded_data;
-};
-
-void
-decodeCheck(const string& input_string, vector<uint8_t>& output,
- const string& expected)
-{
- decodeBase64(input_string, output);
- EXPECT_EQ(expected, string(&output[0], &output[0] + output.size()));
-}
-
-TEST_F(Base64Test, decode) {
- for (vector<StringPair>::const_iterator it = test_sequence.begin();
- it != test_sequence.end();
- ++it) {
- decodeCheck((*it).second, decoded_data, (*it).first);
- }
-
- // whitespace should be allowed
- decodeCheck("Zm 9v\tYmF\ny", decoded_data, "foobar");
- decodeCheck("Zm9vYg==", decoded_data, "foob");
- decodeCheck("Zm9vYmE=\n", decoded_data, "fooba");
-
- // only up to 2 padding characters are allowed
- EXPECT_THROW(decodeBase64("A===", decoded_data), BadValue);
- EXPECT_THROW(decodeBase64("A= ==", decoded_data), BadValue);
-
- // intermediate padding isn't allowed
- EXPECT_THROW(decodeBase64("YmE=YmE=", decoded_data), BadValue);
-
- // Non canonical form isn't allowed.
- // Z => 25(011001), m => 38(100110), 9 => 60(111101), so the padding
- // byte would be 0100 0000.
- EXPECT_THROW(decodeBase64("Zm9=", decoded_data), BadValue);
- // Same for the 1st padding byte. This would make it 01100000.
- EXPECT_THROW(decodeBase64("Zm==", decoded_data), BadValue);
-}
-
-TEST_F(Base64Test, encode) {
- for (vector<StringPair>::const_iterator it = test_sequence.begin();
- it != test_sequence.end();
- ++it) {
- decoded_data.assign((*it).first.begin(), (*it).first.end());
- EXPECT_EQ((*it).second, encodeBase64(decoded_data));
- }
-}
-}
diff --git a/src/lib/dns/tests/buffer_unittest.cc b/src/lib/dns/tests/buffer_unittest.cc
deleted file mode 100644
index 2ac9fc5..0000000
--- a/src/lib/dns/tests/buffer_unittest.cc
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <exceptions/exceptions.h>
-
-#include <dns/buffer.h>
-
-#include <gtest/gtest.h>
-
-using namespace isc;
-
-namespace {
-
-using isc::dns::InputBuffer;
-using isc::dns::OutputBuffer;
-
-class BufferTest : public ::testing::Test {
-protected:
- BufferTest() : ibuffer(testdata, sizeof(testdata)), obuffer(0),
- expected_size(0)
- {
- data16 = (2 << 8) | 3;
- data32 = (4 << 24) | (5 << 16) | (6 << 8) | 7;
- memset(vdata, 0, sizeof(testdata));
- }
-
- InputBuffer ibuffer;
- OutputBuffer obuffer;
- static const uint8_t testdata[5];
- uint8_t vdata[sizeof(testdata)];
- size_t expected_size;
- uint16_t data16;
- uint32_t data32;
-};
-
-const uint8_t BufferTest::testdata[5] = {1, 2, 3, 4, 5};
-
-TEST_F(BufferTest, inputBufferRead) {
- EXPECT_EQ(5, ibuffer.getLength());
- EXPECT_EQ(1, ibuffer.readUint8());
- EXPECT_EQ(1, ibuffer.getPosition());
- data16 = ibuffer.readUint16();
- EXPECT_EQ((2 << 8) | 3, data16);
- EXPECT_EQ(3, ibuffer.getPosition());
- ibuffer.setPosition(1);
- EXPECT_EQ(1, ibuffer.getPosition());
- data32 = ibuffer.readUint32();
- EXPECT_EQ((2 << 24) | (3 << 16) | (4 << 8) | 5, data32);
- ibuffer.setPosition(0);
- memset(vdata, 0, sizeof(vdata));
- ibuffer.readData(vdata, sizeof(vdata));
- EXPECT_EQ(0, memcmp(vdata, testdata, sizeof(testdata)));
-}
-
-TEST_F(BufferTest, inputBufferException) {
- EXPECT_THROW(ibuffer.setPosition(6), isc::dns::InvalidBufferPosition);
-
- ibuffer.setPosition(sizeof(testdata));
- EXPECT_THROW(ibuffer.readUint8(), isc::dns::InvalidBufferPosition);
-
- ibuffer.setPosition(sizeof(testdata) - 1);
- EXPECT_THROW(ibuffer.readUint16(), isc::dns::InvalidBufferPosition);
-
- ibuffer.setPosition(sizeof(testdata) - 3);
- EXPECT_THROW(ibuffer.readUint32(), isc::dns::InvalidBufferPosition);
-
- ibuffer.setPosition(sizeof(testdata) - 4);
- EXPECT_THROW(ibuffer.readData(vdata, sizeof(vdata)),
- isc::dns::InvalidBufferPosition);
-}
-
-TEST_F(BufferTest, outputBufferExtend) {
- EXPECT_EQ(0, obuffer.getCapacity());
- EXPECT_EQ(0, obuffer.getLength());
- obuffer.writeUint8(10);
- EXPECT_LT(0, obuffer.getCapacity());
- EXPECT_EQ(1, obuffer.getLength());
-}
-
-TEST_F(BufferTest, outputBufferWrite) {
- const uint8_t* cp;
-
- obuffer.writeUint8(1);
- expected_size += sizeof(uint8_t);
- EXPECT_EQ(expected_size, obuffer.getLength());
- cp = static_cast<const uint8_t*>(obuffer.getData());
- EXPECT_EQ(1, *cp);
-
- obuffer.writeUint16(data16);
- expected_size += sizeof(data16);
- cp = static_cast<const uint8_t*>(obuffer.getData());
- EXPECT_EQ(expected_size, obuffer.getLength());
- EXPECT_EQ(2, *(cp + 1));
- EXPECT_EQ(3, *(cp + 2));
-
- obuffer.writeUint32(data32);
- expected_size += sizeof(data32);
- cp = static_cast<const uint8_t*>(obuffer.getData());
- EXPECT_EQ(expected_size, obuffer.getLength());
- EXPECT_EQ(4, *(cp + 3));
- EXPECT_EQ(5, *(cp + 4));
- EXPECT_EQ(6, *(cp + 5));
- EXPECT_EQ(7, *(cp + 6));
-
- obuffer.writeData(testdata, sizeof(testdata));
- expected_size += sizeof(testdata);
- EXPECT_EQ(expected_size, obuffer.getLength());
- cp = static_cast<const uint8_t*>(obuffer.getData());
- EXPECT_EQ(0, memcmp(cp + 7, testdata, sizeof(testdata)));
-}
-
-TEST_F(BufferTest, outputBufferWriteat) {
- obuffer.writeUint32(data32);
- expected_size += sizeof(data32);
-
- // overwrite 2nd and 3rd bytes
- obuffer.writeUint16At(data16, 1);
- EXPECT_EQ(expected_size, obuffer.getLength()); // length shouldn't change
- const uint8_t* cp = static_cast<const uint8_t*>(obuffer.getData());
- EXPECT_EQ(2, *(cp + 1));
- EXPECT_EQ(3, *(cp + 2));
-
- // overwrite 3rd and 4th bytes
- obuffer.writeUint16At(data16, 2);
- EXPECT_EQ(expected_size, obuffer.getLength());
- cp = static_cast<const uint8_t*>(obuffer.getData());
- EXPECT_EQ(2, *(cp + 2));
- EXPECT_EQ(3, *(cp + 3));
-
- EXPECT_THROW(obuffer.writeUint16At(data16, 3),
- isc::dns::InvalidBufferPosition);
- EXPECT_THROW(obuffer.writeUint16At(data16, 4),
- isc::dns::InvalidBufferPosition);
- EXPECT_THROW(obuffer.writeUint16At(data16, 5),
- isc::dns::InvalidBufferPosition);
-}
-
-TEST_F(BufferTest, outputBufferSkip) {
- obuffer.skip(4);
- EXPECT_EQ(4, obuffer.getLength());
-
- obuffer.skip(2);
- EXPECT_EQ(6, obuffer.getLength());
-}
-
-TEST_F(BufferTest, outputBufferTrim) {
- obuffer.writeData(testdata, sizeof(testdata));
- EXPECT_EQ(5, obuffer.getLength());
-
- obuffer.trim(1);
- EXPECT_EQ(4, obuffer.getLength());
-
- obuffer.trim(2);
- EXPECT_EQ(2, obuffer.getLength());
-
- EXPECT_THROW(obuffer.trim(3), OutOfRange);
-}
-
-TEST_F(BufferTest, outputBufferReadat) {
- obuffer.writeData(testdata, sizeof(testdata));
- for (int i = 0; i < sizeof(testdata); i ++) {
- EXPECT_EQ(testdata[i], obuffer[i]);
- }
- EXPECT_THROW(obuffer[sizeof(testdata)], isc::dns::InvalidBufferPosition);
-}
-
-TEST_F(BufferTest, outputBufferClear) {
- obuffer.writeData(testdata, sizeof(testdata));
- obuffer.clear();
- EXPECT_EQ(0, obuffer.getLength());
-}
-}
diff --git a/src/lib/dns/tests/dnssectime_unittest.cc b/src/lib/dns/tests/dnssectime_unittest.cc
deleted file mode 100644
index b2708cc..0000000
--- a/src/lib/dns/tests/dnssectime_unittest.cc
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <string>
-
-#include <time.h>
-
-#include <dns/dnssectime.h>
-
-#include <gtest/gtest.h>
-
-using namespace std;
-using namespace isc::dns;
-
-// See dnssectime.cc
-namespace isc {
-namespace dns {
-namespace dnssectime {
-namespace detail {
-extern int64_t (*gettimeFunction)();
-}
-}
-}
-}
-
-namespace {
-
-class DNSSECTimeTest : public ::testing::Test {
-protected:
- ~DNSSECTimeTest() {
- dnssectime::detail::gettimeFunction = NULL;
- }
-};
-
-TEST_F(DNSSECTimeTest, fromText) {
- // In most cases (in practice) the 32-bit and 64-bit versions should
- // behave identically, so we'll mainly test the 32-bit version, which
- // will be more commonly used in actual code (because many of the wire
- // format time field are 32-bit). The subtle cases where these two
- // return different values will be tested at the end of this test case.
-
- // These are bogus and should be rejected
- EXPECT_THROW(timeFromText32("2011 101120000"), InvalidTime);
- EXPECT_THROW(timeFromText32("201101011200-0"), InvalidTime);
-
- // Short length (or "decimal integer" version of representation;
- // it's valid per RFC4034, but is not supported in this implementation)
- EXPECT_THROW(timeFromText32("20100223"), InvalidTime);
-
- // Leap year checks
- EXPECT_THROW(timeFromText32("20110229120000"), InvalidTime);
- EXPECT_THROW(timeFromText32("21000229120000"), InvalidTime);
- EXPECT_NO_THROW(timeFromText32("20000229120000"));
- EXPECT_NO_THROW(timeFromText32("20120229120000"));
-
- // unusual case: this implementation allows SS=60 for "leap seconds"
- EXPECT_NO_THROW(timeFromText32("20110101120060"));
-
- // Out of range parameters
- EXPECT_THROW(timeFromText32("19100223214617"), InvalidTime); // YY<1970
- EXPECT_THROW(timeFromText32("20110001120000"), InvalidTime); // MM=00
- EXPECT_THROW(timeFromText32("20111301120000"), InvalidTime); // MM=13
- EXPECT_THROW(timeFromText32("20110100120000"), InvalidTime); // DD=00
- EXPECT_THROW(timeFromText32("20110132120000"), InvalidTime); // DD=32
- EXPECT_THROW(timeFromText32("20110431120000"), InvalidTime); // 'Apr31'
- EXPECT_THROW(timeFromText32("20110101250000"), InvalidTime); // HH=25
- EXPECT_THROW(timeFromText32("20110101126000"), InvalidTime); // mm=60
- EXPECT_THROW(timeFromText32("20110101120061"), InvalidTime); // SS=61
-
- // Feb 7, 06:28:15 UTC 2106 is the possible maximum time that can be
- // represented as an unsigned 32bit integer without overflow.
- EXPECT_EQ(4294967295LU, timeFromText32("21060207062815"));
-
- // After that, timeFromText32() should start returning the second count
- // modulo 2^32.
- EXPECT_EQ(0, timeFromText32("21060207062816"));
- EXPECT_EQ(10, timeFromText32("21060207062826"));
-
- // On the other hand, the 64-bit version should return monotonically
- // increasing counters.
- EXPECT_EQ(4294967296LL, timeFromText64("21060207062816"));
- EXPECT_EQ(4294967306LL, timeFromText64("21060207062826"));
-}
-
-// This helper templated function tells timeToText32 a faked current time.
-// The template parameter is that faked time in the form of int64_t seconds
-// since epoch.
-template <int64_t NOW>
-int64_t
-testGetTime() {
- return (NOW);
-}
-
-// Seconds since epoch for the year 10K eve. Commonly used in some tests
-// below.
-const uint64_t YEAR10K_EVE = 253402300799LL;
-
-TEST_F(DNSSECTimeTest, toText) {
- // Check a basic case with the default (normal) gettimeFunction
- // based on the "real current time".
- // Note: this will fail after year 2078, but at that point we won't use
- // this program anyway:-)
- EXPECT_EQ("20100311233000", timeToText32(1268350200));
-
- // Set the current time to: Feb 18 09:04:14 UTC 2012 (an arbitrary choice
- // in the range of the first half of uint32 since epoch).
- dnssectime::detail::gettimeFunction = testGetTime<1329555854LL>;
-
- // Test the "year 2038" problem.
- // Check the result of toText() for "INT_MIN" in int32_t. It's in the
- // 68-year range from the faked current time, so the result should be
- // in year 2038, instead of 1901.
- EXPECT_EQ("20380119031408", timeToText64(0x80000000L));
- EXPECT_EQ("20380119031408", timeToText32(0x80000000L));
-
- // A controversial case: what should we do with "-1"? It's out of range
- // in future, but according to RFC time before epoch doesn't seem to be
- // considered "in-range" either. Our toText() implementation handles
- // this range as a special case and always treats them as future time
- // until year 2038. This won't be a real issue in practice, though,
- // since such too large values won't be used in actual deployment by then.
- EXPECT_EQ("21060207062815", timeToText32(0xffffffffL));
-
- // After the singular point of year 2038, the first half of uint32 can
- // point to a future time.
- // Set the current time to: Apr 1 00:00:00 UTC 2038:
- dnssectime::detail::gettimeFunction = testGetTime<2153692800LL>;
- // then time "10" is Feb 7 06:28:26 UTC 2106
- EXPECT_EQ("21060207062826", timeToText32(10));
- // in 64-bit, it's 2^32 + 10
- EXPECT_EQ("21060207062826", timeToText64(0x10000000aLL));
-
- // After year 2106, the upper half of uint32 can point to past time
- // (as it should).
- dnssectime::detail::gettimeFunction = testGetTime<0x10000000aLL>;
- EXPECT_EQ("21060207062815", timeToText32(0xffffffffL));
-
- // Try very large time value. Actually it's the possible farthest time
- // that can be represented in the form of YYYYMMDDHHmmSS.
- EXPECT_EQ("99991231235959", timeToText64(YEAR10K_EVE));
- dnssectime::detail::gettimeFunction = testGetTime<YEAR10K_EVE - 10>;
- EXPECT_EQ("99991231235959", timeToText32(4294197631LU));
-}
-
-TEST_F(DNSSECTimeTest, overflow) {
- // Jan 1, Year 10,000.
- EXPECT_THROW(timeToText64(253402300800LL), InvalidTime);
- dnssectime::detail::gettimeFunction = testGetTime<YEAR10K_EVE - 10>;
- EXPECT_THROW(timeToText32(4294197632LU), InvalidTime);
-}
-
-}
diff --git a/src/lib/dns/tests/edns_unittest.cc b/src/lib/dns/tests/edns_unittest.cc
index fb9a7c2..26cc01c 100644
--- a/src/lib/dns/tests/edns_unittest.cc
+++ b/src/lib/dns/tests/edns_unittest.cc
@@ -16,7 +16,7 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/edns.h>
#include <dns/exceptions.h>
#include <dns/message.h>
@@ -35,6 +35,7 @@
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
const uint8_t EDNS::SUPPORTED_VERSION;
diff --git a/src/lib/dns/tests/hex_unittest.cc b/src/lib/dns/tests/hex_unittest.cc
deleted file mode 100644
index 6f82b17..0000000
--- a/src/lib/dns/tests/hex_unittest.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <stdint.h>
-
-#include <vector>
-#include <string>
-
-#include <exceptions/exceptions.h>
-
-#include <dns/util/hex.h>
-
-#include <gtest/gtest.h>
-
-using namespace std;
-using namespace isc;
-using namespace isc::dns;
-
-namespace {
-const string hex_txt("DEADBEEFDECADE");
-const string hex_txt_space("DEAD BEEF DECADE");
-const string hex_txt_lower("deadbeefdecade");
-
-class HexTest : public ::testing::Test {
-protected:
- HexTest() : encoding_chars("0123456789ABCDEF") {}
- vector<uint8_t> decoded_data;
- const string encoding_chars;
-};
-
-TEST_F(HexTest, encodeHex) {
- std::vector<uint8_t> data;
-
- data.push_back(0xde);
- data.push_back(0xad);
- data.push_back(0xbe);
- data.push_back(0xef);
- data.push_back(0xde);
- data.push_back(0xca);
- data.push_back(0xde);
- EXPECT_EQ(hex_txt, encodeHex(data));
-}
-
-void
-compareData(const std::vector<uint8_t>& data) {
- EXPECT_EQ(0xde, data[0]);
- EXPECT_EQ(0xad, data[1]);
- EXPECT_EQ(0xbe, data[2]);
- EXPECT_EQ(0xef, data[3]);
- EXPECT_EQ(0xde, data[4]);
- EXPECT_EQ(0xca, data[5]);
- EXPECT_EQ(0xde, data[6]);
-}
-
-TEST_F(HexTest, decodeHex) {
- std::vector<uint8_t> result;
-
- decodeHex(hex_txt, result);
- compareData(result);
-
- // lower case hex digits should be accepted
- result.clear();
- decodeHex(hex_txt_lower, result);
- compareData(result);
-
- // white space should be ignored
- result.clear();
- decodeHex(hex_txt_space, result);
- compareData(result);
-
- // Bogus input: should fail
- result.clear();
- EXPECT_THROW(decodeHex("1x", result), BadValue);
-
- // Bogus input: encoded string must have an even number of characters.
- result.clear();
- EXPECT_THROW(decodeHex("dea", result), BadValue);
-}
-
-// For Hex encode/decode we use handmade mappings, so it's prudent to test the
-// entire mapping table explicitly.
-TEST_F(HexTest, decodeMap) {
- string input("00"); // input placeholder
-
- // See Base32HexTest.decodeMap for details of the following tests.
- for (int i = 0; i < 256; ++i) {
- input[1] = i;
-
- const char ch = toupper(i);
- const size_t pos = encoding_chars.find(ch);
- if (pos == string::npos) {
- EXPECT_THROW(decodeHex(input, decoded_data), BadValue);
- } else {
- decodeHex(input, decoded_data);
- EXPECT_EQ(1, decoded_data.size());
- EXPECT_EQ(pos, decoded_data[0]);
- }
- }
-}
-
-TEST_F(HexTest, encodeMap) {
- for (int i = 0; i < 16; ++i) {
- decoded_data.clear();
- decoded_data.push_back(i);
- EXPECT_EQ(encoding_chars[i], encodeHex(decoded_data)[1]);
- }
-}
-
-}
diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc
index 92adbc9..c79ea2c 100644
--- a/src/lib/dns/tests/message_unittest.cc
+++ b/src/lib/dns/tests/message_unittest.cc
@@ -12,9 +12,18 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <fstream>
+
+#include <boost/scoped_ptr.hpp>
+
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
+#include <util/time_utilities.h>
+
+#include <util/unittests/testdata.h>
+#include <util/unittests/textdata.h>
+
#include <dns/edns.h>
#include <dns/exceptions.h>
#include <dns/message.h>
@@ -26,6 +35,8 @@
#include <dns/rrclass.h>
#include <dns/rrttl.h>
#include <dns/rrtype.h>
+#include <dns/tsig.h>
+#include <dns/tsigkey.h>
#include <gtest/gtest.h>
@@ -35,6 +46,7 @@ using isc::UnitTestUtil;
using namespace std;
using namespace isc;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
//
@@ -52,6 +64,18 @@ using namespace isc::dns::rdata;
const uint16_t Message::DEFAULT_MAX_UDPSIZE;
const Name test_name("test.example.com");
+namespace isc {
+namespace util {
+namespace detail {
+extern int64_t (*gettimeFunction)();
+}
+}
+}
+
+// XXX: this is defined as class static constants, but some compilers
+// seemingly cannot find the symbol when used in the EXPECT_xxx macros.
+const uint16_t TSIGContext::DEFAULT_FUDGE;
+
namespace {
class MessageTest : public ::testing::Test {
protected:
@@ -59,7 +83,9 @@ protected:
message_parse(Message::PARSE),
message_render(Message::RENDER),
bogus_section(static_cast<Message::Section>(
- Message::SECTION_ADDITIONAL + 1))
+ Message::SECTION_ADDITIONAL + 1)),
+ tsig_ctx(TSIGKey("www.example.com:"
+ "SFuWd/q99SzF8Yzd1QbB9g=="))
{
rrset_a = RRsetPtr(new RRset(test_name, RRClass::IN(),
RRType::A(), RRTTL(3600)));
@@ -87,6 +113,9 @@ protected:
RRsetPtr rrset_a; // A RRset with two RDATAs
RRsetPtr rrset_aaaa; // AAAA RRset with one RDATA with RRSIG
RRsetPtr rrset_rrsig; // RRSIG for the AAAA RRset
+ TSIGContext tsig_ctx;
+ vector<unsigned char> expected_data;
+
static void factoryFromFile(Message& message, const char* datafile);
};
@@ -165,6 +194,70 @@ TEST_F(MessageTest, setEDNS) {
EXPECT_EQ(edns, message_render.getEDNS());
}
+TEST_F(MessageTest, fromWireWithTSIG) {
+ // Initially there should be no TSIG
+ EXPECT_EQ(static_cast<void*>(NULL), message_parse.getTSIGRecord());
+
+ // getTSIGRecord() is only valid in the parse mode.
+ EXPECT_THROW(message_render.getTSIGRecord(), InvalidMessageOperation);
+
+ factoryFromFile(message_parse, "message_toWire2.wire");
+ const char expected_mac[] = {
+ 0x22, 0x70, 0x26, 0xad, 0x29, 0x7b, 0xee, 0xe7,
+ 0x21, 0xce, 0x6c, 0x6f, 0xff, 0x1e, 0x9e, 0xf3
+ };
+ const TSIGRecord* tsig_rr = message_parse.getTSIGRecord();
+ ASSERT_NE(static_cast<void*>(NULL), tsig_rr);
+ EXPECT_EQ(Name("www.example.com"), tsig_rr->getName());
+ EXPECT_EQ(85, tsig_rr->getLength()); // see TSIGRecordTest.getLength
+ 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));
+ EXPECT_EQ(0, tsig_rr->getRdata().getError());
+ EXPECT_EQ(0, tsig_rr->getRdata().getOtherLen());
+ EXPECT_EQ(static_cast<void*>(NULL), tsig_rr->getRdata().getOtherData());
+
+ // If we clear the message for reuse, the recorded TSIG will be cleared.
+ message_parse.clear(Message::PARSE);
+ EXPECT_EQ(static_cast<void*>(NULL), message_parse.getTSIGRecord());
+}
+
+TEST_F(MessageTest, fromWireWithTSIGCompressed) {
+ // Mostly same as fromWireWithTSIG, but the TSIG owner name is compressed.
+ factoryFromFile(message_parse, "message_fromWire12.wire");
+ const TSIGRecord* tsig_rr = message_parse.getTSIGRecord();
+ ASSERT_NE(static_cast<void*>(NULL), tsig_rr);
+ EXPECT_EQ(Name("www.example.com"), tsig_rr->getName());
+ // len(www.example.com) = 17, but when fully compressed, the length is
+ // 2 bytes. So the length of the record should be 15 bytes shorter.
+ EXPECT_EQ(70, tsig_rr->getLength());
+}
+
+TEST_F(MessageTest, fromWireWithBadTSIG) {
+ // Multiple TSIG RRs
+ EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire13.wire"),
+ DNSMessageFORMERR);
+ message_parse.clear(Message::PARSE);
+
+ // TSIG in the answer section (must be in additional)
+ EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire14.wire"),
+ DNSMessageFORMERR);
+ message_parse.clear(Message::PARSE);
+
+ // TSIG is not the last record.
+ EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire15.wire"),
+ DNSMessageFORMERR);
+ message_parse.clear(Message::PARSE);
+
+ // Unexpected RR Class (this will fail in constructing TSIGRecord)
+ EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire16.wire"),
+ DNSMessageFORMERR);
+}
+
TEST_F(MessageTest, getRRCount) {
// by default all counters should be 0
EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_QUESTION));
@@ -518,6 +611,65 @@ TEST_F(MessageTest, toWireInParseMode) {
EXPECT_THROW(message_parse.toWire(renderer), InvalidMessageOperation);
}
+// See dnssectime_unittest.cc
+template <int64_t NOW>
+int64_t
+testGetTime() {
+ return (NOW);
+}
+
+void
+commonTSIGToWireCheck(Message& message, MessageRenderer& renderer,
+ TSIGContext& tsig_ctx, const char* const expected_file)
+{
+ message.setOpcode(Opcode::QUERY());
+ message.setRcode(Rcode::NOERROR());
+ message.setHeaderFlag(Message::HEADERFLAG_RD, true);
+ message.addQuestion(Question(Name("www.example.com"), RRClass::IN(),
+ RRType::A()));
+
+ 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());
+}
+
+TEST_F(MessageTest, toWireWithTSIG) {
+ // Rendering a message with TSIG. Various special cases specific to
+ // TSIG are tested in the tsig tests. We only check the message contains
+ // a TSIG at the end and the ARCOUNT of the header is updated.
+
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+ message_render.setQid(0x2d65);
+
+ {
+ SCOPED_TRACE("Message sign with TSIG");
+ commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
+ "message_toWire2.wire");
+ }
+}
+
+TEST_F(MessageTest, toWireWithEDNSAndTSIG) {
+ // Similar to the previous test, but with an EDNS before TSIG.
+ // The wire data check will confirm the ordering.
+ isc::util::detail::gettimeFunction = testGetTime<0x4db60d1f>;
+
+ message_render.setQid(0x6cd);
+
+ EDNSPtr edns(new EDNS());
+ edns->setUDPSize(4096);
+ message_render.setEDNS(edns);
+
+ {
+ SCOPED_TRACE("Message sign with TSIG and EDNS");
+ commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
+ "message_toWire3.wire");
+ }
+}
+
TEST_F(MessageTest, toWireWithoutOpcode) {
message_render.setRcode(Rcode::NOERROR());
EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation);
@@ -528,6 +680,45 @@ TEST_F(MessageTest, toWireWithoutRcode) {
EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation);
}
+TEST_F(MessageTest, toText) {
+ // Check toText() output for a typical DNS response with records in
+ // all sections
+
+ factoryFromFile(message_parse, "message_toText1.wire");
+ {
+ SCOPED_TRACE("Message toText test (basic case)");
+ ifstream ifs;
+ unittests::openTestData("message_toText1.txt", ifs);
+ unittests::matchTextData(ifs, message_parse.toText());
+ }
+
+ // Another example with EDNS. The expected data was slightly modified
+ // from the dig output (other than replacing tabs with a space): adding
+ // a newline after the "OPT PSEUDOSECTION". This is an intentional change
+ // in our version for better readability.
+ message_parse.clear(Message::PARSE);
+ factoryFromFile(message_parse, "message_toText2.wire");
+ {
+ SCOPED_TRACE("Message toText test with EDNS");
+ ifstream ifs;
+ unittests::openTestData("message_toText2.txt", ifs);
+ unittests::matchTextData(ifs, message_parse.toText());
+ }
+
+ // Another example with TSIG. The expected data was slightly modified
+ // from the dig output (other than replacing tabs with a space): removing
+ // a redundant white space at the end of TSIG RDATA. We'd rather consider
+ // it a dig's defect than a feature.
+ message_parse.clear(Message::PARSE);
+ factoryFromFile(message_parse, "message_toText3.wire");
+ {
+ SCOPED_TRACE("Message toText test with TSIG");
+ ifstream ifs;
+ unittests::openTestData("message_toText3.txt", ifs);
+ unittests::matchTextData(ifs, message_parse.toText());
+ }
+}
+
TEST_F(MessageTest, toTextWithoutOpcode) {
message_render.setRcode(Rcode::NOERROR());
EXPECT_THROW(message_render.toText(), InvalidMessageOperation);
diff --git a/src/lib/dns/tests/messagerenderer_unittest.cc b/src/lib/dns/tests/messagerenderer_unittest.cc
index c3d3edb..fe790fe 100644
--- a/src/lib/dns/tests/messagerenderer_unittest.cc
+++ b/src/lib/dns/tests/messagerenderer_unittest.cc
@@ -14,7 +14,7 @@
#include <vector>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
@@ -23,9 +23,9 @@
#include <gtest/gtest.h>
using isc::UnitTestUtil;
-using isc::dns::OutputBuffer;
using isc::dns::Name;
using isc::dns::MessageRenderer;
+using isc::util::OutputBuffer;
namespace {
class MessageRendererTest : public ::testing::Test {
diff --git a/src/lib/dns/tests/name_unittest.cc b/src/lib/dns/tests/name_unittest.cc
index 5daba9c..6434229 100644
--- a/src/lib/dns/tests/name_unittest.cc
+++ b/src/lib/dns/tests/name_unittest.cc
@@ -19,7 +19,7 @@
#include <limits>
#include <stdexcept>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
@@ -31,6 +31,7 @@
using namespace std;
using namespace isc;
using namespace isc::dns;
+using namespace isc::util;
//
// XXX: these are defined as class static constants, but some compilers
diff --git a/src/lib/dns/tests/question_unittest.cc b/src/lib/dns/tests/question_unittest.cc
index 59a4815..25fd75b 100644
--- a/src/lib/dns/tests/question_unittest.cc
+++ b/src/lib/dns/tests/question_unittest.cc
@@ -17,7 +17,7 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/name.h>
@@ -32,6 +32,7 @@
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
namespace {
class QuestionTest : public ::testing::Test {
diff --git a/src/lib/dns/tests/rdata_cname_unittest.cc b/src/lib/dns/tests/rdata_cname_unittest.cc
index e3137a7..d6b02aa 100644
--- a/src/lib/dns/tests/rdata_cname_unittest.cc
+++ b/src/lib/dns/tests/rdata_cname_unittest.cc
@@ -12,7 +12,7 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
@@ -28,6 +28,7 @@
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
diff --git a/src/lib/dns/tests/rdata_dname_unittest.cc b/src/lib/dns/tests/rdata_dname_unittest.cc
index c2384b6..ebd9e0e 100644
--- a/src/lib/dns/tests/rdata_dname_unittest.cc
+++ b/src/lib/dns/tests/rdata_dname_unittest.cc
@@ -12,7 +12,7 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
@@ -28,6 +28,7 @@
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
diff --git a/src/lib/dns/tests/rdata_dnskey_unittest.cc b/src/lib/dns/tests/rdata_dnskey_unittest.cc
index e26bf57..f0596ed 100644
--- a/src/lib/dns/tests/rdata_dnskey_unittest.cc
+++ b/src/lib/dns/tests/rdata_dnskey_unittest.cc
@@ -16,7 +16,7 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
@@ -32,6 +32,7 @@ using isc::UnitTestUtil;
using namespace std;
using namespace isc;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
diff --git a/src/lib/dns/tests/rdata_ds_unittest.cc b/src/lib/dns/tests/rdata_ds_unittest.cc
index d7e3f88..5988620 100644
--- a/src/lib/dns/tests/rdata_ds_unittest.cc
+++ b/src/lib/dns/tests/rdata_ds_unittest.cc
@@ -14,7 +14,7 @@
#include <string>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
@@ -29,6 +29,7 @@
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
diff --git a/src/lib/dns/tests/rdata_in_a_unittest.cc b/src/lib/dns/tests/rdata_in_a_unittest.cc
index 7302881..47e2bfa 100644
--- a/src/lib/dns/tests/rdata_in_a_unittest.cc
+++ b/src/lib/dns/tests/rdata_in_a_unittest.cc
@@ -12,7 +12,7 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
@@ -28,6 +28,7 @@
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
diff --git a/src/lib/dns/tests/rdata_in_aaaa_unittest.cc b/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
index c1953d6..6fd4d0e 100644
--- a/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
+++ b/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
@@ -12,7 +12,7 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
@@ -28,6 +28,7 @@
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
diff --git a/src/lib/dns/tests/rdata_mx_unittest.cc b/src/lib/dns/tests/rdata_mx_unittest.cc
index dd7677d..c4c9757 100644
--- a/src/lib/dns/tests/rdata_mx_unittest.cc
+++ b/src/lib/dns/tests/rdata_mx_unittest.cc
@@ -12,7 +12,7 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
@@ -27,6 +27,7 @@
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
diff --git a/src/lib/dns/tests/rdata_ns_unittest.cc b/src/lib/dns/tests/rdata_ns_unittest.cc
index 6d4a69e..b805783 100644
--- a/src/lib/dns/tests/rdata_ns_unittest.cc
+++ b/src/lib/dns/tests/rdata_ns_unittest.cc
@@ -12,7 +12,7 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
@@ -28,6 +28,7 @@
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
diff --git a/src/lib/dns/tests/rdata_nsec3_unittest.cc b/src/lib/dns/tests/rdata_nsec3_unittest.cc
index 749e262..441c6d8 100644
--- a/src/lib/dns/tests/rdata_nsec3_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec3_unittest.cc
@@ -16,9 +16,9 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
+#include <util/encode/hex.h>
#include <dns/exceptions.h>
-#include <dns/util/hex.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
@@ -34,6 +34,8 @@ 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;
namespace {
diff --git a/src/lib/dns/tests/rdata_nsec3param_unittest.cc b/src/lib/dns/tests/rdata_nsec3param_unittest.cc
index 53e9126..8d802d6 100644
--- a/src/lib/dns/tests/rdata_nsec3param_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec3param_unittest.cc
@@ -16,9 +16,9 @@
#include <exceptions/exceptions.h>
-#include <dns/util/base32hex.h>
-#include <dns/buffer.h>
-#include <dns/util/hex.h>
+#include <util/encode/base32hex.h>
+#include <util/encode/hex.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
@@ -34,6 +34,8 @@ 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;
namespace {
diff --git a/src/lib/dns/tests/rdata_nsec_unittest.cc b/src/lib/dns/tests/rdata_nsec_unittest.cc
index 8286dee..5aa1e9c 100644
--- a/src/lib/dns/tests/rdata_nsec_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec_unittest.cc
@@ -14,7 +14,7 @@
#include <string>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
@@ -30,6 +30,7 @@
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
diff --git a/src/lib/dns/tests/rdata_opt_unittest.cc b/src/lib/dns/tests/rdata_opt_unittest.cc
index be92b91..698d586 100644
--- a/src/lib/dns/tests/rdata_opt_unittest.cc
+++ b/src/lib/dns/tests/rdata_opt_unittest.cc
@@ -12,7 +12,7 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
@@ -27,6 +27,7 @@
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
diff --git a/src/lib/dns/tests/rdata_ptr_unittest.cc b/src/lib/dns/tests/rdata_ptr_unittest.cc
index da13dcb..7f5de20 100644
--- a/src/lib/dns/tests/rdata_ptr_unittest.cc
+++ b/src/lib/dns/tests/rdata_ptr_unittest.cc
@@ -12,7 +12,7 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
@@ -28,6 +28,7 @@
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
//
diff --git a/src/lib/dns/tests/rdata_rp_unittest.cc b/src/lib/dns/tests/rdata_rp_unittest.cc
new file mode 100644
index 0000000..07f5a93
--- /dev/null
+++ b/src/lib/dns/tests/rdata_rp_unittest.cc
@@ -0,0 +1,163 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <util/buffer.h>
+#include <dns/exceptions.h>
+#include <dns/rdataclass.h>
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace isc::util;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace {
+class Rdata_RP_Test : public RdataTest {
+protected:
+ Rdata_RP_Test() :
+ mailbox_name("root.example.com."),
+ text_name("rp-text.example.com."),
+ // this also serves as a test for "from text" constructor in a normal
+ // case.
+ rdata_rp("root.example.com. rp-text.example.com."),
+ obuffer(0), renderer(obuffer)
+ {}
+
+ const Name mailbox_name, text_name;
+ const generic::RP rdata_rp; // commonly used test RDATA
+ OutputBuffer obuffer;
+ MessageRenderer renderer;
+ vector<uint8_t> expected_wire;
+};
+
+TEST_F(Rdata_RP_Test, createFromText) {
+ EXPECT_EQ(mailbox_name, rdata_rp.getMailbox());
+ EXPECT_EQ(text_name, rdata_rp.getText());
+
+ // Invalid textual input cases follow:
+ // names are invalid
+ EXPECT_THROW(generic::RP("bad..name. rp-text.example.com"), EmptyLabel);
+ EXPECT_THROW(generic::RP("mailbox.example.com. bad..name"), EmptyLabel);
+
+ // missing field
+ EXPECT_THROW(generic::RP("mailbox.example.com."), InvalidRdataText);
+
+ // redundant field
+ EXPECT_THROW(generic::RP("mailbox.example.com. rp-text.example.com. "
+ "redundant.example."), InvalidRdataText);
+}
+
+TEST_F(Rdata_RP_Test, createFromWire) {
+ RdataPtr rdata(rdataFactoryFromFile(RRType::RP(), RRClass::IN(),
+ "rdata_rp_fromWire1.wire"));
+ EXPECT_EQ(mailbox_name, dynamic_cast<generic::RP&>(*rdata).getMailbox());
+ EXPECT_EQ(text_name, dynamic_cast<generic::RP&>(*rdata).getText());
+
+ // a similar test with names being compressed
+ rdata = rdataFactoryFromFile(RRType::RP(), RRClass::IN(),
+ "rdata_rp_fromWire2.wire", 30);
+ EXPECT_EQ(mailbox_name, dynamic_cast<generic::RP&>(*rdata).getMailbox());
+ EXPECT_EQ(Name("rp-text.example.net"),
+ dynamic_cast<generic::RP&>(*rdata).getText());
+}
+
+TEST_F(Rdata_RP_Test, badFromWire) {
+ // RDLEN is too short
+ EXPECT_THROW(rdataFactoryFromFile(RRType::RP(), RRClass::IN(),
+ "rdata_rp_fromWire3.wire"),
+ InvalidRdataLength);
+
+ // RDLEN is too long
+ EXPECT_THROW(rdataFactoryFromFile(RRType::RP(), RRClass::IN(),
+ "rdata_rp_fromWire4.wire"),
+ InvalidRdataLength);
+
+ // bogus mailbox name
+ EXPECT_THROW(rdataFactoryFromFile(RRType::RP(), RRClass::IN(),
+ "rdata_rp_fromWire5.wire"),
+ DNSMessageFORMERR);
+
+ // bogus text name
+ EXPECT_THROW(rdataFactoryFromFile(RRType::RP(), RRClass::IN(),
+ "rdata_rp_fromWire6.wire"),
+ DNSMessageFORMERR);
+}
+
+TEST_F(Rdata_RP_Test, createFromParams) {
+ EXPECT_EQ(mailbox_name, generic::RP(mailbox_name, text_name).getMailbox());
+ EXPECT_EQ(text_name, generic::RP(mailbox_name, text_name).getText());
+}
+
+TEST_F(Rdata_RP_Test, toWireBuffer) {
+ // construct expected data
+ UnitTestUtil::readWireData("rdata_rp_toWire1.wire", expected_wire);
+
+ // construct actual data
+ rdata_rp.toWire(obuffer);
+
+ // then compare them
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+ obuffer.getData(), obuffer.getLength(),
+ &expected_wire[0], expected_wire.size());
+}
+
+TEST_F(Rdata_RP_Test, toWireRenderer) {
+ // similar to toWireBuffer, but names in RDATA could be compressed due to
+ // preceding names. Actually they must not be compressed according to
+ // RFC3597, and this test checks that.
+
+ UnitTestUtil::readWireData("rdata_rp_toWire2.wire", expected_wire);
+
+ 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());
+}
+
+TEST_F(Rdata_RP_Test, toText) {
+ // there's not much to test for this method. Only checking a simple case.
+ EXPECT_EQ("root.example.com. rp-text.example.com.", rdata_rp.toText());
+}
+
+TEST_F(Rdata_RP_Test, compare) {
+ // check reflexivity
+ EXPECT_EQ(0, rdata_rp.compare(rdata_rp));
+
+ // names must be compared in case-insensitive manner
+ EXPECT_EQ(0, rdata_rp.compare(generic::RP("ROOT.example.com. "
+ "rp-text.EXAMPLE.com.")));
+
+ // another RP whose mailbox name is larger than that of rdata_rp.
+ const generic::RP large1_rp("zzzz.example.com. rp-text.example.com.");
+ EXPECT_GT(0, rdata_rp.compare(large1_rp));
+ EXPECT_LT(0, large1_rp.compare(rdata_rp));
+
+ // yet another RP whose text name is larger than that of rdata_rp.
+ const generic::RP large2_rp("root.example.com. zzzzzzz.example.com.");
+ EXPECT_GT(0, rdata_rp.compare(large2_rp));
+ EXPECT_LT(0, large2_rp.compare(rdata_rp));
+
+ // comparison attempt between incompatible RR types should be rejected
+ EXPECT_THROW(rdata_rp.compare(*RdataTest::rdata_nomatch), bad_cast);
+}
+}
diff --git a/src/lib/dns/tests/rdata_rrsig_unittest.cc b/src/lib/dns/tests/rdata_rrsig_unittest.cc
index 04d9469..903021f 100644
--- a/src/lib/dns/tests/rdata_rrsig_unittest.cc
+++ b/src/lib/dns/tests/rdata_rrsig_unittest.cc
@@ -14,8 +14,8 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
-#include <dns/dnssectime.h>
+#include <util/buffer.h>
+#include <util/time_utilities.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
@@ -31,6 +31,7 @@ using isc::UnitTestUtil;
using namespace std;
using namespace isc;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
diff --git a/src/lib/dns/tests/rdata_soa_unittest.cc b/src/lib/dns/tests/rdata_soa_unittest.cc
index 6858a0b..63fe1f7 100644
--- a/src/lib/dns/tests/rdata_soa_unittest.cc
+++ b/src/lib/dns/tests/rdata_soa_unittest.cc
@@ -12,7 +12,7 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
@@ -27,6 +27,7 @@
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
diff --git a/src/lib/dns/tests/rdata_tsig_unittest.cc b/src/lib/dns/tests/rdata_tsig_unittest.cc
index 5c9a14f..76f91a6 100644
--- a/src/lib/dns/tests/rdata_tsig_unittest.cc
+++ b/src/lib/dns/tests/rdata_tsig_unittest.cc
@@ -16,7 +16,7 @@
#include <exceptions/exceptions.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
@@ -32,6 +32,7 @@
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
diff --git a/src/lib/dns/tests/rdata_txt_unittest.cc b/src/lib/dns/tests/rdata_txt_unittest.cc
index 54993e1..e5f8ac9 100644
--- a/src/lib/dns/tests/rdata_txt_unittest.cc
+++ b/src/lib/dns/tests/rdata_txt_unittest.cc
@@ -12,7 +12,7 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
@@ -28,6 +28,7 @@
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
diff --git a/src/lib/dns/tests/rdata_unittest.cc b/src/lib/dns/tests/rdata_unittest.cc
index 5ce4c03..fa791dc 100644
--- a/src/lib/dns/tests/rdata_unittest.cc
+++ b/src/lib/dns/tests/rdata_unittest.cc
@@ -16,7 +16,7 @@
#include <string>
#include <sstream>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
@@ -31,6 +31,7 @@
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace isc {
diff --git a/src/lib/dns/tests/rdata_unittest.h b/src/lib/dns/tests/rdata_unittest.h
index 748c8d3..1bc0fa4 100644
--- a/src/lib/dns/tests/rdata_unittest.h
+++ b/src/lib/dns/tests/rdata_unittest.h
@@ -15,7 +15,7 @@
#ifndef __RDATA_UNITTEST_H
#define __RDATA_UNITTEST_H 1
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
@@ -23,6 +23,8 @@
#include <gtest/gtest.h>
+using namespace isc::util;
+
namespace isc {
namespace dns {
namespace rdata {
diff --git a/src/lib/dns/tests/rdatafields_unittest.cc b/src/lib/dns/tests/rdatafields_unittest.cc
new file mode 100644
index 0000000..d619220
--- /dev/null
+++ b/src/lib/dns/tests/rdatafields_unittest.cc
@@ -0,0 +1,380 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <vector>
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatafields.h>
+#include <dns/tests/unittest_util.h>
+
+#include <gtest/gtest.h>
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using isc::util::OutputBuffer;
+using isc::util::InputBuffer;
+
+namespace {
+class RdataFieldsTest : public ::testing::Test {
+protected:
+ RdataFieldsTest() : obuffer(0), renderer_buffer(0),
+ renderer(renderer_buffer),
+ ns_name("example.com"),
+ other_name("www.example.com")
+ {}
+ void constructCommonTests(const RdataFields& fields,
+ const uint8_t* const expected_data,
+ const size_t expected_data_len);
+ void constructCommonTestsNS(const RdataFields& fields);
+ void constructCommonTestsTXT(const RdataFields& fields);
+ void constructCommonTestsRRSIG(const RdataFields& fields);
+ void constructCommonTestsOPT(const RdataFields& fields);
+ OutputBuffer obuffer;
+ OutputBuffer renderer_buffer;
+ MessageRenderer renderer;
+ const Name ns_name;
+ const Name other_name;
+ vector<unsigned char> expected_wire;
+ vector<unsigned char> fields_wire;
+};
+
+const uint8_t in_a_data[] = { 192, 0, 2, 1 };
+// binary representation of example.com.
+const uint8_t ns_data[] = { 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00 };
+
+//
+// IN/A RDATA: fixed length, single data field
+//
+void
+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());
+ 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());
+
+ fields.toWire(renderer);
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, expected_data,
+ expected_data_len, renderer.getData(),
+ renderer.getLength());
+}
+
+TEST_F(RdataFieldsTest, constructFromRdata) {
+ const RdataFields fields(in::A("192.0.2.1"));
+ constructCommonTests(fields, in_a_data, sizeof(in_a_data));
+}
+
+TEST_F(RdataFieldsTest, constructFromParams) {
+ const RdataFields::FieldSpec spec(RdataFields::DATA, 4);
+ const RdataFields fields(&spec, sizeof(spec), in_a_data,
+ sizeof(in_a_data));
+ constructCommonTests(fields, in_a_data, sizeof(in_a_data));
+}
+
+//
+// NS RDATA: containing a compressible name.
+//
+void
+RdataFieldsTest::constructCommonTestsNS(const RdataFields& fields) {
+ EXPECT_EQ(sizeof(RdataFields::FieldSpec), fields.getFieldSpecDataSize());
+ EXPECT_EQ(1, fields.getFieldCount());
+ EXPECT_EQ(RdataFields::COMPRESSIBLE_NAME, fields.getFieldSpec(0).type);
+ EXPECT_EQ(ns_name.getLength(), fields.getFieldSpec(0).len);
+
+ expected_wire.clear();
+ 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());
+
+ 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());
+}
+
+TEST_F(RdataFieldsTest, constructFromRdataNS) {
+ const RdataFields fields_ns((generic::NS(ns_name)));
+ constructCommonTestsNS(fields_ns);
+}
+
+TEST_F(RdataFieldsTest, constructFromParamsNS) {
+ const RdataFields::FieldSpec spec(RdataFields::COMPRESSIBLE_NAME,
+ sizeof(ns_data));
+ const RdataFields fields_ns(&spec, sizeof(spec), ns_data, sizeof(ns_data));
+ constructCommonTestsNS(fields_ns);
+}
+
+//
+// TXT RDATA: multiple fields, lengths vary
+//
+void
+RdataFieldsTest::constructCommonTestsTXT(const RdataFields& fields) {
+ // Since all fields are plain data, they are handled as a single data
+ // field.
+ EXPECT_EQ(sizeof(RdataFields::FieldSpec), fields.getFieldSpecDataSize());
+ EXPECT_EQ(1, fields.getFieldCount());
+ EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(0).type);
+ 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());
+
+ fields.toWire(renderer);
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
+ expected_wire.size(), renderer.getData(),
+ renderer.getLength());
+}
+
+TEST_F(RdataFieldsTest, constructFromRdataTXT) {
+ UnitTestUtil::readWireData("rdatafields3.wire", expected_wire);
+ InputBuffer ibuffer(&expected_wire[0], expected_wire.size());
+ const uint16_t rdlen = ibuffer.readUint16();
+ const RdataFields fields(generic::TXT(ibuffer, rdlen));
+
+ // drop the RDLEN part
+ expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2);
+
+ constructCommonTestsTXT(fields);
+}
+
+TEST_F(RdataFieldsTest, constructFromParamsTXT) {
+ UnitTestUtil::readWireData("rdatafields3.wire", expected_wire);
+ expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2);
+ const RdataFields::FieldSpec spec(RdataFields::DATA, expected_wire.size());
+ const RdataFields fields(&spec, sizeof(spec), &expected_wire[0],
+ expected_wire.size());
+ constructCommonTestsTXT(fields);
+}
+
+//
+// RRSIG: multiple fields, with an incompressible name
+//
+void
+RdataFieldsTest::constructCommonTestsRRSIG(const RdataFields& fields) {
+ // In terms of RdataFields RRSIG RDATA consists of 3 fields:
+ // - 18-byte data field (from the "type covered" field to "key tag" field)
+ // - an incompressible name field (for the signer's name field).
+ // this is a variable length field. In this test it's a 13-byte field.
+ // - a variable-length data field for the signature. In this tests
+ // it's a 15-byte field.
+ EXPECT_EQ(3 * sizeof(RdataFields::FieldSpec),
+ fields.getFieldSpecDataSize());
+ EXPECT_EQ(3, fields.getFieldCount());
+ EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(0).type);
+ EXPECT_EQ(18, fields.getFieldSpec(0).len);
+ EXPECT_EQ(RdataFields::INCOMPRESSIBLE_NAME, fields.getFieldSpec(1).type);
+ EXPECT_EQ(13, fields.getFieldSpec(1).len);
+ EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(2).type);
+ EXPECT_EQ(15, fields.getFieldSpec(2).len);
+
+ expected_wire.clear();
+ UnitTestUtil::readWireData("rdatafields5.wire", expected_wire);
+ Name("com").toWire(obuffer);
+ 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());
+
+ expected_wire.clear();
+ UnitTestUtil::readWireData("rdatafields6.wire", expected_wire);
+ Name("com").toWire(renderer);
+ 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());
+}
+
+TEST_F(RdataFieldsTest, constructFromRdataRRSIG) {
+ UnitTestUtil::readWireData("rdatafields4.wire", expected_wire);
+ InputBuffer ibuffer(&expected_wire[0], expected_wire.size());
+ const uint16_t rdlen = ibuffer.readUint16();
+ const RdataFields fields(generic::RRSIG(ibuffer, rdlen));
+
+ // drop the RDLEN part
+ expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2);
+
+ constructCommonTestsRRSIG(fields);
+}
+
+TEST_F(RdataFieldsTest, constructFromParamsRRSIG) {
+ UnitTestUtil::readWireData("rdatafields4.wire", fields_wire);
+ fields_wire.erase(fields_wire.begin(), fields_wire.begin() + 2);
+
+ const RdataFields::FieldSpec specs[] = {
+ RdataFields::FieldSpec(RdataFields::DATA, 18),
+ RdataFields::FieldSpec(RdataFields::INCOMPRESSIBLE_NAME, 13),
+ RdataFields::FieldSpec(RdataFields::DATA, 15)
+ };
+ const RdataFields fields(specs, sizeof(specs), &fields_wire[0],
+ fields_wire.size());
+ constructCommonTestsRRSIG(fields);
+}
+
+TEST_F(RdataFieldsTest, convertRdatatoParams) {
+ // Confirm we can restore the original data from the serialized data.
+ // We use RRSIG as a relatively complicated field structure.
+ UnitTestUtil::readWireData("rdatafields4.wire", expected_wire);
+ InputBuffer ibuffer(&expected_wire[0], expected_wire.size());
+ const uint16_t rdlen = ibuffer.readUint16();
+ const RdataFields fields(generic::RRSIG(ibuffer, rdlen));
+
+ expected_wire.erase(expected_wire.begin(), expected_wire.begin() + 2);
+
+ // Copy the data in separate storage
+ vector<uint8_t> spec_store(fields.getFieldSpecDataSize());
+ void* cp_spec = &spec_store[0];
+ memcpy(cp_spec, fields.getFieldSpecData(), spec_store.size());
+ vector<uint8_t> data_store(fields.getDataLength());
+ memcpy(&data_store[0], fields.getData(), fields.getDataLength());
+
+ // Restore the data in the form of RdataFields
+ const RdataFields fields_byparams(cp_spec, fields.getFieldSpecDataSize(),
+ &data_store[0], fields.getDataLength());
+
+ // Check it's valid
+ constructCommonTestsRRSIG(fields_byparams);
+}
+
+//
+// OPT: an empty RDATA
+//
+void
+RdataFieldsTest::constructCommonTestsOPT(const RdataFields& fields) {
+ EXPECT_EQ(0, fields.getFieldSpecDataSize());
+ EXPECT_EQ(0, fields.getFieldCount());
+ EXPECT_EQ(0, fields.getDataLength());
+ EXPECT_EQ((const uint8_t*) NULL, fields.getData());
+ fields.toWire(obuffer);
+ EXPECT_EQ(0, obuffer.getLength());
+ fields.toWire(renderer);
+ EXPECT_EQ(0, renderer.getLength());
+}
+
+TEST_F(RdataFieldsTest, constructFromRdataOPT) {
+ InputBuffer ibuffer(NULL, 0);
+ const RdataFields fields(generic::OPT(ibuffer, 0));
+ constructCommonTestsOPT(fields);
+}
+
+TEST_F(RdataFieldsTest, constructFromParamsOPT) {
+ const RdataFields fields(NULL, 0, NULL, 0);
+ constructCommonTestsOPT(fields);
+}
+
+// Invalid input to the "from parameter" constructor: sum of the field lengths
+// is not equal to the data length.
+TEST_F(RdataFieldsTest, invalidFieldLength) {
+ UnitTestUtil::readWireData("rdatafields4.wire", fields_wire);
+ fields_wire.erase(fields_wire.begin(), fields_wire.begin() + 2);
+
+ const RdataFields::FieldSpec specs[] = {
+ RdataFields::FieldSpec(RdataFields::DATA, 18),
+ RdataFields::FieldSpec(RdataFields::INCOMPRESSIBLE_NAME, 13),
+ RdataFields::FieldSpec(RdataFields::DATA, 14)
+ };
+ // sum of field len < data len
+ EXPECT_THROW(RdataFields(specs, 3, &fields_wire[0], fields_wire.size()),
+ isc::InvalidParameter);
+ // sum of field len > data len
+ EXPECT_THROW(RdataFields(specs, 3, &fields_wire[0],
+ fields_wire.size() - 2),
+ isc::InvalidParameter);
+}
+
+// Invalid input to the "from parameter" constructor: NULL vs length mismatch
+TEST_F(RdataFieldsTest, mismatchFieldLengthAndData) {
+ const unsigned char dummy_data = 0;
+ const RdataFields::FieldSpec dummy_spec(RdataFields::DATA, 1);
+
+ EXPECT_THROW(RdataFields(NULL, 1, &dummy_data, 1), isc::InvalidParameter);
+ EXPECT_THROW(RdataFields(&dummy_spec, 0, NULL, 0), isc::InvalidParameter);
+ EXPECT_THROW(RdataFields(&dummy_spec, 1, NULL, 1), isc::InvalidParameter);
+ EXPECT_THROW(RdataFields(NULL, 0, &dummy_data, 0), isc::InvalidParameter);
+}
+
+// Bogus input to getFieldSpec()
+TEST_F(RdataFieldsTest, getFieldSpecWithBadFieldId) {
+ const RdataFields fields_in_a(in::A("192.0.2.1"));
+ EXPECT_THROW(fields_in_a.getFieldSpec(1), isc::OutOfRange);
+}
+
+// Tests for unexpected methods in RdataFieldComposerTest. Confirm
+// a call to these methods triggers an exception. Expected methods are
+// tested via other tests above.
+class DummyRdata : public Rdata {
+public:
+ enum Mode { CLEAR, SKIP, TRIM };
+ explicit DummyRdata(Mode mode) : mode_(mode) {}
+ DummyRdata(const DummyRdata& source) : Rdata(), mode_(source.mode_) {}
+ virtual ~DummyRdata() {}
+ virtual void toWire(AbstractMessageRenderer& renderer) const {
+ // call the unexpected method corresponding to the test mode.
+ // method parameters don't matter.
+ switch (mode_) {
+ case CLEAR:
+ renderer.clear();
+ break;
+ case SKIP:
+ renderer.skip(2);
+ break;
+ case TRIM:
+ renderer.trim(2);
+ break;
+ }
+ }
+
+ // These are defined only to make the compiler happy. We don't use them
+ // for the test.
+ virtual string toText() const { return (""); }
+ virtual void toWire(OutputBuffer&) const {}
+ virtual int compare(const Rdata&) const { return (0); }
+private:
+ const int mode_;
+};
+
+TEST(RdataFieldComposerTest, unusedMethods) {
+ EXPECT_THROW(RdataFields(DummyRdata(DummyRdata::CLEAR)), isc::Unexpected);
+}
+}
diff --git a/src/lib/dns/tests/rrclass_unittest.cc b/src/lib/dns/tests/rrclass_unittest.cc
index 4eeb1e0..15f9a8c 100644
--- a/src/lib/dns/tests/rrclass_unittest.cc
+++ b/src/lib/dns/tests/rrclass_unittest.cc
@@ -14,7 +14,7 @@
#include <gtest/gtest.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rrclass.h>
@@ -23,6 +23,7 @@
using namespace std;
using namespace isc;
using namespace isc::dns;
+using namespace isc::util;
namespace {
class RRClassTest : public ::testing::Test {
diff --git a/src/lib/dns/tests/rrparamregistry_unittest.cc b/src/lib/dns/tests/rrparamregistry_unittest.cc
index a75eed5..d2bec5c 100644
--- a/src/lib/dns/tests/rrparamregistry_unittest.cc
+++ b/src/lib/dns/tests/rrparamregistry_unittest.cc
@@ -27,6 +27,7 @@
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
diff --git a/src/lib/dns/tests/rrset_unittest.cc b/src/lib/dns/tests/rrset_unittest.cc
index c704cc8..f951341 100644
--- a/src/lib/dns/tests/rrset_unittest.cc
+++ b/src/lib/dns/tests/rrset_unittest.cc
@@ -14,7 +14,7 @@
#include <stdexcept>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/name.h>
#include <dns/rdata.h>
@@ -32,6 +32,7 @@ using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
diff --git a/src/lib/dns/tests/rrttl_unittest.cc b/src/lib/dns/tests/rrttl_unittest.cc
index b8f5ac2..fe75eb6 100644
--- a/src/lib/dns/tests/rrttl_unittest.cc
+++ b/src/lib/dns/tests/rrttl_unittest.cc
@@ -14,7 +14,7 @@
#include <gtest/gtest.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rrttl.h>
@@ -23,6 +23,7 @@
using namespace std;
using namespace isc;
using namespace isc::dns;
+using namespace isc::util;
namespace {
class RRTTLTest : public ::testing::Test {
diff --git a/src/lib/dns/tests/rrtype_unittest.cc b/src/lib/dns/tests/rrtype_unittest.cc
index 6da7381..12f6001 100644
--- a/src/lib/dns/tests/rrtype_unittest.cc
+++ b/src/lib/dns/tests/rrtype_unittest.cc
@@ -14,7 +14,7 @@
#include <gtest/gtest.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rrtype.h>
@@ -23,6 +23,7 @@
using namespace std;
using namespace isc;
using namespace isc::dns;
+using namespace isc::util;
namespace {
class RRTypeTest : public ::testing::Test {
diff --git a/src/lib/dns/tests/run_unittests.cc b/src/lib/dns/tests/run_unittests.cc
index 3cdc61d..7616202 100644
--- a/src/lib/dns/tests/run_unittests.cc
+++ b/src/lib/dns/tests/run_unittests.cc
@@ -13,14 +13,18 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+#include <util/unittests/testdata.h>
#include <dns/tests/unittest_util.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::UnitTestUtil::addDataPath(TEST_DATA_SRCDIR);
+ isc::util::unittests::addTestDataPath(TEST_DATA_SRCDIR);
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
+ isc::util::unittests::addTestDataPath(TEST_DATA_BUILDDIR);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/dns/tests/sha1_unittest.cc b/src/lib/dns/tests/sha1_unittest.cc
deleted file mode 100644
index 79bc37d..0000000
--- a/src/lib/dns/tests/sha1_unittest.cc
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <stdint.h>
-#include <string>
-
-#include <dns/util/sha1.h>
-
-#include <gtest/gtest.h>
-
-#include <dns/tests/unittest_util.h>
-
-using isc::UnitTestUtil;
-using namespace std;
-using namespace isc::dns;
-
-namespace {
-class Sha1Test : public ::testing::Test {
-protected:
- Sha1Test() {}
-};
-
-// Tests copied from RFC 3174
-TEST_F(Sha1Test, Test1) {
- SHA1Context sha;
- uint8_t digest[SHA1_HASHSIZE];
- uint8_t expected[SHA1_HASHSIZE] = {
- 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e,
- 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d
- };
-
- EXPECT_EQ(0, SHA1Reset(&sha));
- EXPECT_EQ(0, SHA1Input(&sha, (const uint8_t *) "abc", 3));
- EXPECT_EQ(0, SHA1Result(&sha, digest));
- for (int i = 0; i < SHA1_HASHSIZE; i++) {
- EXPECT_EQ(digest[i], expected[i]);
- }
-}
-
-TEST_F(Sha1Test, Test2) {
- SHA1Context sha;
- string test("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
- uint8_t digest[SHA1_HASHSIZE];
- uint8_t expected[SHA1_HASHSIZE] = {
- 0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, 0x6e, 0xba, 0xae,
- 0x4a, 0xa1, 0xf9, 0x51, 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1
- };
-
- EXPECT_EQ(0, SHA1Reset(&sha));
- EXPECT_EQ(0, SHA1Input(&sha, (const uint8_t *) test.c_str(),
- test.length()));
- EXPECT_EQ(0, SHA1Result(&sha, digest));
- for (int i = 0; i < SHA1_HASHSIZE; i++) {
- EXPECT_EQ(digest[i], expected[i]);
- }
-}
-
-TEST_F(Sha1Test, Test3) {
- SHA1Context sha;
- uint8_t digest[SHA1_HASHSIZE];
- uint8_t expected[SHA1_HASHSIZE] = {
- 0x34, 0xaa, 0x97, 0x3c, 0xd4, 0xc4, 0xda, 0xa4, 0xf6, 0x1e,
- 0xeb, 0x2b, 0xdb, 0xad, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6f
- };
-
- EXPECT_EQ(0, SHA1Reset(&sha));
- for (int i = 0; i < 1000000; i++) {
- EXPECT_EQ(0, SHA1Input(&sha, (const uint8_t *) "a", 1));
- }
- EXPECT_EQ(0, SHA1Result(&sha, digest));
- for (int i = 0; i < SHA1_HASHSIZE; i++) {
- EXPECT_EQ(digest[i], expected[i]);
- }
-}
-
-TEST_F(Sha1Test, Test4) {
- SHA1Context sha;
- string test("01234567012345670123456701234567"
- "01234567012345670123456701234567");
- uint8_t digest[SHA1_HASHSIZE];
- uint8_t expected[SHA1_HASHSIZE] = {
- 0xde, 0xa3, 0x56, 0xa2, 0xcd, 0xdd, 0x90, 0xc7, 0xa7, 0xec,
- 0xed, 0xc5, 0xeb, 0xb5, 0x63, 0x93, 0x4f, 0x46, 0x04, 0x52
- };
-
- EXPECT_EQ(0, SHA1Reset(&sha));
- for (int i = 0; i < 10; i++) {
- EXPECT_EQ(0, SHA1Input(&sha, (const uint8_t *) test.c_str(),
- test.length()));
- }
- EXPECT_EQ(0, SHA1Result(&sha, digest));
- for (int i = 0; i < SHA1_HASHSIZE; i++) {
- EXPECT_EQ(digest[i], expected[i]);
- }
-}
-}
-
diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am
index 1aaddb6..cb1bb1c 100644
--- a/src/lib/dns/tests/testdata/Makefile.am
+++ b/src/lib/dns/tests/testdata/Makefile.am
@@ -3,7 +3,15 @@ CLEANFILES = *.wire
BUILT_SOURCES = edns_toWire1.wire edns_toWire2.wire edns_toWire3.wire
BUILT_SOURCES += edns_toWire4.wire
BUILT_SOURCES += message_fromWire10.wire message_fromWire11.wire
+BUILT_SOURCES += message_fromWire12.wire message_fromWire13.wire
+BUILT_SOURCES += message_fromWire14.wire message_fromWire15.wire
+BUILT_SOURCES += message_fromWire16.wire
+BUILT_SOURCES += message_toWire2.wire message_toWire3.wire
+BUILT_SOURCES += message_toText1.wire message_toText2.wire
+BUILT_SOURCES += message_toText3.wire
BUILT_SOURCES += name_toWire5.wire name_toWire6.wire
+BUILT_SOURCES += rdatafields1.wire rdatafields2.wire rdatafields3.wire
+BUILT_SOURCES += rdatafields4.wire rdatafields5.wire rdatafields6.wire
BUILT_SOURCES += rdata_nsec_fromWire4.wire rdata_nsec_fromWire5.wire
BUILT_SOURCES += rdata_nsec_fromWire6.wire rdata_nsec_fromWire7.wire
BUILT_SOURCES += rdata_nsec_fromWire8.wire rdata_nsec_fromWire9.wire
@@ -16,6 +24,10 @@ BUILT_SOURCES += rdata_nsec3_fromWire10.wire rdata_nsec3_fromWire11.wire
BUILT_SOURCES += rdata_nsec3_fromWire12.wire rdata_nsec3_fromWire13.wire
BUILT_SOURCES += rdata_nsec3_fromWire14.wire rdata_nsec3_fromWire15.wire
BUILT_SOURCES += rdata_rrsig_fromWire2.wire
+BUILT_SOURCES += rdata_rp_fromWire1.wire rdata_rp_fromWire2.wire
+BUILT_SOURCES += rdata_rp_fromWire3.wire rdata_rp_fromWire4.wire
+BUILT_SOURCES += rdata_rp_fromWire5.wire rdata_rp_fromWire6.wire
+BUILT_SOURCES += rdata_rp_toWire1.wire rdata_rp_toWire2.wire
BUILT_SOURCES += rdata_soa_toWireUncompressed.wire
BUILT_SOURCES += rdata_txt_fromWire2.wire rdata_txt_fromWire3.wire
BUILT_SOURCES += rdata_txt_fromWire4.wire rdata_txt_fromWire5.wire
@@ -27,6 +39,11 @@ BUILT_SOURCES += rdata_tsig_fromWire9.wire
BUILT_SOURCES += rdata_tsig_toWire1.wire rdata_tsig_toWire2.wire
BUILT_SOURCES += rdata_tsig_toWire3.wire rdata_tsig_toWire4.wire
BUILT_SOURCES += rdata_tsig_toWire5.wire
+BUILT_SOURCES += tsigrecord_toWire1.wire tsigrecord_toWire2.wire
+BUILT_SOURCES += tsig_verify1.wire tsig_verify2.wire tsig_verify3.wire
+BUILT_SOURCES += tsig_verify4.wire tsig_verify5.wire tsig_verify6.wire
+BUILT_SOURCES += tsig_verify7.wire tsig_verify8.wire tsig_verify9.wire
+BUILT_SOURCES += tsig_verify10.wire
# NOTE: keep this in sync with real file listing
# so is included in tarball
@@ -39,8 +56,13 @@ EXTRA_DIST += message_fromWire3 message_fromWire4
EXTRA_DIST += message_fromWire5 message_fromWire6
EXTRA_DIST += message_fromWire7 message_fromWire8
EXTRA_DIST += message_fromWire9 message_fromWire10.spec
-EXTRA_DIST += message_fromWire11.spec
-EXTRA_DIST += message_toWire1
+EXTRA_DIST += message_fromWire11.spec message_fromWire12.spec
+EXTRA_DIST += message_fromWire13.spec message_fromWire14.spec
+EXTRA_DIST += message_fromWire15.spec message_fromWire16.spec
+EXTRA_DIST += message_toWire1 message_toWire2.spec message_toWire3.spec
+EXTRA_DIST += message_toText1.txt message_toText1.spec
+EXTRA_DIST += message_toText2.txt message_toText2.spec
+EXTRA_DIST += message_toText3.txt message_toText3.spec
EXTRA_DIST += name_fromWire1 name_fromWire2 name_fromWire3_1 name_fromWire3_2
EXTRA_DIST += name_fromWire4 name_fromWire6 name_fromWire7 name_fromWire8
EXTRA_DIST += name_fromWire9 name_fromWire10 name_fromWire11 name_fromWire12
@@ -48,6 +70,8 @@ EXTRA_DIST += name_fromWire13 name_fromWire14
EXTRA_DIST += name_toWire1 name_toWire2 name_toWire3 name_toWire4
EXTRA_DIST += name_toWire5.spec name_toWire6.spec
EXTRA_DIST += question_fromWire question_toWire1 question_toWire2
+EXTRA_DIST += rdatafields1.spec rdatafields2.spec rdatafields3.spec
+EXTRA_DIST += rdatafields4.spec rdatafields5.spec rdatafields6.spec
EXTRA_DIST += rdata_cname_fromWire rdata_dname_fromWire rdata_dnskey_fromWire
EXTRA_DIST += rdata_ds_fromWire rdata_in_a_fromWire rdata_in_aaaa_fromWire
EXTRA_DIST += rdata_mx_fromWire rdata_mx_toWire1 rdata_mx_toWire2
@@ -58,7 +82,8 @@ EXTRA_DIST += rdata_nsec_fromWire6.spec rdata_nsec_fromWire7.spec
EXTRA_DIST += rdata_nsec_fromWire8.spec rdata_nsec_fromWire9.spec
EXTRA_DIST += rdata_nsec_fromWire10.spec
EXTRA_DIST += rdata_nsec3param_fromWire1
-EXTRA_DIST += rdata_nsec3_fromWire1 rdata_nsec3_fromWire3
+EXTRA_DIST += rdata_nsec3_fromWire1
+EXTRA_DIST += rdata_nsec3_fromWire2.spec rdata_nsec3_fromWire3
EXTRA_DIST += rdata_nsec3_fromWire4.spec rdata_nsec3_fromWire5.spec
EXTRA_DIST += rdata_nsec3_fromWire6.spec rdata_nsec3_fromWire7.spec
EXTRA_DIST += rdata_nsec3_fromWire8.spec rdata_nsec3_fromWire9.spec
@@ -67,6 +92,10 @@ EXTRA_DIST += rdata_nsec3_fromWire12.spec rdata_nsec3_fromWire13.spec
EXTRA_DIST += rdata_nsec3_fromWire14.spec rdata_nsec3_fromWire15.spec
EXTRA_DIST += rdata_opt_fromWire rdata_rrsig_fromWire1
EXTRA_DIST += rdata_rrsig_fromWire2.spec
+EXTRA_DIST += rdata_rp_fromWire1.spec rdata_rp_fromWire2.spec
+EXTRA_DIST += rdata_rp_fromWire3.spec rdata_rp_fromWire4.spec
+EXTRA_DIST += rdata_rp_fromWire5.spec rdata_rp_fromWire6.spec
+EXTRA_DIST += rdata_rp_toWire1.spec rdata_rp_toWire2.spec
EXTRA_DIST += rdata_soa_fromWire rdata_soa_toWireUncompressed.spec
EXTRA_DIST += rdata_txt_fromWire1 rdata_txt_fromWire2.spec
EXTRA_DIST += rdata_txt_fromWire3.spec rdata_txt_fromWire4.spec
@@ -82,7 +111,11 @@ EXTRA_DIST += rdata_tsig_fromWire9.spec
EXTRA_DIST += rdata_tsig_toWire1.spec rdata_tsig_toWire2.spec
EXTRA_DIST += rdata_tsig_toWire3.spec rdata_tsig_toWire4.spec
EXTRA_DIST += rdata_tsig_toWire5.spec
-EXTRA_DIST += rdata_nsec3_fromWire2.spec
+EXTRA_DIST += tsigrecord_toWire1.spec tsigrecord_toWire2.spec
+EXTRA_DIST += tsig_verify1.spec tsig_verify2.spec tsig_verify3.spec
+EXTRA_DIST += tsig_verify4.spec tsig_verify5.spec tsig_verify6.spec
+EXTRA_DIST += tsig_verify7.spec tsig_verify8.spec tsig_verify9.spec
+EXTRA_DIST += tsig_verify10.spec
.spec.wire:
./gen-wiredata.py -o $@ $<
diff --git a/src/lib/dns/tests/testdata/gen-wiredata.py.in b/src/lib/dns/tests/testdata/gen-wiredata.py.in
index 645430c..fd98c6e 100755
--- a/src/lib/dns/tests/testdata/gen-wiredata.py.in
+++ b/src/lib/dns/tests/testdata/gen-wiredata.py.in
@@ -15,7 +15,7 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-import configparser, re, time, sys
+import configparser, re, time, socket, sys
from datetime import datetime
from optparse import OptionParser
@@ -90,7 +90,7 @@ def encode_name(name, absolute=True):
for l in labels:
if len(l) > 4 and l[0:4] == 'ptr=':
# special meta-syntax for compression pointer
- wire += ' %04x' % (0xc000 | int(l[4:]))
+ wire += '%04x' % (0xc000 | int(l[4:]))
break
if absolute or len(l) > 0:
wire += '%02x' % len(l)
@@ -215,9 +215,76 @@ class EDNS:
f.write('# RDLEN=%d\n' % self.rdlen)
f.write('%04x\n' % self.rdlen)
-class SOA:
- # this currently doesn't support name compression within the RDATA.
- rdlen = -1 # auto-calculate
+class RR:
+ '''This is a base class for various types of RR test data.
+ For each RR type (A, AAAA, NS, etc), we define a derived class of RR
+ to dump type specific RDATA parameters. This class defines parameters
+ common to all types of RDATA, namely the owner name, RR class and TTL.
+ The dump() method of derived classes are expected to call dump_header(),
+ whose default implementation is provided in this class. This method
+ decides whether to dump the test data as an RR (with name, type, class)
+ or only as RDATA (with its length), and dumps the corresponding data
+ via the specified file object.
+
+ By convention we assume derived classes are named after the common
+ standard mnemonic of the corresponding RR types. For example, the
+ derived class for the RR type SOA should be named "SOA".
+
+ Configurable parameters are as follows:
+ - as_rr (bool): Whether or not the data is to be dumped as an RR. False
+ by default.
+ - rr_class (string): The RR class of the data. Only meaningful when the
+ data is dumped as an RR. Default is 'IN'.
+ - rr_ttl (integer): The TTL value of the RR. Only meaningful when the
+ data is dumped as an RR. Default is 86400 (1 day).
+ '''
+
+ def __init__(self):
+ self.as_rr = False
+ # only when as_rr is True, same for class/TTL:
+ self.rr_name = 'example.com'
+ self.rr_class = 'IN'
+ self.rr_ttl = 86400
+ def dump_header(self, f, rdlen):
+ type_txt = self.__class__.__name__
+ type_code = parse_value(type_txt, dict_rrtype)
+ if self.as_rr:
+ rrclass = parse_value(self.rr_class, dict_rrclass)
+ f.write('\n# %s RR (QNAME=%s Class=%s TTL=%d RDLEN=%d)\n' %
+ (type_txt, self.rr_name,
+ code_totext(rrclass, rdict_rrclass), self.rr_ttl, rdlen))
+ f.write('%s %04x %04x %08x %04x\n' %
+ (encode_name(self.rr_name), type_code, rrclass,
+ self.rr_ttl, rdlen))
+ else:
+ f.write('\n# %s RDATA (RDLEN=%d)\n' % (type_txt, rdlen))
+ f.write('%04x\n' % rdlen)
+
+class A(RR):
+ rdlen = 4 # fixed by default
+ address = '192.0.2.1'
+
+ def dump(self, f):
+ self.dump_header(f, self.rdlen)
+ f.write('# Address=%s\n' % (self.address))
+ bin_address = socket.inet_aton(self.address)
+ f.write('%02x%02x%02x%02x\n' % (bin_address[0], bin_address[1],
+ bin_address[2], bin_address[3]))
+
+class NS(RR):
+ rdlen = None # auto calculate
+ nsname = 'ns.example.com'
+
+ def dump(self, f):
+ nsname_wire = encode_name(self.nsname)
+ if self.rdlen is None:
+ self.rdlen = len(nsname_wire) / 2
+ self.dump_header(f, self.rdlen)
+ f.write('# NS name=%s\n' % (self.nsname))
+ f.write('%s\n' % nsname_wire)
+
+class SOA(RR):
+ rdlen = None # auto-calculate
mname = 'ns.example.com'
rname = 'root.example.com'
serial = 2010012601
@@ -228,11 +295,9 @@ class SOA:
def dump(self, f):
mname_wire = encode_name(self.mname)
rname_wire = encode_name(self.rname)
- rdlen = self.rdlen
- if rdlen < 0:
- rdlen = int(20 + len(mname_wire) / 2 + len(str(rname_wire)) / 2)
- f.write('\n# SOA RDATA (RDLEN=%d)\n' % rdlen)
- f.write('%04x\n' % rdlen);
+ if self.rdlen is None:
+ self.rdlen = int(20 + len(mname_wire) / 2 + len(str(rname_wire)) / 2)
+ self.dump_header(f, self.rdlen)
f.write('# NNAME=%s RNAME=%s\n' % (self.mname, self.rname))
f.write('%s %s\n' % (mname_wire, rname_wire))
f.write('# SERIAL(%d) REFRESH(%d) RETRY(%d) EXPIRE(%d) MINIMUM(%d)\n' %
@@ -277,6 +342,34 @@ class TXT:
' ' if len(wirestring_list[i]) > 0 else '',
wirestring_list[i]))
+class RP:
+ '''Implements rendering RP RDATA in the wire format.
+ Configurable parameters are as follows:
+ - rdlen: 16-bit RDATA length. If omitted, the accurate value is auto
+ calculated and used; if negative, the RDLEN field will be omitted from
+ the output data.
+ - mailbox: The mailbox field.
+ - text: The text field.
+ All of these parameters have the default values and can be omitted.
+ '''
+ rdlen = None # auto-calculate
+ mailbox = 'root.example.com'
+ text = 'rp-text.example.com'
+ def dump(self, f):
+ mailbox_wire = encode_name(self.mailbox)
+ text_wire = encode_name(self.text)
+ if self.rdlen is None:
+ self.rdlen = (len(mailbox_wire) + len(text_wire)) / 2
+ else:
+ self.rdlen = int(self.rdlen)
+ if self.rdlen >= 0:
+ f.write('\n# RP RDATA (RDLEN=%d)\n' % self.rdlen)
+ f.write('%04x\n' % self.rdlen)
+ else:
+ f.write('\n# RP RDATA (RDLEN omitted)\n')
+ f.write('# MAILBOX=%s TEXT=%s\n' % (self.mailbox, self.text))
+ f.write('%s %s\n' % (mailbox_wire, text_wire))
+
class NSECBASE:
'''Implements rendering NSEC/NSEC3 type bitmaps commonly used for
these RRs. The NSEC and NSEC3 classes will be inherited from this
@@ -404,7 +497,7 @@ class RRSIG:
f.write('# Tag=%d Signer=%s and Signature\n' % (self.tag, self.signer))
f.write('%04x %s %s\n' % (self.tag, name_wire, sig_wire))
-class TSIG:
+class TSIG(RR):
rdlen = None # auto-calculate
algorithm = 'hmac-sha256'
time_signed = 1286978795 # arbitrarily chosen default
@@ -416,12 +509,18 @@ class TSIG:
other_len = None # 6 if error is BADTIME; otherwise 0
other_data = None # use time_signed + fudge + 1 for BADTIME
dict_macsize = { 'hmac-md5' : 16, 'hmac-sha1' : 20, 'hmac-sha256' : 32 }
+
+ # TSIG has some special defaults
+ def __init__(self):
+ super().__init__()
+ self.rr_class = 'ANY'
+ self.rr_ttl = 0
+
def dump(self, f):
if str(self.algorithm) == 'hmac-md5':
name_wire = encode_name('hmac-md5.sig-alg.reg.int')
else:
name_wire = encode_name(self.algorithm)
- rdlen = self.rdlen
mac_size = self.mac_size
if mac_size is None:
if self.algorithm in self.dict_macsize.keys():
@@ -440,11 +539,10 @@ class TSIG:
if self.error == 18 else ''
else:
other_data = encode_string(self.other_data, other_len)
- if rdlen is None:
- rdlen = int(len(name_wire) / 2 + 16 + len(mac) / 2 + \
- len(other_data) / 2)
- f.write('\n# TSIG RDATA (RDLEN=%d)\n' % rdlen)
- f.write('%04x\n' % rdlen);
+ if self.rdlen is None:
+ self.rdlen = int(len(name_wire) / 2 + 16 + len(mac) / 2 + \
+ len(other_data) / 2)
+ self.dump_header(f, self.rdlen)
f.write('# Algorithm=%s Time-Signed=%d Fudge=%d\n' %
(self.algorithm, self.time_signed, self.fudge))
f.write('%s %012x %04x\n' % (name_wire, self.time_signed, self.fudge))
@@ -460,9 +558,11 @@ def get_config_param(section):
config_param = {'name' : (Name, {}),
'header' : (DNSHeader, header_xtables),
'question' : (DNSQuestion, question_xtables),
- 'edns' : (EDNS, {}), 'soa' : (SOA, {}), 'txt' : (TXT, {}),
- 'rrsig' : (RRSIG, {}), 'nsec' : (NSEC, {}),
- 'nsec3' : (NSEC3, {}), 'tsig' : (TSIG, {}) }
+ 'edns' : (EDNS, {}), 'a' : (A, {}), 'ns' : (NS, {}),
+ 'soa' : (SOA, {}), 'txt' : (TXT, {}),
+ 'rp' : (RP, {}), 'rrsig' : (RRSIG, {}),
+ 'nsec' : (NSEC, {}), 'nsec3' : (NSEC3, {}),
+ 'tsig' : (TSIG, {}) }
s = section
m = re.match('^([^:]+)/\d+$', section)
if m:
diff --git a/src/lib/dns/tests/testdata/message_fromWire12.spec b/src/lib/dns/tests/testdata/message_fromWire12.spec
new file mode 100644
index 0000000..4eadeed
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_fromWire12.spec
@@ -0,0 +1,21 @@
+#
+# A simple DNS response message with TSIG signed, but the owner name of TSIG
+# is compressed
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x2d65
+rd: 1
+arcount: 1
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+rr_name: ptr=12
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0x227026ad297beee721ce6c6fff1e9ef3
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/message_fromWire13.spec b/src/lib/dns/tests/testdata/message_fromWire13.spec
new file mode 100644
index 0000000..e81ec4c
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_fromWire13.spec
@@ -0,0 +1,20 @@
+#
+# Invalid TSIG: containing 2 TSIG RRs.
+#
+
+[custom]
+sections: header:question:tsig:tsig
+[header]
+id: 0x2d65
+rd: 1
+arcount: 2
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0x227026ad297beee721ce6c6fff1e9ef3
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/message_fromWire14.spec b/src/lib/dns/tests/testdata/message_fromWire14.spec
new file mode 100644
index 0000000..bf68a93
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_fromWire14.spec
@@ -0,0 +1,21 @@
+#
+# Invalid TSIG: not in the additional section.
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x2d65
+rd: 1
+# TSIG goes to the answer section
+ancount: 1
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0x227026ad297beee721ce6c6fff1e9ef3
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/message_fromWire15.spec b/src/lib/dns/tests/testdata/message_fromWire15.spec
new file mode 100644
index 0000000..25d810f
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_fromWire15.spec
@@ -0,0 +1,22 @@
+#
+# Invalid TSIG: not at the end of the message
+#
+
+[custom]
+sections: header:question:tsig:edns
+[header]
+id: 0x2d65
+rd: 1
+arcount: 2
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0x227026ad297beee721ce6c6fff1e9ef3
+original_id: 0x2d65
+[edns]
+# (all default)
diff --git a/src/lib/dns/tests/testdata/message_fromWire16.spec b/src/lib/dns/tests/testdata/message_fromWire16.spec
new file mode 100644
index 0000000..be0abc3
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_fromWire16.spec
@@ -0,0 +1,21 @@
+#
+# Invalid TSIG: not in the additional section.
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x2d65
+rd: 1
+arcount: 1
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+rr_class: IN
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0x227026ad297beee721ce6c6fff1e9ef3
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/message_toText1.spec b/src/lib/dns/tests/testdata/message_toText1.spec
new file mode 100644
index 0000000..b31310e
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toText1.spec
@@ -0,0 +1,24 @@
+#
+# A standard DNS message (taken from an invocation of dig)
+#
+
+[custom]
+sections: header:question:a/1:ns:a/2
+[header]
+id: 29174
+qr: 1
+aa: 1
+ancount: 1
+nscount: 1
+arcount: 1
+[question]
+name: www.example.com
+[a/1]
+as_rr: True
+rr_name: www.example.com
+address: 192.0.2.80
+[ns]
+as_rr: True
+[a/2]
+as_rr: True
+rr_name: ns.example.com
diff --git a/src/lib/dns/tests/testdata/message_toText1.txt b/src/lib/dns/tests/testdata/message_toText1.txt
new file mode 100644
index 0000000..58c7239
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toText1.txt
@@ -0,0 +1,14 @@
+;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29174
+;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1
+
+;; QUESTION SECTION:
+;www.example.com. IN A
+
+;; ANSWER SECTION:
+www.example.com. 86400 IN A 192.0.2.80
+
+;; AUTHORITY SECTION:
+example.com. 86400 IN NS ns.example.com.
+
+;; ADDITIONAL SECTION:
+ns.example.com. 86400 IN A 192.0.2.1
diff --git a/src/lib/dns/tests/testdata/message_toText2.spec b/src/lib/dns/tests/testdata/message_toText2.spec
new file mode 100644
index 0000000..978aab3
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toText2.spec
@@ -0,0 +1,14 @@
+#
+# A standard DNS message with EDNS (taken from an invocation of dig)
+#
+
+[custom]
+sections: header:question:edns
+[header]
+id: 45981
+qr: 1
+rcode: refused
+arcount: 1
+[question]
+[edns]
+do: 1
diff --git a/src/lib/dns/tests/testdata/message_toText2.txt b/src/lib/dns/tests/testdata/message_toText2.txt
new file mode 100644
index 0000000..42cc2c1
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toText2.txt
@@ -0,0 +1,8 @@
+;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 45981
+;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
+
+;; OPT PSEUDOSECTION:
+; EDNS: version: 0, flags: do; udp: 4096
+
+;; QUESTION SECTION:
+;example.com. IN A
diff --git a/src/lib/dns/tests/testdata/message_toText3.spec b/src/lib/dns/tests/testdata/message_toText3.spec
new file mode 100644
index 0000000..a74ea1b
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toText3.spec
@@ -0,0 +1,31 @@
+#
+# A standard DNS message with TSIG (taken from an invocation of dig)
+#
+
+[custom]
+sections: header:question:a/1:ns:a/2:tsig
+[header]
+id: 10140
+qr: 1
+aa: 1
+ancount: 1
+nscount: 1
+arcount: 2
+[question]
+name: www.example.com
+[a/1]
+as_rr: True
+rr_name: www.example.com
+address: 192.0.2.80
+[ns]
+as_rr: True
+[a/2]
+as_rr: True
+rr_name: ns.example.com
+[tsig]
+as_rr: True
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 1304384318
+original_id: 10140
+mac: 0x5257c80396f2fa95b20c77ae9a652fb2
diff --git a/src/lib/dns/tests/testdata/message_toText3.txt b/src/lib/dns/tests/testdata/message_toText3.txt
new file mode 100644
index 0000000..359b9c5
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toText3.txt
@@ -0,0 +1,17 @@
+;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10140
+;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2
+
+;; QUESTION SECTION:
+;www.example.com. IN A
+
+;; ANSWER SECTION:
+www.example.com. 86400 IN A 192.0.2.80
+
+;; AUTHORITY SECTION:
+example.com. 86400 IN NS ns.example.com.
+
+;; ADDITIONAL SECTION:
+ns.example.com. 86400 IN A 192.0.2.1
+
+;; TSIG PSEUDOSECTION:
+www.example.com. 0 ANY TSIG hmac-md5.sig-alg.reg.int. 1304384318 300 16 UlfIA5by+pWyDHeummUvsg== 10140 NOERROR 0
diff --git a/src/lib/dns/tests/testdata/message_toWire2.spec b/src/lib/dns/tests/testdata/message_toWire2.spec
new file mode 100644
index 0000000..d256052
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toWire2.spec
@@ -0,0 +1,21 @@
+#
+# A simple DNS query message with TSIG signed
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x2d65
+rd: 1
+arcount: 1
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0x227026ad297beee721ce6c6fff1e9ef3
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/message_toWire3.spec b/src/lib/dns/tests/testdata/message_toWire3.spec
new file mode 100644
index 0000000..c8e9453
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toWire3.spec
@@ -0,0 +1,22 @@
+#
+# A simple DNS query message with EDNS and TSIG
+#
+
+[custom]
+sections: header:question:edns:tsig
+[header]
+id: 0x06cd
+rd: 1
+arcount: 2
+[question]
+name: www.example.com
+[edns]
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4db60d1f
+mac_size: 16
+mac: 0x93444053881c83d7eb120e86f25b369e
+original_id: 0x06cd
diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire1.spec
new file mode 100644
index 0000000..edb9f34
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire1.spec
@@ -0,0 +1,6 @@
+#
+# A simplest form of RP: all default parameters
+#
+[custom]
+sections: rp
+[rp]
diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire2.spec
new file mode 100644
index 0000000..57adb5a
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire2.spec
@@ -0,0 +1,12 @@
+#
+# A simplest form of RP: names are compressed.
+#
+[custom]
+sections: name/1:name/2:rp
+[name/1]
+name: a.example.com
+[name/2]
+name: b.example.net
+[rp]
+mailbox: root.ptr=2
+text: rp-text.ptr=17
diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire3.spec
new file mode 100644
index 0000000..a238b7e
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire3.spec
@@ -0,0 +1,7 @@
+#
+# RP-like RDATA but RDLEN is too short.
+#
+[custom]
+sections: rp
+[rp]
+rdlen: 38
diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire4.spec
new file mode 100644
index 0000000..6f3abd1
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire4.spec
@@ -0,0 +1,7 @@
+#
+# RP-like RDATA but RDLEN is too long.
+#
+[custom]
+sections: rp
+[rp]
+rdlen: 40
diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire5.spec
new file mode 100644
index 0000000..b8d5e29
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire5.spec
@@ -0,0 +1,7 @@
+#
+# RP-like RDATA but mailbox name is broken.
+#
+[custom]
+sections: rp
+[rp]
+mailbox: "01234567890123456789012345678901234567890123456789012345678901234"
diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire6.spec
new file mode 100644
index 0000000..e9e79f3
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_rp_fromWire6.spec
@@ -0,0 +1,7 @@
+#
+# RP-like RDATA but text name is broken.
+#
+[custom]
+sections: rp
+[rp]
+text: "01234567890123456789012345678901234567890123456789012345678901234"
diff --git a/src/lib/dns/tests/testdata/rdata_rp_toWire1.spec b/src/lib/dns/tests/testdata/rdata_rp_toWire1.spec
new file mode 100644
index 0000000..948bd1a
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_rp_toWire1.spec
@@ -0,0 +1,8 @@
+#
+# A simplest form of RP for toWire test: all default parameters except rdlen,
+# which is to be omitted.
+#
+[custom]
+sections: rp
+[rp]
+rdlen: -1
diff --git a/src/lib/dns/tests/testdata/rdata_rp_toWire2.spec b/src/lib/dns/tests/testdata/rdata_rp_toWire2.spec
new file mode 100644
index 0000000..09a7ddc
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_rp_toWire2.spec
@@ -0,0 +1,14 @@
+#
+# A simple form of RP: names could be compressed (but MUST NOT).
+# rdlen is omitted for the "to wire" test.
+#
+[custom]
+sections: name/1:name/2:rp
+[name/1]
+name: a.example.com
+[name/2]
+name: b.example.net
+[rp]
+rdlen: -1
+mailbox: root.example.com
+text: rp-text.example.net
diff --git a/src/lib/dns/tests/testdata/rdatafields1.spec b/src/lib/dns/tests/testdata/rdatafields1.spec
new file mode 100644
index 0000000..6e105fb
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdatafields1.spec
@@ -0,0 +1,10 @@
+#
+# A sequence of names that could be compressed (but not compressed)
+#
+
+[custom]
+sections: name/1:name/2
+[name/1]
+name: www.example.com
+[name/2]
+name: example.com
diff --git a/src/lib/dns/tests/testdata/rdatafields2.spec b/src/lib/dns/tests/testdata/rdatafields2.spec
new file mode 100644
index 0000000..920dc95
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdatafields2.spec
@@ -0,0 +1,11 @@
+#
+# A sequence of names that can be compressed.
+#
+
+[custom]
+sections: name/1:name/2
+[name/1]
+name: www.example.com
+[name/2]
+name: ''
+pointer: 4
diff --git a/src/lib/dns/tests/testdata/rdatafields3.spec b/src/lib/dns/tests/testdata/rdatafields3.spec
new file mode 100644
index 0000000..b37fca3
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdatafields3.spec
@@ -0,0 +1,11 @@
+#
+# TXT RDATA with multiple character-strings.
+#
+
+[custom]
+sections: txt
+[txt]
+nstring: 3
+string0: 'first string'
+string1: 'second string'
+string2: 'last string'
diff --git a/src/lib/dns/tests/testdata/rdatafields4.spec b/src/lib/dns/tests/testdata/rdatafields4.spec
new file mode 100644
index 0000000..24b59aa
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdatafields4.spec
@@ -0,0 +1,7 @@
+#
+# Simple form of RRSIG (all fields use the default of generator script)
+#
+
+[custom]
+sections: rrsig
+[rrsig]
diff --git a/src/lib/dns/tests/testdata/rdatafields5.spec b/src/lib/dns/tests/testdata/rdatafields5.spec
new file mode 100644
index 0000000..2c78282
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdatafields5.spec
@@ -0,0 +1,12 @@
+#
+# Names and RDATA (RRSIG) with an incompressible name. All names are
+# rendered without compression.
+#
+
+[custom]
+sections: name/1:rrsig:name/2
+[name/1]
+name: com
+[rrsig]
+[name/2]
+name: www.example.com
diff --git a/src/lib/dns/tests/testdata/rdatafields6.spec b/src/lib/dns/tests/testdata/rdatafields6.spec
new file mode 100644
index 0000000..f9f0da1
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdatafields6.spec
@@ -0,0 +1,13 @@
+#
+# Names and RDATA (RRSIG) with an incompressible name. The name in RRSIG
+# isn't compressed, but it's used as the compression target.
+#
+
+[custom]
+sections: name/1:rrsig:name/2
+[name/1]
+name: com
+[rrsig]
+[name/2]
+name: www
+pointer: 25
diff --git a/src/lib/dns/tests/testdata/tsig_verify1.spec b/src/lib/dns/tests/testdata/tsig_verify1.spec
new file mode 100644
index 0000000..687013a
--- /dev/null
+++ b/src/lib/dns/tests/testdata/tsig_verify1.spec
@@ -0,0 +1,19 @@
+#
+# An example of signed AXFR request
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x3410
+arcount: 1
+[question]
+rrtype: AXFR
+[tsig]
+as_rr: True
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8e951
+mac_size: 16
+mac: 0x35b2fd08268781634400c7c8a5533b13
+original_id: 0x3410
diff --git a/src/lib/dns/tests/testdata/tsig_verify10.spec b/src/lib/dns/tests/testdata/tsig_verify10.spec
new file mode 100644
index 0000000..33ce83e
--- /dev/null
+++ b/src/lib/dns/tests/testdata/tsig_verify10.spec
@@ -0,0 +1,22 @@
+#
+# A simple DNS query message with TSIG signed whose MAC is too short
+# (only 1 byte)
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x2d65
+rd: 1
+arcount: 1
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 1
+mac: 0x22
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/tsig_verify2.spec b/src/lib/dns/tests/testdata/tsig_verify2.spec
new file mode 100644
index 0000000..ff98ca3
--- /dev/null
+++ b/src/lib/dns/tests/testdata/tsig_verify2.spec
@@ -0,0 +1,32 @@
+#
+# An example of signed AXFR response
+#
+
+[custom]
+sections: header:question:soa:tsig
+[header]
+id: 0x3410
+aa: 1
+qr: 1
+ancount: 1
+arcount: 1
+[question]
+rrtype: AXFR
+[soa]
+# note that names are compressed in this RR
+as_rr: True
+rr_name: ptr=12
+mname: ns.ptr=12
+rname: root.ptr=12
+serial: 2011041503
+refresh: 7200
+retry: 3600
+expire: 2592000
+[tsig]
+as_rr: True
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8e951
+mac_size: 16
+mac: 0xbdd612cd2c7f9e0648bd6dc23713e83c
+original_id: 0x3410
diff --git a/src/lib/dns/tests/testdata/tsig_verify3.spec b/src/lib/dns/tests/testdata/tsig_verify3.spec
new file mode 100644
index 0000000..7e2f797
--- /dev/null
+++ b/src/lib/dns/tests/testdata/tsig_verify3.spec
@@ -0,0 +1,26 @@
+#
+# An example of signed AXFR response (continued)
+#
+
+[custom]
+sections: header:ns:tsig
+[header]
+id: 0x3410
+aa: 1
+qr: 1
+qdcount: 0
+ancount: 1
+arcount: 1
+[ns]
+# note that names are compressed in this RR
+as_rr: True
+rr_name: example.com.
+nsname: ns.ptr=12
+[tsig]
+as_rr: True
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8e951
+mac_size: 16
+mac: 0x102458f7f62ddd7d638d746034130968
+original_id: 0x3410
diff --git a/src/lib/dns/tests/testdata/tsig_verify4.spec b/src/lib/dns/tests/testdata/tsig_verify4.spec
new file mode 100644
index 0000000..4ffbbcf
--- /dev/null
+++ b/src/lib/dns/tests/testdata/tsig_verify4.spec
@@ -0,0 +1,27 @@
+#
+# An example of signed DNS response with bogus MAC
+#
+
+[custom]
+sections: header:question:a:tsig
+[header]
+id: 0x2d65
+aa: 1
+qr: 1
+rd: 1
+ancount: 1
+arcount: 1
+[question]
+name: www.example.com
+[a]
+as_rr: True
+rr_name: ptr=12
+[tsig]
+as_rr: True
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+# bogus MAC
+mac: 0xdeadbeefdeadbeefdeadbeefdeadbeef
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/tsig_verify5.spec b/src/lib/dns/tests/testdata/tsig_verify5.spec
new file mode 100644
index 0000000..a6cc643
--- /dev/null
+++ b/src/lib/dns/tests/testdata/tsig_verify5.spec
@@ -0,0 +1,26 @@
+#
+# An example of signed DNS response
+#
+
+[custom]
+sections: header:question:a:tsig
+[header]
+id: 0x2d65
+aa: 1
+qr: 1
+rd: 1
+ancount: 1
+arcount: 1
+[question]
+name: www.example.com
+[a]
+as_rr: True
+rr_name: ptr=12
+[tsig]
+as_rr: True
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0x8fcda66a7cd1a3b9948eb1869d384a9f
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/tsig_verify6.spec b/src/lib/dns/tests/testdata/tsig_verify6.spec
new file mode 100644
index 0000000..32e0818
--- /dev/null
+++ b/src/lib/dns/tests/testdata/tsig_verify6.spec
@@ -0,0 +1,21 @@
+#
+# Forwarded DNS query message with TSIG signed (header ID != orig ID)
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x1035
+rd: 1
+arcount: 1
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0x227026ad297beee721ce6c6fff1e9ef3
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/tsig_verify7.spec b/src/lib/dns/tests/testdata/tsig_verify7.spec
new file mode 100644
index 0000000..377578e
--- /dev/null
+++ b/src/lib/dns/tests/testdata/tsig_verify7.spec
@@ -0,0 +1,21 @@
+#
+# DNS query message with TSIG that has empty MAC (invalidly)
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x2d65
+rd: 1
+arcount: 1
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 0
+mac: ''
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/tsig_verify8.spec b/src/lib/dns/tests/testdata/tsig_verify8.spec
new file mode 100644
index 0000000..5432d4a
--- /dev/null
+++ b/src/lib/dns/tests/testdata/tsig_verify8.spec
@@ -0,0 +1,23 @@
+#
+# DNS query message with TSIG that has empty MAC + BADKEY error
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x2d65
+rd: 1
+arcount: 1
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 0
+mac: ''
+# 17: BADKEY
+error: 17
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/tsig_verify9.spec b/src/lib/dns/tests/testdata/tsig_verify9.spec
new file mode 100644
index 0000000..5888455
--- /dev/null
+++ b/src/lib/dns/tests/testdata/tsig_verify9.spec
@@ -0,0 +1,21 @@
+#
+# A simple DNS query message with TSIG signed, but TSIG key and algorithm
+# names have upper case characters (unusual)
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x2d65
+rd: 1
+arcount: 1
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+rr_name: WWW.EXAMPLE.COM
+algorithm: HMAC-MD5.SIG-ALG.REG.INT
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0x227026ad297beee721ce6c6fff1e9ef3
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/tsigrecord_toWire1.spec b/src/lib/dns/tests/testdata/tsigrecord_toWire1.spec
new file mode 100644
index 0000000..a25dc46
--- /dev/null
+++ b/src/lib/dns/tests/testdata/tsigrecord_toWire1.spec
@@ -0,0 +1,16 @@
+#
+# A simple TSIG RR (some of the parameters are taken from a live example
+# and don't have a specific meaning)
+#
+
+[custom]
+sections: tsig
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0xdadadadadadadadadadadadadadadada
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/tsigrecord_toWire2.spec b/src/lib/dns/tests/testdata/tsigrecord_toWire2.spec
new file mode 100644
index 0000000..f667e4c
--- /dev/null
+++ b/src/lib/dns/tests/testdata/tsigrecord_toWire2.spec
@@ -0,0 +1,19 @@
+#
+# TSIG RR after some names that could (unexpectedly) cause name compression
+#
+
+[custom]
+sections: name/1:name/2:tsig
+[name/1]
+name: hmac-md5.sig-alg.reg.int
+[name/2]
+name: foo.example.com
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0xdadadadadadadadadadadadadadadada
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/tsig_unittest.cc b/src/lib/dns/tests/tsig_unittest.cc
new file mode 100644
index 0000000..cbb1267
--- /dev/null
+++ b/src/lib/dns/tests/tsig_unittest.cc
@@ -0,0 +1,930 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <time.h>
+#include <string>
+#include <stdexcept>
+#include <vector>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <gtest/gtest.h>
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+#include <util/encode/base64.h>
+#include <util/unittests/newhook.h>
+#include <util/time_utilities.h>
+
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/question.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/tsig.h>
+#include <dns/tsigkey.h>
+#include <dns/tsigrecord.h>
+
+#include <dns/tests/unittest_util.h>
+
+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;
+
+// See dnssectime.cc
+namespace isc {
+namespace util {
+namespace detail {
+extern int64_t (*gettimeFunction)();
+}
+}
+}
+
+namespace {
+// See dnssectime_unittest.cc
+template <int64_t NOW>
+int64_t
+testGetTime() {
+ return (NOW);
+}
+
+class TSIGTest : public ::testing::Test {
+protected:
+ TSIGTest() :
+ tsig_ctx(NULL), qid(0x2d65), test_name("www.example.com"),
+ badkey_name("badkey.example.com"), test_class(RRClass::IN()),
+ test_ttl(86400), message(Message::RENDER), buffer(0), renderer(buffer),
+ dummy_data(1024, 0xdd), // should be sufficiently large for all tests
+ dummy_record(badkey_name, any::TSIG(TSIGKey::HMACMD5_NAME(),
+ 0x4da8877a,
+ TSIGContext::DEFAULT_FUDGE,
+ 0, NULL, qid, 0, 0, NULL))
+ {
+ // Make sure we use the system time by default so that we won't be
+ // confused due to other tests that tweak the time.
+ isc::util::detail::gettimeFunction = NULL;
+
+ decodeBase64("SFuWd/q99SzF8Yzd1QbB9g==", secret);
+ tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
+ TSIGKey::HMACMD5_NAME(),
+ &secret[0], secret.size())));
+ tsig_verify_ctx.reset(new TSIGContext(TSIGKey(test_name,
+ TSIGKey::HMACMD5_NAME(),
+ &secret[0],
+ secret.size())));
+ }
+ ~TSIGTest() {
+ isc::util::detail::gettimeFunction = NULL;
+ }
+
+ // Many of the tests below create some DNS message and sign it under
+ // some specific TSIG context. This helper method unifies the common
+ // logic with slightly different parameters.
+ ConstTSIGRecordPtr createMessageAndSign(uint16_t qid, const Name& qname,
+ TSIGContext* ctx,
+ unsigned int message_flags =
+ RD_FLAG,
+ RRType qtype = RRType::A(),
+ const char* answer_data = NULL,
+ const RRType* answer_type = NULL,
+ bool add_question = true,
+ Rcode rcode = Rcode::NOERROR());
+
+ void createMessageFromFile(const char* datafile);
+
+ // bit-wise constant flags to configure DNS header flags for test
+ // messages.
+ static const unsigned int QR_FLAG = 0x1;
+ static const unsigned int AA_FLAG = 0x2;
+ static const unsigned int RD_FLAG = 0x4;
+
+ boost::scoped_ptr<TSIGContext> tsig_ctx;
+ boost::scoped_ptr<TSIGContext> tsig_verify_ctx;
+ TSIGKeyRing keyring;
+ const uint16_t qid;
+ const Name test_name;
+ const Name badkey_name;
+ const RRClass test_class;
+ const RRTTL test_ttl;
+ Message message;
+ OutputBuffer buffer;
+ MessageRenderer renderer;
+ vector<uint8_t> secret;
+ vector<uint8_t> dummy_data;
+ const TSIGRecord dummy_record;
+ vector<uint8_t> received_data;
+};
+
+ConstTSIGRecordPtr
+TSIGTest::createMessageAndSign(uint16_t id, const Name& qname,
+ TSIGContext* ctx, unsigned int message_flags,
+ RRType qtype, const char* answer_data,
+ const RRType* answer_type, bool add_question,
+ Rcode rcode)
+{
+ message.clear(Message::RENDER);
+ message.setQid(id);
+ message.setOpcode(Opcode::QUERY());
+ message.setRcode(rcode);
+ if ((message_flags & QR_FLAG) != 0) {
+ message.setHeaderFlag(Message::HEADERFLAG_QR);
+ }
+ if ((message_flags & AA_FLAG) != 0) {
+ message.setHeaderFlag(Message::HEADERFLAG_AA);
+ }
+ if ((message_flags & RD_FLAG) != 0) {
+ message.setHeaderFlag(Message::HEADERFLAG_RD);
+ }
+ if (add_question) {
+ message.addQuestion(Question(qname, test_class, qtype));
+ }
+ if (answer_data != NULL) {
+ if (answer_type == NULL) {
+ answer_type = &qtype;
+ }
+ RRsetPtr answer_rrset(new RRset(qname, test_class, *answer_type,
+ test_ttl));
+ answer_rrset->addRdata(createRdata(*answer_type, test_class,
+ answer_data));
+ message.addRRset(Message::SECTION_ANSWER, answer_rrset);
+ }
+ renderer.clear();
+ message.toWire(renderer);
+
+ TSIGContext::State expected_new_state =
+ (ctx->getState() == TSIGContext::INIT) ?
+ TSIGContext::SENT_REQUEST : TSIGContext::SENT_RESPONSE;
+ ConstTSIGRecordPtr tsig = ctx->sign(id, renderer.getData(),
+ renderer.getLength());
+ EXPECT_EQ(expected_new_state, ctx->getState());
+
+ return (tsig);
+}
+
+void
+TSIGTest::createMessageFromFile(const char* datafile) {
+ message.clear(Message::PARSE);
+ received_data.clear();
+ UnitTestUtil::readWireData(datafile, received_data);
+ InputBuffer buffer(&received_data[0], received_data.size());
+ message.fromWire(buffer);
+}
+
+void
+commonSignChecks(ConstTSIGRecordPtr tsig, uint16_t expected_qid,
+ uint64_t expected_timesigned,
+ const uint8_t* expected_mac, size_t expected_maclen,
+ uint16_t expected_error = 0,
+ uint16_t expected_otherlen = 0,
+ const uint8_t* expected_otherdata = NULL,
+ const Name& expected_algorithm = TSIGKey::HMACMD5_NAME())
+{
+ ASSERT_TRUE(tsig != NULL);
+ const any::TSIG& tsig_rdata = tsig->getRdata();
+
+ EXPECT_EQ(expected_algorithm, tsig_rdata.getAlgorithm());
+ 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);
+ 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);
+}
+
+void
+commonVerifyChecks(TSIGContext& ctx, const TSIGRecord* record,
+ const void* data, size_t data_len, TSIGError expected_error,
+ TSIGContext::State expected_new_state =
+ TSIGContext::VERIFIED_RESPONSE)
+{
+ EXPECT_EQ(expected_error, ctx.verify(record, data, data_len));
+ EXPECT_EQ(expected_error, ctx.getError());
+ EXPECT_EQ(expected_new_state, ctx.getState());
+}
+
+TEST_F(TSIGTest, initialState) {
+ // Until signing or verifying, the state should be INIT
+ EXPECT_EQ(TSIGContext::INIT, tsig_ctx->getState());
+
+ // And there should be no error code.
+ EXPECT_EQ(TSIGError(Rcode::NOERROR()), tsig_ctx->getError());
+}
+
+TEST_F(TSIGTest, constructFromKeyRing) {
+ // Construct a TSIG context with an empty key ring. Key shouldn't be
+ // found, and the BAD_KEY error should be recorded.
+ TSIGContext ctx1(test_name, TSIGKey::HMACMD5_NAME(), keyring);
+ EXPECT_EQ(TSIGContext::INIT, ctx1.getState());
+ EXPECT_EQ(TSIGError::BAD_KEY(), ctx1.getError());
+
+ // Add a matching key (we don't use the secret so leave it empty), and
+ // construct it again. This time it should be constructed with a valid
+ // key.
+ keyring.add(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(), NULL, 0));
+ TSIGContext ctx2(test_name, TSIGKey::HMACMD5_NAME(), keyring);
+ EXPECT_EQ(TSIGContext::INIT, ctx2.getState());
+ EXPECT_EQ(TSIGError::NOERROR(), ctx2.getError());
+
+ // Similar to the first case except that the key ring isn't empty but
+ // it doesn't contain a matching key.
+ TSIGContext ctx3(test_name, TSIGKey::HMACSHA1_NAME(), keyring);
+ EXPECT_EQ(TSIGContext::INIT, ctx3.getState());
+ EXPECT_EQ(TSIGError::BAD_KEY(), ctx3.getError());
+
+ TSIGContext ctx4(Name("different-key.example"), TSIGKey::HMACMD5_NAME(),
+ keyring);
+ EXPECT_EQ(TSIGContext::INIT, ctx4.getState());
+ EXPECT_EQ(TSIGError::BAD_KEY(), ctx4.getError());
+
+ // "Unknown" algorithm name will result in BADKEY, too.
+ TSIGContext ctx5(test_name, Name("unknown.algorithm"), keyring);
+ EXPECT_EQ(TSIGContext::INIT, ctx5.getState());
+ EXPECT_EQ(TSIGError::BAD_KEY(), ctx5.getError());
+}
+
+// Example output generated by
+// "dig -y www.example.com:SFuWd/q99SzF8Yzd1QbB9g== www.example.com
+// QID: 0x2d65
+// Time Signed: 0x00004da8877a
+// MAC: 227026ad297beee721ce6c6fff1e9ef3
+const uint8_t common_expected_mac[] = {
+ 0x22, 0x70, 0x26, 0xad, 0x29, 0x7b, 0xee, 0xe7,
+ 0x21, 0xce, 0x6c, 0x6f, 0xff, 0x1e, 0x9e, 0xf3
+};
+TEST_F(TSIGTest, sign) {
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+ {
+ SCOPED_TRACE("Sign test for query");
+ commonSignChecks(createMessageAndSign(qid, test_name, tsig_ctx.get()),
+ qid, 0x4da8877a, common_expected_mac,
+ sizeof(common_expected_mac));
+ }
+}
+
+// Same test as sign, but specifying the key name with upper-case (i.e.
+// non canonical) characters. The digest must be the same. It should actually
+// be ensured at the level of TSIGKey, but we confirm that at this level, too.
+TEST_F(TSIGTest, signUsingUpperCasedKeyName) {
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+ TSIGContext cap_ctx(TSIGKey(Name("WWW.EXAMPLE.COM"),
+ TSIGKey::HMACMD5_NAME(),
+ &secret[0], secret.size()));
+
+ {
+ SCOPED_TRACE("Sign test for query using non canonical key name");
+ commonSignChecks(createMessageAndSign(qid, test_name, &cap_ctx), qid,
+ 0x4da8877a, common_expected_mac,
+ sizeof(common_expected_mac));
+ }
+}
+
+// Same as the previous test, but for the algorithm name.
+TEST_F(TSIGTest, signUsingUpperCasedAlgorithmName) {
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+ TSIGContext cap_ctx(TSIGKey(test_name,
+ Name("HMAC-md5.SIG-alg.REG.int"),
+ &secret[0], secret.size()));
+
+ {
+ SCOPED_TRACE("Sign test for query using non canonical algorithm name");
+ commonSignChecks(createMessageAndSign(qid, test_name, &cap_ctx), qid,
+ 0x4da8877a, common_expected_mac,
+ sizeof(common_expected_mac));
+ }
+}
+
+TEST_F(TSIGTest, signAtActualTime) {
+ // Sign the message using the actual time, and check the accuracy of it.
+ // We cannot reasonably predict the expected MAC, so don't bother to
+ // check it.
+ const uint64_t now = static_cast<uint64_t>(time(NULL));
+
+ {
+ SCOPED_TRACE("Sign test for query at actual time");
+ ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name,
+ tsig_ctx.get());
+ const any::TSIG& tsig_rdata = tsig->getRdata();
+
+ // Check the resulted time signed is in the range of [now, now + 5]
+ // (5 is an arbitrary choice). Note that due to the order of the call
+ // to time() and sign(), time signed must not be smaller than the
+ // current time.
+ EXPECT_LE(now, tsig_rdata.getTimeSigned());
+ EXPECT_GE(now + 5, tsig_rdata.getTimeSigned());
+ }
+}
+
+TEST_F(TSIGTest, signBadData) {
+ // some specific bad data should be rejected proactively.
+ const unsigned char dummy_data = 0;
+ EXPECT_THROW(tsig_ctx->sign(0, NULL, 10), InvalidParameter);
+ EXPECT_THROW(tsig_ctx->sign(0, &dummy_data, 0), InvalidParameter);
+}
+
+TEST_F(TSIGTest, verifyBadData) {
+ // the data must at least hold the DNS message header and the specified
+ // TSIG.
+ EXPECT_THROW(tsig_ctx->verify(&dummy_record, &dummy_data[0],
+ 12 + dummy_record.getLength() - 1),
+ InvalidParameter);
+
+ // And the data must not be NULL.
+ EXPECT_THROW(tsig_ctx->verify(&dummy_record, NULL,
+ 12 + dummy_record.getLength()),
+ InvalidParameter);
+}
+
+#ifdef ENABLE_CUSTOM_OPERATOR_NEW
+// We enable this test only when we enable custom new/delete at build time
+// We could enable/disable the test runtime using the gtest filter, but
+// we'd basically like to minimize the number of disabled tests (they
+// should generally be considered tests that temporarily fail and should
+// be fixed).
+TEST_F(TSIGTest, signExceptionSafety) {
+ // Check sign() provides the strong exception guarantee for the simpler
+ // case (with a key error and empty MAC). The general case is more
+ // complicated and involves more memory allocation, so the test result
+ // won't be reliable.
+
+ commonVerifyChecks(*tsig_verify_ctx, &dummy_record, &dummy_data[0],
+ dummy_data.size(), TSIGError::BAD_KEY(),
+ TSIGContext::RECEIVED_REQUEST);
+
+ try {
+ int dummydata;
+ isc::util::unittests::force_throw_on_new = true;
+ isc::util::unittests::throw_size_on_new = sizeof(TSIGRecord);
+ tsig_verify_ctx->sign(0, &dummydata, sizeof(dummydata));
+ isc::util::unittests::force_throw_on_new = false;
+ ASSERT_FALSE(true) << "Expected throw on new, but it didn't happen";
+ } catch (const std::bad_alloc&) {
+ isc::util::unittests::force_throw_on_new = false;
+
+ // sign() threw, so the state should still be RECEIVED_REQUEST
+ EXPECT_EQ(TSIGContext::RECEIVED_REQUEST, tsig_verify_ctx->getState());
+ }
+ isc::util::unittests::force_throw_on_new = false;
+}
+#endif // ENABLE_CUSTOM_OPERATOR_NEW
+
+// Same test as "sign" but use a different algorithm just to confirm we don't
+// naively hardcode constants specific to a particular algorithm.
+// Test data generated by
+// "dig -y hmac-sha1:www.example.com:MA+QDhXbyqUak+qnMFyTyEirzng= www.example.com"
+// QID: 0x0967, RDflag
+// Current Time: 00004da8be86
+// Time Signed: 00004dae7d5f
+// HMAC Size: 20
+// HMAC: 415340c7daf824ed684ee586f7b5a67a2febc0d3
+TEST_F(TSIGTest, signUsingHMACSHA1) {
+ isc::util::detail::gettimeFunction = testGetTime<0x4dae7d5f>;
+
+ secret.clear();
+ decodeBase64("MA+QDhXbyqUak+qnMFyTyEirzng=", secret);
+ TSIGContext sha1_ctx(TSIGKey(test_name, TSIGKey::HMACSHA1_NAME(),
+ &secret[0], secret.size()));
+
+ const uint16_t sha1_qid = 0x0967;
+ const uint8_t expected_mac[] = {
+ 0x41, 0x53, 0x40, 0xc7, 0xda, 0xf8, 0x24, 0xed, 0x68, 0x4e,
+ 0xe5, 0x86, 0xf7, 0xb5, 0xa6, 0x7a, 0x2f, 0xeb, 0xc0, 0xd3
+ };
+ {
+ SCOPED_TRACE("Sign test using HMAC-SHA1");
+ commonSignChecks(createMessageAndSign(sha1_qid, test_name, &sha1_ctx),
+ sha1_qid, 0x4dae7d5f, expected_mac,
+ sizeof(expected_mac), 0, 0, NULL,
+ TSIGKey::HMACSHA1_NAME());
+ }
+}
+
+TEST_F(TSIGTest, signUsingHMACSHA224) {
+ isc::util::detail::gettimeFunction = testGetTime<0x4dae7d5f>;
+
+ secret.clear();
+ decodeBase64("MA+QDhXbyqUak+qnMFyTyEirzng=", secret);
+ TSIGContext sha1_ctx(TSIGKey(test_name, TSIGKey::HMACSHA224_NAME(),
+ &secret[0], secret.size()));
+
+ const uint16_t sha1_qid = 0x0967;
+ const uint8_t expected_mac[] = {
+ 0x3b, 0x93, 0xd3, 0xc5, 0xf9, 0x64, 0xb9, 0xc5, 0x00, 0x35,
+ 0x02, 0x69, 0x9f, 0xfc, 0x44, 0xd6, 0xe2, 0x66, 0xf4, 0x08,
+ 0xef, 0x33, 0xa2, 0xda, 0xa1, 0x48, 0x71, 0xd3
+ };
+ {
+ SCOPED_TRACE("Sign test using HMAC-SHA224");
+ commonSignChecks(createMessageAndSign(sha1_qid, test_name, &sha1_ctx),
+ sha1_qid, 0x4dae7d5f, expected_mac,
+ sizeof(expected_mac), 0, 0, NULL,
+ TSIGKey::HMACSHA224_NAME());
+ }
+}
+
+// The first part of this test checks verifying the signed query used for
+// the "sign" test.
+// The second part of this test generates a signed response to the signed
+// query as follows:
+// Answer: www.example.com. 86400 IN A 192.0.2.1
+// MAC: 8fcda66a7cd1a3b9948eb1869d384a9f
+TEST_F(TSIGTest, verifyThenSignResponse) {
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+ // This test data for the message test has the same wire format data
+ // as the message used in the "sign" test.
+ createMessageFromFile("message_toWire2.wire");
+ {
+ SCOPED_TRACE("Verify test for request");
+ commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(),
+ &received_data[0], received_data.size(),
+ TSIGError::NOERROR(), TSIGContext::RECEIVED_REQUEST);
+ }
+
+ // Transform the original message to a response, then sign the response
+ // with the context of "verified state".
+ ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name,
+ tsig_verify_ctx.get(),
+ QR_FLAG|AA_FLAG|RD_FLAG,
+ RRType::A(), "192.0.2.1");
+ const uint8_t expected_mac[] = {
+ 0x8f, 0xcd, 0xa6, 0x6a, 0x7c, 0xd1, 0xa3, 0xb9,
+ 0x94, 0x8e, 0xb1, 0x86, 0x9d, 0x38, 0x4a, 0x9f
+ };
+ {
+ SCOPED_TRACE("Sign test for response");
+ commonSignChecks(tsig, qid, 0x4da8877a, expected_mac,
+ sizeof(expected_mac));
+ }
+}
+
+TEST_F(TSIGTest, verifyUpperCaseNames) {
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+ // This test data for the message test has the same wire format data
+ // as the message used in the "sign" test.
+ createMessageFromFile("tsig_verify9.wire");
+ {
+ SCOPED_TRACE("Verify test for request");
+ commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(),
+ &received_data[0], received_data.size(),
+ TSIGError::NOERROR(), TSIGContext::RECEIVED_REQUEST);
+ }
+}
+
+TEST_F(TSIGTest, verifyForwardedMessage) {
+ // Similar to the first part of the previous test, but this test emulates
+ // the "forward" case, where the ID of the Header and the original ID in
+ // TSIG is different.
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+ createMessageFromFile("tsig_verify6.wire");
+ {
+ SCOPED_TRACE("Verify test for forwarded request");
+ commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(),
+ &received_data[0], received_data.size(),
+ TSIGError::NOERROR(), TSIGContext::RECEIVED_REQUEST);
+ }
+}
+
+// Example of signing multiple messages in a single TCP stream,
+// taken from data using BIND 9's "one-answer" transfer-format.
+// Request:
+// QID: 0x3410, flags (none)
+// Question: example.com/IN/AXFR
+// Time Signed: 0x4da8e951
+// MAC: 35b2fd08268781634400c7c8a5533b13
+// First message:
+// QID: 0x3410, flags QR, AA
+// Question: example.com/IN/AXFR
+// Answer: example.com. 86400 IN SOA ns.example.com. root.example.com. (
+// 2011041503 7200 3600 2592000 1200)
+// MAC: bdd612cd2c7f9e0648bd6dc23713e83c
+// Second message:
+// Answer: example.com. 86400 IN NS ns.example.com.
+// MAC: 102458f7f62ddd7d638d746034130968
+TEST_F(TSIGTest, signContinuation) {
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8e951>;
+
+ const uint16_t axfr_qid = 0x3410;
+ const Name zone_name("example.com");
+
+ // Create and sign the AXFR request
+ ConstTSIGRecordPtr tsig = createMessageAndSign(axfr_qid, zone_name,
+ tsig_ctx.get(), 0,
+ RRType::AXFR());
+ // Then verify it (the wire format test data should contain the same
+ // message data, and verification should succeed).
+ received_data.clear();
+ UnitTestUtil::readWireData("tsig_verify1.wire", received_data);
+ {
+ SCOPED_TRACE("Verify AXFR query");
+ commonVerifyChecks(*tsig_verify_ctx, tsig.get(), &received_data[0],
+ received_data.size(), TSIGError::NOERROR(),
+ TSIGContext::RECEIVED_REQUEST);
+ }
+
+ // Create and sign the first response message
+ tsig = createMessageAndSign(axfr_qid, zone_name, tsig_verify_ctx.get(),
+ AA_FLAG|QR_FLAG, RRType::AXFR(),
+ "ns.example.com. root.example.com. "
+ "2011041503 7200 3600 2592000 1200",
+ &RRType::SOA());
+
+ // Then verify it at the requester side.
+ received_data.clear();
+ UnitTestUtil::readWireData("tsig_verify2.wire", received_data);
+ {
+ SCOPED_TRACE("Verify first AXFR response");
+ commonVerifyChecks(*tsig_ctx, tsig.get(), &received_data[0],
+ received_data.size(), TSIGError::NOERROR());
+ }
+
+ // Create and sign the second response message
+ const uint8_t expected_mac[] = {
+ 0x10, 0x24, 0x58, 0xf7, 0xf6, 0x2d, 0xdd, 0x7d,
+ 0x63, 0x8d, 0x74, 0x60, 0x34, 0x13, 0x09, 0x68
+ };
+ {
+ SCOPED_TRACE("Sign test for continued response in TCP stream");
+ tsig = createMessageAndSign(axfr_qid, zone_name, tsig_verify_ctx.get(),
+ AA_FLAG|QR_FLAG, RRType::AXFR(),
+ "ns.example.com.", &RRType::NS(), false);
+ commonSignChecks(tsig, axfr_qid, 0x4da8e951, expected_mac,
+ sizeof(expected_mac));
+ }
+
+ // Then verify it at the requester side.
+ received_data.clear();
+ UnitTestUtil::readWireData("tsig_verify3.wire", received_data);
+ {
+ SCOPED_TRACE("Verify second AXFR response");
+ commonVerifyChecks(*tsig_ctx, tsig.get(), &received_data[0],
+ received_data.size(), TSIGError::NOERROR());
+ }
+}
+
+// BADTIME example, taken from data using specially hacked BIND 9's nsupdate
+// Query:
+// QID: 0x1830, RD flag
+// Current Time: 00004da8be86
+// Time Signed: 00004da8b9d6
+// Question: www.example.com/IN/SOA
+//(mac) 8406 7d50 b8e7 d054 3d50 5bd9 de2a bb68
+// Response:
+// QRbit, RCODE=9(NOTAUTH)
+// Time Signed: 00004da8b9d6 (the one in the query)
+// MAC: d4b043f6f44495ec8a01260e39159d76
+// Error: 0x12 (BADTIME), Other Len: 6
+// Other data: 00004da8be86
+TEST_F(TSIGTest, badtimeResponse) {
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6>;
+
+ const uint16_t test_qid = 0x7fc4;
+ ConstTSIGRecordPtr tsig = createMessageAndSign(test_qid, test_name,
+ tsig_ctx.get(), 0,
+ RRType::SOA());
+
+ // "advance the clock" and try validating, which should fail due to BADTIME
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8be86>;
+ {
+ SCOPED_TRACE("Verify resulting in BADTIME due to expired SIG");
+ commonVerifyChecks(*tsig_verify_ctx, tsig.get(), &dummy_data[0],
+ dummy_data.size(), TSIGError::BAD_TIME(),
+ TSIGContext::RECEIVED_REQUEST);
+ }
+
+ // make and sign a response in the context of TSIG error.
+ tsig = createMessageAndSign(test_qid, test_name, tsig_verify_ctx.get(),
+ QR_FLAG, RRType::SOA(), NULL, NULL,
+ true, Rcode::NOTAUTH());
+ const uint8_t expected_otherdata[] = { 0, 0, 0x4d, 0xa8, 0xbe, 0x86 };
+ const uint8_t expected_mac[] = {
+ 0xd4, 0xb0, 0x43, 0xf6, 0xf4, 0x44, 0x95, 0xec,
+ 0x8a, 0x01, 0x26, 0x0e, 0x39, 0x15, 0x9d, 0x76
+ };
+ {
+ SCOPED_TRACE("Sign test for response with BADTIME");
+ commonSignChecks(tsig, message.getQid(), 0x4da8b9d6,
+ expected_mac, sizeof(expected_mac),
+ 18, // error: BADTIME
+ sizeof(expected_otherdata),
+ expected_otherdata);
+ }
+}
+
+TEST_F(TSIGTest, badtimeResponse2) {
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6>;
+
+ ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name,
+ tsig_ctx.get(), 0,
+ RRType::SOA());
+
+ // "rewind the clock" and try validating, which should fail due to BADTIME
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 - 600>;
+ {
+ SCOPED_TRACE("Verify resulting in BADTIME due to too future SIG");
+ commonVerifyChecks(*tsig_verify_ctx, tsig.get(), &dummy_data[0],
+ dummy_data.size(), TSIGError::BAD_TIME(),
+ TSIGContext::RECEIVED_REQUEST);
+ }
+}
+
+TEST_F(TSIGTest, badtimeBoundaries) {
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6>;
+
+ // Test various boundary conditions. We intentionally use the magic
+ // number of 300 instead of the constant variable for testing.
+ // In the okay cases, signature is not correct, but it's sufficient to
+ // check the error code isn't BADTIME for the purpose of this test.
+ ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name,
+ tsig_ctx.get(), 0,
+ RRType::SOA());
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 + 301>;
+ EXPECT_EQ(TSIGError::BAD_TIME(),
+ tsig_verify_ctx->verify(tsig.get(), &dummy_data[0],
+ dummy_data.size()));
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 + 300>;
+ EXPECT_NE(TSIGError::BAD_TIME(),
+ tsig_verify_ctx->verify(tsig.get(), &dummy_data[0],
+ dummy_data.size()));
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 - 301>;
+ EXPECT_EQ(TSIGError::BAD_TIME(),
+ tsig_verify_ctx->verify(tsig.get(), &dummy_data[0],
+ dummy_data.size()));
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 - 300>;
+ EXPECT_NE(TSIGError::BAD_TIME(),
+ tsig_verify_ctx->verify(tsig.get(), &dummy_data[0],
+ dummy_data.size()));
+}
+
+TEST_F(TSIGTest, badtimeOverflow) {
+ isc::util::detail::gettimeFunction = testGetTime<200>;
+ ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name,
+ tsig_ctx.get(), 0,
+ RRType::SOA());
+
+ // This should be in the okay range, but since "200 - fudge" overflows
+ // and we compare them as 64-bit unsigned integers, it results in a false
+ // positive (we intentionally accept that).
+ isc::util::detail::gettimeFunction = testGetTime<100>;
+ EXPECT_EQ(TSIGError::BAD_TIME(),
+ tsig_verify_ctx->verify(tsig.get(), &dummy_data[0],
+ dummy_data.size()));
+}
+
+TEST_F(TSIGTest, badsigResponse) {
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+ // Try to sign a simple message with bogus secret. It should fail
+ // with BADSIG.
+ createMessageFromFile("message_toWire2.wire");
+ TSIGContext bad_ctx(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(),
+ &dummy_data[0], dummy_data.size()));
+ {
+ SCOPED_TRACE("Verify resulting in BADSIG");
+ commonVerifyChecks(bad_ctx, message.getTSIGRecord(),
+ &received_data[0], received_data.size(),
+ TSIGError::BAD_SIG(), TSIGContext::RECEIVED_REQUEST);
+ }
+
+ // Sign the same message (which doesn't matter for this test) with the
+ // context of "checked state".
+ {
+ SCOPED_TRACE("Sign test for response with BADSIG error");
+ commonSignChecks(createMessageAndSign(qid, test_name, &bad_ctx),
+ message.getQid(), 0x4da8877a, NULL, 0,
+ 16); // 16: BADSIG
+ }
+}
+
+TEST_F(TSIGTest, badkeyResponse) {
+ // A similar test as badsigResponse but for BADKEY
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+ tsig_ctx.reset(new TSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(),
+ keyring));
+ {
+ SCOPED_TRACE("Verify resulting in BADKEY");
+ commonVerifyChecks(*tsig_ctx, &dummy_record, &dummy_data[0],
+ dummy_data.size(), TSIGError::BAD_KEY(),
+ TSIGContext::RECEIVED_REQUEST);
+ }
+
+ {
+ SCOPED_TRACE("Sign test for response with BADKEY error");
+ ConstTSIGRecordPtr sig = createMessageAndSign(qid, test_name,
+ tsig_ctx.get());
+ EXPECT_EQ(badkey_name, sig->getName());
+ commonSignChecks(sig, qid, 0x4da8877a, NULL, 0, 17); // 17: BADKEY
+ }
+}
+
+TEST_F(TSIGTest, badkeyForResponse) {
+ // "BADKEY" case for a response to a signed message
+ createMessageAndSign(qid, test_name, tsig_ctx.get());
+ {
+ SCOPED_TRACE("Verify a response resulting in BADKEY");
+ commonVerifyChecks(*tsig_ctx, &dummy_record, &dummy_data[0],
+ dummy_data.size(), TSIGError::BAD_KEY(),
+ TSIGContext::SENT_REQUEST);
+ }
+
+ // A similar case with a different algorithm
+ const TSIGRecord dummy_record2(test_name,
+ any::TSIG(TSIGKey::HMACSHA1_NAME(),
+ 0x4da8877a,
+ TSIGContext::DEFAULT_FUDGE,
+ 0, NULL, qid, 0, 0, NULL));
+ {
+ SCOPED_TRACE("Verify a response resulting in BADKEY due to bad alg");
+ commonVerifyChecks(*tsig_ctx, &dummy_record2, &dummy_data[0],
+ dummy_data.size(), TSIGError::BAD_KEY(),
+ TSIGContext::SENT_REQUEST);
+ }
+}
+
+TEST_F(TSIGTest, badsigThenValidate) {
+ // According to RFC2845 4.6, if TSIG verification fails the client
+ // should discard that message and wait for another signed response.
+ // This test emulates that situation.
+
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+ createMessageAndSign(qid, test_name, tsig_ctx.get());
+
+ createMessageFromFile("tsig_verify4.wire");
+ {
+ SCOPED_TRACE("Verify a response that should fail due to BADSIG");
+ commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(),
+ &received_data[0], received_data.size(),
+ TSIGError::BAD_SIG(), TSIGContext::SENT_REQUEST);
+ }
+
+ createMessageFromFile("tsig_verify5.wire");
+ {
+ SCOPED_TRACE("Verify a response after a BADSIG failure");
+ commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(),
+ &received_data[0], received_data.size(),
+ TSIGError::NOERROR(),
+ TSIGContext::VERIFIED_RESPONSE);
+ }
+}
+
+TEST_F(TSIGTest, nosigThenValidate) {
+ // Similar to the previous test, but the first response doesn't contain
+ // TSIG.
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+ createMessageAndSign(qid, test_name, tsig_ctx.get());
+
+ {
+ SCOPED_TRACE("Verify a response without TSIG that should exist");
+ commonVerifyChecks(*tsig_ctx, NULL, &dummy_data[0],
+ dummy_data.size(), TSIGError::FORMERR(),
+ TSIGContext::SENT_REQUEST);
+ }
+
+ createMessageFromFile("tsig_verify5.wire");
+ {
+ SCOPED_TRACE("Verify a response after a FORMERR failure");
+ commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(),
+ &received_data[0], received_data.size(),
+ TSIGError::NOERROR(),
+ TSIGContext::VERIFIED_RESPONSE);
+ }
+}
+
+TEST_F(TSIGTest, badtimeThenValidate) {
+ // Similar to the previous test, but the first response results in BADTIME.
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+ ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name,
+ tsig_ctx.get());
+
+ // "advance the clock" and try validating, which should fail due to BADTIME
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a + 600>;
+ {
+ SCOPED_TRACE("Verify resulting in BADTIME due to expired SIG");
+ commonVerifyChecks(*tsig_ctx, tsig.get(), &dummy_data[0],
+ dummy_data.size(), TSIGError::BAD_TIME(),
+ TSIGContext::SENT_REQUEST);
+ }
+
+ // revert the clock again.
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+ createMessageFromFile("tsig_verify5.wire");
+ {
+ SCOPED_TRACE("Verify a response after a BADTIME failure");
+ commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(),
+ &received_data[0], received_data.size(),
+ TSIGError::NOERROR(),
+ TSIGContext::VERIFIED_RESPONSE);
+ }
+}
+
+TEST_F(TSIGTest, emptyMAC) {
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+ // We don't allow empty MAC unless the TSIG error is BADSIG or BADKEY.
+ createMessageFromFile("tsig_verify7.wire");
+ {
+ SCOPED_TRACE("Verify test for request");
+ commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(),
+ &received_data[0], received_data.size(),
+ TSIGError::BAD_SIG(), TSIGContext::RECEIVED_REQUEST);
+ }
+
+ // If the empty MAC comes with a BADKEY error, the error is passed
+ // transparently.
+ createMessageFromFile("tsig_verify8.wire");
+ {
+ SCOPED_TRACE("Verify test for request");
+ commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(),
+ &received_data[0], received_data.size(),
+ TSIGError::BAD_KEY(), TSIGContext::RECEIVED_REQUEST);
+ }
+}
+
+TEST_F(TSIGTest, verifyAfterSendResponse) {
+ // Once the context is used for sending a signed response, it shouldn't
+ // be used for further verification.
+
+ // The following are essentially the same as what verifyThenSignResponse
+ // does with simplification.
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+ createMessageFromFile("message_toWire2.wire");
+ tsig_verify_ctx->verify(message.getTSIGRecord(), &received_data[0],
+ received_data.size());
+ EXPECT_EQ(TSIGContext::RECEIVED_REQUEST, tsig_verify_ctx->getState());
+ createMessageAndSign(qid, test_name, tsig_verify_ctx.get(),
+ QR_FLAG|AA_FLAG|RD_FLAG, RRType::A(), "192.0.2.1");
+ EXPECT_EQ(TSIGContext::SENT_RESPONSE, tsig_verify_ctx->getState());
+
+ // Now trying further verification.
+ createMessageFromFile("message_toWire2.wire");
+ EXPECT_THROW(tsig_verify_ctx->verify(message.getTSIGRecord(),
+ &received_data[0],
+ received_data.size()),
+ TSIGContextError);
+}
+
+TEST_F(TSIGTest, signAfterVerified) {
+ // Likewise, once the context verifies a response, it shouldn't for
+ // signing any more.
+
+ // The following are borrowed from badsigThenValidate (without the
+ // intermediate failure)
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+ createMessageAndSign(qid, test_name, tsig_ctx.get());
+ createMessageFromFile("tsig_verify5.wire");
+ tsig_ctx->verify(message.getTSIGRecord(), &received_data[0],
+ received_data.size());
+ EXPECT_EQ(TSIGContext::VERIFIED_RESPONSE, tsig_ctx->getState());
+
+ // Now trying further signing.
+ EXPECT_THROW(createMessageAndSign(qid, test_name, tsig_ctx.get()),
+ TSIGContextError);
+}
+
+TEST_F(TSIGTest, tooShortMAC) {
+ // Too short MAC should be rejected.
+ // Note: when we implement RFC4635-based checks, the error code will
+ // (probably) be FORMERR.
+
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+ createMessageFromFile("tsig_verify10.wire");
+ {
+ SCOPED_TRACE("Verify test for request");
+ commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(),
+ &received_data[0], received_data.size(),
+ TSIGError::BAD_SIG(), TSIGContext::RECEIVED_REQUEST);
+ }
+}
+
+} // end namespace
diff --git a/src/lib/dns/tests/tsigerror_unittest.cc b/src/lib/dns/tests/tsigerror_unittest.cc
new file mode 100644
index 0000000..bb08aef
--- /dev/null
+++ b/src/lib/dns/tests/tsigerror_unittest.cc
@@ -0,0 +1,116 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+#include <ostream>
+
+#include <gtest/gtest.h>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/rcode.h>
+#include <dns/tsigerror.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dns;
+
+namespace {
+TEST(TSIGErrorTest, constructFromErrorCode) {
+ // These are pretty trivial, and also test getCode();
+ EXPECT_EQ(0, TSIGError(0).getCode());
+ EXPECT_EQ(18, TSIGError(18).getCode());
+ EXPECT_EQ(65535, TSIGError(65535).getCode());
+}
+
+TEST(TSIGErrorTest, constructFromRcode) {
+ // We use RCODE for code values from 0-15.
+ EXPECT_EQ(0, TSIGError(Rcode::NOERROR()).getCode());
+ EXPECT_EQ(15, TSIGError(Rcode(15)).getCode());
+
+ // From error code 16 TSIG errors define a separate space, so passing
+ // corresponding RCODE for such code values should be prohibited.
+ EXPECT_THROW(TSIGError(Rcode(16)).getCode(), OutOfRange);
+}
+
+TEST(TSIGErrorTest, constants) {
+ // We'll only test arbitrarily chosen subsets of the codes.
+ // This class is quite simple, so it should be suffice.
+
+ EXPECT_EQ(TSIGError::BAD_SIG_CODE, TSIGError(16).getCode());
+ EXPECT_EQ(TSIGError::BAD_KEY_CODE, TSIGError(17).getCode());
+ EXPECT_EQ(TSIGError::BAD_TIME_CODE, TSIGError(18).getCode());
+
+ EXPECT_EQ(0, TSIGError::NOERROR().getCode());
+ EXPECT_EQ(9, TSIGError::NOTAUTH().getCode());
+ EXPECT_EQ(14, TSIGError::RESERVED14().getCode());
+ EXPECT_EQ(TSIGError::BAD_SIG_CODE, TSIGError::BAD_SIG().getCode());
+ EXPECT_EQ(TSIGError::BAD_KEY_CODE, TSIGError::BAD_KEY().getCode());
+ EXPECT_EQ(TSIGError::BAD_TIME_CODE, TSIGError::BAD_TIME().getCode());
+}
+
+TEST(TSIGErrorTest, equal) {
+ EXPECT_TRUE(TSIGError::NOERROR() == TSIGError(Rcode::NOERROR()));
+ EXPECT_TRUE(TSIGError(Rcode::NOERROR()) == TSIGError::NOERROR());
+ EXPECT_TRUE(TSIGError::NOERROR().equals(TSIGError(Rcode::NOERROR())));
+ EXPECT_TRUE(TSIGError::NOERROR().equals(TSIGError(Rcode::NOERROR())));
+
+ EXPECT_TRUE(TSIGError::BAD_SIG() == TSIGError(16));
+ EXPECT_TRUE(TSIGError(16) == TSIGError::BAD_SIG());
+ EXPECT_TRUE(TSIGError::BAD_SIG().equals(TSIGError(16)));
+ EXPECT_TRUE(TSIGError(16).equals(TSIGError::BAD_SIG()));
+}
+
+TEST(TSIGErrorTest, nequal) {
+ EXPECT_TRUE(TSIGError::BAD_KEY() != TSIGError(Rcode::NOERROR()));
+ EXPECT_TRUE(TSIGError(Rcode::NOERROR()) != TSIGError::BAD_KEY());
+ EXPECT_TRUE(TSIGError::BAD_KEY().nequals(TSIGError(Rcode::NOERROR())));
+ EXPECT_TRUE(TSIGError(Rcode::NOERROR()).nequals(TSIGError::BAD_KEY()));
+}
+
+TEST(TSIGErrorTest, toText) {
+ // TSIGError derived from the standard Rcode
+ EXPECT_EQ("NOERROR", TSIGError(Rcode::NOERROR()).toText());
+
+ // Well known TSIG errors
+ EXPECT_EQ("BADSIG", TSIGError::BAD_SIG().toText());
+ EXPECT_EQ("BADKEY", TSIGError::BAD_KEY().toText());
+ EXPECT_EQ("BADTIME", TSIGError::BAD_TIME().toText());
+
+ // Unknown (or not yet supported) codes. Simply converted as numeric.
+ EXPECT_EQ("19", TSIGError(19).toText());
+ EXPECT_EQ("65535", TSIGError(65535).toText());
+}
+
+TEST(TSIGErrorTest, toRcode) {
+ // TSIGError derived from the standard Rcode
+ EXPECT_EQ(Rcode::NOERROR(), TSIGError(Rcode::NOERROR()).toRcode());
+
+ // Well known TSIG errors
+ EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_SIG().toRcode());
+ EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_KEY().toRcode());
+ EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_TIME().toRcode());
+
+ // Unknown (or not yet supported) codes are treated as SERVFAIL.
+ EXPECT_EQ(Rcode::SERVFAIL(), TSIGError(19).toRcode());
+ EXPECT_EQ(Rcode::SERVFAIL(), TSIGError(65535).toRcode());
+}
+
+// test operator<<. We simply confirm it appends the result of toText().
+TEST(TSIGErrorTest, LeftShiftOperator) {
+ ostringstream oss;
+ oss << TSIGError::BAD_KEY();
+ EXPECT_EQ(TSIGError::BAD_KEY().toText(), oss.str());
+}
+} // end namespace
diff --git a/src/lib/dns/tests/tsigkey_unittest.cc b/src/lib/dns/tests/tsigkey_unittest.cc
index 6b2b8c5..20ee802 100644
--- a/src/lib/dns/tests/tsigkey_unittest.cc
+++ b/src/lib/dns/tests/tsigkey_unittest.cc
@@ -18,6 +18,8 @@
#include <exceptions/exceptions.h>
+#include <cryptolink/cryptolink.h>
+
#include <dns/tsigkey.h>
#include <dns/tests/unittest_util.h>
@@ -31,13 +33,34 @@ class TSIGKeyTest : public ::testing::Test {
protected:
TSIGKeyTest() : secret("someRandomData"), key_name("example.com") {}
string secret;
- Name key_name;
+ const Name key_name;
};
TEST_F(TSIGKeyTest, algorithmNames) {
EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), TSIGKey::HMACMD5_NAME());
EXPECT_EQ(Name("hmac-sha1"), TSIGKey::HMACSHA1_NAME());
EXPECT_EQ(Name("hmac-sha256"), TSIGKey::HMACSHA256_NAME());
+ EXPECT_EQ(Name("hmac-sha224"), TSIGKey::HMACSHA224_NAME());
+ EXPECT_EQ(Name("hmac-sha384"), TSIGKey::HMACSHA384_NAME());
+ EXPECT_EQ(Name("hmac-sha512"), TSIGKey::HMACSHA512_NAME());
+
+ // Also check conversion to cryptolink definitions
+ EXPECT_EQ(isc::cryptolink::MD5, TSIGKey(key_name, TSIGKey::HMACMD5_NAME(),
+ NULL, 0).getAlgorithm());
+ EXPECT_EQ(isc::cryptolink::SHA1, TSIGKey(key_name, TSIGKey::HMACSHA1_NAME(),
+ NULL, 0).getAlgorithm());
+ EXPECT_EQ(isc::cryptolink::SHA256, TSIGKey(key_name,
+ TSIGKey::HMACSHA256_NAME(),
+ NULL, 0).getAlgorithm());
+ EXPECT_EQ(isc::cryptolink::SHA224, TSIGKey(key_name,
+ TSIGKey::HMACSHA224_NAME(),
+ NULL, 0).getAlgorithm());
+ EXPECT_EQ(isc::cryptolink::SHA384, TSIGKey(key_name,
+ TSIGKey::HMACSHA384_NAME(),
+ NULL, 0).getAlgorithm());
+ EXPECT_EQ(isc::cryptolink::SHA512, TSIGKey(key_name,
+ TSIGKey::HMACSHA512_NAME(),
+ NULL, 0).getAlgorithm());
}
TEST_F(TSIGKeyTest, construct) {
@@ -48,9 +71,13 @@ TEST_F(TSIGKeyTest, construct) {
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, secret.c_str(),
secret.size(), key.getSecret(), key.getSecretLength());
+ // "unknown" algorithm is only accepted with empty secret.
EXPECT_THROW(TSIGKey(key_name, Name("unknown-alg"),
secret.c_str(), secret.size()),
isc::InvalidParameter);
+ TSIGKey key2(key_name, Name("unknown-alg"), NULL, 0);
+ EXPECT_EQ(key_name, key2.getKeyName());
+ EXPECT_EQ(Name("unknown-alg"), key2.getAlgorithmName());
// The algorithm name should be converted to the canonical form.
EXPECT_EQ("hmac-sha1.",
@@ -58,6 +85,12 @@ TEST_F(TSIGKeyTest, construct) {
secret.c_str(),
secret.size()).getAlgorithmName().toText());
+ // Same for key name
+ EXPECT_EQ("example.com.",
+ TSIGKey(Name("EXAMPLE.CoM."), TSIGKey::HMACSHA256_NAME(),
+ secret.c_str(),
+ secret.size()).getKeyName().toText());
+
// Invalid combinations of secret and secret_len:
EXPECT_THROW(TSIGKey(key_name, TSIGKey::HMACSHA1_NAME(), secret.c_str(), 0),
isc::InvalidParameter);
@@ -109,12 +142,18 @@ class TSIGKeyRingTest : public ::testing::Test {
protected:
TSIGKeyRingTest() :
key_name("example.com"),
+ md5_name("hmac-md5.sig-alg.reg.int"),
+ sha1_name("hmac-sha1"),
+ sha256_name("hmac-sha256"),
secretstring("anotherRandomData"),
secret(secretstring.c_str()),
secret_len(secretstring.size())
{}
TSIGKeyRing keyring;
- Name key_name;
+ const Name key_name;
+ const Name md5_name;
+ const Name sha1_name;
+ const Name sha256_name;
private:
const string secretstring;
protected:
@@ -134,8 +173,8 @@ TEST_F(TSIGKeyRingTest, add) {
EXPECT_EQ(TSIGKeyRing::EXIST, keyring.add(
TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(),
secret, secret_len)));
- // keys are identified their names, the same name of key with a different
- // algorithm would be considered a duplicate.
+ // keys are identified by their names, the same name of key with a
+ // different algorithm would be considered a duplicate.
EXPECT_EQ(TSIGKeyRing::EXIST, keyring.add(
TSIGKey(Name("example.com"), TSIGKey::HMACSHA1_NAME(),
secret, secret_len)));
@@ -187,44 +226,90 @@ TEST_F(TSIGKeyRingTest, removeFromSome) {
}
TEST_F(TSIGKeyRingTest, find) {
- EXPECT_EQ(TSIGKeyRing::NOTFOUND, keyring.find(key_name).code);
- EXPECT_EQ(static_cast<const TSIGKey*>(NULL), keyring.find(key_name).key);
-
- EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(
- TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(),
- secret, secret_len)));
- const TSIGKeyRing::FindResult result(keyring.find(key_name));
- EXPECT_EQ(TSIGKeyRing::SUCCESS, result.code);
- EXPECT_EQ(key_name, result.key->getKeyName());
- EXPECT_EQ(TSIGKey::HMACSHA256_NAME(), result.key->getAlgorithmName());
+ // If the keyring is empty the search should fail.
+ EXPECT_EQ(TSIGKeyRing::NOTFOUND, keyring.find(key_name, md5_name).code);
+ EXPECT_EQ(static_cast<const TSIGKey*>(NULL),
+ keyring.find(key_name, md5_name).key);
+
+ // Add a key and try to find it. Should succeed.
+ EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(TSIGKey(key_name, sha256_name,
+ secret, secret_len)));
+ const TSIGKeyRing::FindResult result1(keyring.find(key_name, sha256_name));
+ 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,
- result.key->getSecret(),
- result.key->getSecretLength());
+ result1.key->getSecret(),
+ result1.key->getSecretLength());
+
+ // If either key name or algorithm doesn't match, search should fail.
+ const TSIGKeyRing::FindResult result2 =
+ keyring.find(Name("different-key.example"), sha256_name);
+ EXPECT_EQ(TSIGKeyRing::NOTFOUND, result2.code);
+ EXPECT_EQ(static_cast<const TSIGKey*>(NULL), result2.key);
+
+ const TSIGKeyRing::FindResult result3 = keyring.find(key_name, md5_name);
+ EXPECT_EQ(TSIGKeyRing::NOTFOUND, result3.code);
+ EXPECT_EQ(static_cast<const TSIGKey*>(NULL), result3.key);
}
TEST_F(TSIGKeyRingTest, findFromSome) {
// essentially the same test, but search a larger set
- EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(
- TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(),
- secret, secret_len)));
- EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(
- TSIGKey(Name("another.example"), TSIGKey::HMACMD5_NAME(),
- secret, secret_len)));
- EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(
- TSIGKey(Name("more.example"), TSIGKey::HMACSHA1_NAME(),
- secret, secret_len)));
+ EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(TSIGKey(key_name, sha256_name,
+ secret, secret_len)));
+ EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(TSIGKey(Name("another.example"),
+ md5_name,
+ secret, secret_len)));
+ EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(TSIGKey(Name("more.example"),
+ sha1_name,
+ secret, secret_len)));
const TSIGKeyRing::FindResult result(
- keyring.find(Name("another.example")));
+ keyring.find(Name("another.example"), md5_name));
EXPECT_EQ(TSIGKeyRing::SUCCESS, result.code);
EXPECT_EQ(Name("another.example"), result.key->getKeyName());
EXPECT_EQ(TSIGKey::HMACMD5_NAME(), result.key->getAlgorithmName());
EXPECT_EQ(TSIGKeyRing::NOTFOUND,
- keyring.find(Name("noexist.example")).code);
+ keyring.find(Name("noexist.example"), sha1_name).code);
EXPECT_EQ(static_cast<const TSIGKey*>(NULL),
- keyring.find(Name("noexist.example")).key);
+ keyring.find(Name("noexist.example"), sha256_name).key);
+
+ EXPECT_EQ(TSIGKeyRing::NOTFOUND,
+ keyring.find(Name("another.example"), sha1_name).code);
+ EXPECT_EQ(static_cast<const TSIGKey*>(NULL),
+ keyring.find(Name("another.example"), sha256_name).key);
}
+TEST(TSIGStringTest, TSIGKeyFromToString) {
+ TSIGKey k1 = TSIGKey("test.example:MSG6Ng==:hmac-md5.sig-alg.reg.int");
+ TSIGKey k2 = TSIGKey("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.");
+ TSIGKey k3 = TSIGKey("test.example:MSG6Ng==");
+ TSIGKey k4 = TSIGKey(Name("test.example."), Name("hmac-sha1."), NULL, 0);
+ // "Unknown" key with empty secret is okay
+ TSIGKey k5 = TSIGKey("test.example.::unknown");
+
+ EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.",
+ k1.toText());
+ EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.",
+ k2.toText());
+ EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.",
+ k3.toText());
+ EXPECT_EQ("test.example.::hmac-sha1.", k4.toText());
+ EXPECT_EQ(Name("test.example."), k5.getKeyName());
+ EXPECT_EQ(Name("unknown"), k5.getAlgorithmName());
+
+ EXPECT_THROW(TSIGKey(""), isc::InvalidParameter);
+ EXPECT_THROW(TSIGKey(":"), isc::InvalidParameter);
+ EXPECT_THROW(TSIGKey("::"), isc::InvalidParameter);
+ EXPECT_THROW(TSIGKey("..:aa:"), isc::InvalidParameter);
+ EXPECT_THROW(TSIGKey("test.example:xxxx:"), isc::InvalidParameter);
+ EXPECT_THROW(TSIGKey("test.example.::"), isc::InvalidParameter);
+ EXPECT_THROW(TSIGKey("test.example.:"), isc::InvalidParameter);
+ EXPECT_THROW(TSIGKey("test.example.:MSG6Ng==:"), isc::InvalidParameter);
+ EXPECT_THROW(TSIGKey("test.example.:MSG6Ng==:unknown"), isc::InvalidParameter);
+}
+
+
} // end namespace
diff --git a/src/lib/dns/tests/tsigrecord_unittest.cc b/src/lib/dns/tests/tsigrecord_unittest.cc
new file mode 100644
index 0000000..e1b3e93
--- /dev/null
+++ b/src/lib/dns/tests/tsigrecord_unittest.cc
@@ -0,0 +1,159 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <vector>
+#include <sstream>
+
+#include <gtest/gtest.h>
+
+#include <util/buffer.h>
+
+#include <dns/exceptions.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/tsig.h>
+#include <dns/tsigkey.h>
+#include <dns/tsigrecord.h>
+
+#include <dns/tests/unittest_util.h>
+
+using namespace std;
+using namespace isc::util;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+
+namespace {
+class TSIGRecordTest : public ::testing::Test {
+protected:
+ TSIGRecordTest() :
+ test_name("www.example.com"), test_mac(16, 0xda),
+ test_rdata(any::TSIG(TSIGKey::HMACMD5_NAME(), 0x4da8877a,
+ TSIGContext::DEFAULT_FUDGE,
+ test_mac.size(), &test_mac[0],
+ 0x2d65, 0, 0, NULL)),
+ test_record(test_name, test_rdata),
+ buffer(0), renderer(buffer)
+ {}
+ const Name test_name;
+ vector<unsigned char> test_mac;
+ const any::TSIG test_rdata;
+ const TSIGRecord test_record;
+ OutputBuffer buffer;
+ MessageRenderer renderer;
+ vector<unsigned char> data;
+};
+
+TEST_F(TSIGRecordTest, getName) {
+ EXPECT_EQ(test_name, test_record.getName());
+}
+
+TEST_F(TSIGRecordTest, getLength) {
+ // 85 = 17 + 26 + 16 + 26
+ // len(www.example.com) = 17
+ // len(hmac-md5.sig-alg.reg.int) = 26
+ // len(MAC) = 16
+ // the rest are fixed length fields (26 in total)
+ EXPECT_EQ(85, test_record.getLength());
+}
+
+TEST_F(TSIGRecordTest, fromParams) {
+ // Construct the same TSIG RR as test_record from parameters.
+ // See the getLength test for the magic number of 85 (although it
+ // actually doesn't matter)
+ const TSIGRecord record(test_name, TSIGRecord::getClass(),
+ TSIGRecord::getTTL(), test_rdata, 85);
+ // Perform straight sanity checks
+ EXPECT_EQ(test_name, record.getName());
+ EXPECT_EQ(85, record.getLength());
+ EXPECT_EQ(0, test_rdata.compare(record.getRdata()));
+
+ // The constructor doesn't check the length...
+ EXPECT_NO_THROW(TSIGRecord(test_name, TSIGRecord::getClass(),
+ TSIGRecord::getTTL(), test_rdata, 82));
+ // ...even for impossibly small values...
+ EXPECT_NO_THROW(TSIGRecord(test_name, TSIGRecord::getClass(),
+ TSIGRecord::getTTL(), test_rdata, 1));
+ // ...or too large values.
+ EXPECT_NO_THROW(TSIGRecord(test_name, TSIGRecord::getClass(),
+ TSIGRecord::getTTL(), test_rdata, 65536));
+
+ // RDATA must indeed be TSIG
+ EXPECT_THROW(TSIGRecord(test_name, TSIGRecord::getClass(),
+ TSIGRecord::getTTL(), in::A("192.0.2.1"), 85),
+ DNSMessageFORMERR);
+
+ // Unexpected class
+ EXPECT_THROW(TSIGRecord(test_name, RRClass::IN(), TSIGRecord::getTTL(),
+ test_rdata, 85), DNSMessageFORMERR);
+
+ // Unexpected TTL
+ EXPECT_THROW(TSIGRecord(test_name, TSIGRecord::getClass(),
+ RRTTL(3600), test_rdata, 85), DNSMessageFORMERR);
+}
+
+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());
+
+ // 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());
+}
+
+TEST_F(TSIGRecordTest, recordToOLongToWire) {
+ // By setting the limit to "record length - 1", it will fail, and the
+ // renderer will be marked as "truncated".
+ renderer.setLengthLimit(test_record.getLength() - 1);
+ EXPECT_FALSE(renderer.isTruncated()); // not marked before render attempt
+ EXPECT_EQ(0, test_record.toWire(renderer));
+ EXPECT_TRUE(renderer.isTruncated());
+}
+
+TEST_F(TSIGRecordTest, recordToWireAfterNames) {
+ // A similar test but the TSIG RR follows some domain names that could
+ // cause name compression inside TSIG. Our implementation shouldn't
+ // compress either owner (key) name or the algorithm name. This test
+ // confirms that.
+
+ UnitTestUtil::readWireData("tsigrecord_toWire2.wire", data);
+ 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());
+}
+
+TEST_F(TSIGRecordTest, toText) {
+ EXPECT_EQ("www.example.com. 0 ANY TSIG hmac-md5.sig-alg.reg.int. "
+ "1302890362 300 16 2tra2tra2tra2tra2tra2g== 11621 NOERROR 0\n",
+ test_record.toText());
+}
+
+// test operator<<. We simply confirm it appends the result of toText().
+TEST_F(TSIGRecordTest, LeftShiftOperator) {
+ ostringstream oss;
+ oss << test_record;
+ EXPECT_EQ(test_record.toText(), oss.str());
+}
+} // end namespace
diff --git a/src/lib/dns/tsig.cc b/src/lib/dns/tsig.cc
new file mode 100644
index 0000000..714b2a5
--- /dev/null
+++ b/src/lib/dns/tsig.cc
@@ -0,0 +1,453 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <sys/time.h>
+
+#include <stdint.h>
+
+#include <cassert>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+#include <util/time_utilities.h>
+
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/tsig.h>
+#include <dns/tsigerror.h>
+#include <dns/tsigkey.h>
+
+#include <cryptolink/cryptolink.h>
+#include <cryptolink/crypto_hmac.h>
+
+using namespace std;
+using namespace isc::util;
+using namespace isc::cryptolink;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace dns {
+namespace {
+typedef boost::shared_ptr<HMAC> HMACPtr;
+
+// TSIG uses 48-bit unsigned integer to represent time signed.
+// Since gettimeWrapper() returns a 64-bit *signed* integer, we
+// make sure it's stored in an unsigned 64-bit integer variable and
+// represents a value in the expected range. (In reality, however,
+// gettimeWrapper() will return a positive integer that will fit
+// in 48 bits)
+uint64_t
+getTSIGTime() {
+ return (detail::gettimeWrapper() & 0x0000ffffffffffffULL);
+}
+}
+
+struct TSIGContext::TSIGContextImpl {
+ TSIGContextImpl(const TSIGKey& key) :
+ state_(INIT), key_(key), error_(Rcode::NOERROR()),
+ previous_timesigned_(0)
+ {}
+
+ // This helper method is used from verify(). It's expected to be called
+ // just before verify() returns. It updates internal state based on
+ // the verification result and return the TSIGError to be returned to
+ // the caller of verify(), so that verify() can call this method within
+ // its 'return' statement.
+ TSIGError postVerifyUpdate(TSIGError error, const void* digest,
+ uint16_t digest_len)
+ {
+ if (state_ == INIT) {
+ state_ = RECEIVED_REQUEST;
+ } else if (state_ == SENT_REQUEST && error == TSIGError::NOERROR()) {
+ state_ = VERIFIED_RESPONSE;
+ }
+ if (digest != NULL) {
+ previous_digest_.assign(static_cast<const uint8_t*>(digest),
+ static_cast<const uint8_t*>(digest) +
+ digest_len);
+ }
+ error_ = error;
+ return (error);
+ }
+
+ // The following three are helper methods to compute the digest for
+ // TSIG sign/verify in order to unify the common code logic for sign()
+ // and verify() and to keep these callers concise.
+ // These methods take an HMAC object, which will be updated with the
+ // calculated digest.
+ // Note: All methods construct a local OutputBuffer as a work space with a
+ // fixed initial buffer size to avoid intermediate buffer extension.
+ // This should be efficient enough, especially for fundamentally expensive
+ // operation like cryptographic sign/verify, but if the creation of the
+ // buffer in each helper method is still identified to be a severe
+ // performance bottleneck, we could have this class a buffer as a member
+ // variable and reuse it throughout the object's lifetime. Right now,
+ // we prefer keeping the scope for local things as small as possible.
+ void digestPreviousMAC(HMACPtr hmac) const;
+ void digestTSIGVariables(HMACPtr hmac, uint16_t rrclass, uint32_t rrttl,
+ uint64_t time_signed, uint16_t fudge,
+ uint16_t error, uint16_t otherlen,
+ const void* otherdata,
+ bool time_variables_only) const;
+ void digestDNSMessage(HMACPtr hmac, uint16_t qid, const void* data,
+ size_t data_len) const;
+ State state_;
+ const TSIGKey key_;
+ vector<uint8_t> previous_digest_;
+ TSIGError error_;
+ uint64_t previous_timesigned_; // only meaningful for response with BADTIME
+};
+
+void
+TSIGContext::TSIGContextImpl::digestPreviousMAC(HMACPtr hmac) const {
+ // We should have ensured the digest size fits 16 bits within this class
+ // implementation.
+ assert(previous_digest_.size() <= 0xffff);
+
+ OutputBuffer buffer(sizeof(uint16_t) + previous_digest_.size());
+ const uint16_t previous_digest_len(previous_digest_.size());
+ buffer.writeUint16(previous_digest_len);
+ if (previous_digest_len != 0) {
+ buffer.writeData(&previous_digest_[0], previous_digest_len);
+ }
+ hmac->update(buffer.getData(), buffer.getLength());
+}
+
+void
+TSIGContext::TSIGContextImpl::digestTSIGVariables(
+ HMACPtr hmac, uint16_t rrclass, uint32_t rrttl, uint64_t time_signed,
+ uint16_t fudge, uint16_t error, uint16_t otherlen, const void* otherdata,
+ bool time_variables_only) const
+{
+ // It's bit complicated, but we can still predict the necessary size of
+ // the data to be digested. So we precompute it to avoid possible
+ // reallocation inside OutputBuffer (not absolutely necessary, but this
+ // is a bit more efficient)
+ size_t data_size = 8;
+ if (!time_variables_only) {
+ data_size += 10 + key_.getKeyName().getLength() +
+ key_.getAlgorithmName().getLength();
+ }
+ OutputBuffer buffer(data_size);
+
+ if (!time_variables_only) {
+ key_.getKeyName().toWire(buffer);
+ buffer.writeUint16(rrclass);
+ buffer.writeUint32(rrttl);
+ key_.getAlgorithmName().toWire(buffer);
+ }
+ buffer.writeUint16(time_signed >> 32);
+ buffer.writeUint32(time_signed & 0xffffffff);
+ buffer.writeUint16(fudge);
+
+ if (!time_variables_only) {
+ buffer.writeUint16(error);
+ buffer.writeUint16(otherlen);
+ }
+
+ hmac->update(buffer.getData(), buffer.getLength());
+ if (!time_variables_only && otherlen > 0) {
+ hmac->update(otherdata, otherlen);
+ }
+}
+
+// In digestDNSMessage, we exploit some minimum knowledge of DNS message
+// format:
+// - the header section has a fixed length of 12 octets (MESSAGE_HEADER_LEN)
+// - the offset in the header section to the ID field is 0
+// - the offset in the header section to the ARCOUNT field is 10 (and the field
+// length is 2 octets)
+// We could construct a separate Message object from the given data, adjust
+// fields via the Message interfaces and then render it back to a separate
+// buffer, but that would be overkilling. The DNS message header has a
+// fixed length and necessary modifications are quite straightforward, so
+// we do the job using lower level interfaces.
+namespace {
+const size_t MESSAGE_HEADER_LEN = 12;
+}
+void
+TSIGContext::TSIGContextImpl::digestDNSMessage(HMACPtr hmac,
+ uint16_t qid, const void* data,
+ size_t data_len) const
+{
+ OutputBuffer buffer(MESSAGE_HEADER_LEN);
+ const uint8_t* msgptr = static_cast<const uint8_t*>(data);
+
+ // Install the original ID
+ buffer.writeUint16(qid);
+ msgptr += sizeof(uint16_t);
+
+ // Copy the rest of the header except the ARCOUNT field.
+ buffer.writeData(msgptr, 8);
+ msgptr += 8;
+
+ // Install the adjusted ARCOUNT (we don't care even if the value is bogus
+ // and it underflows; it would simply result in verification failure)
+ buffer.writeUint16(InputBuffer(msgptr, sizeof(uint16_t)).readUint16() - 1);
+ msgptr += 2;
+
+ // Digest the header and the rest of the DNS message
+ hmac->update(buffer.getData(), buffer.getLength());
+ hmac->update(msgptr, data_len - MESSAGE_HEADER_LEN);
+}
+
+TSIGContext::TSIGContext(const TSIGKey& key) : impl_(new TSIGContextImpl(key))
+{
+}
+
+TSIGContext::TSIGContext(const Name& key_name, const Name& algorithm_name,
+ const TSIGKeyRing& keyring) : impl_(NULL)
+{
+ const TSIGKeyRing::FindResult result(keyring.find(key_name,
+ algorithm_name));
+ if (result.code == TSIGKeyRing::NOTFOUND) {
+ // If not key is found, create a dummy key with the specified key
+ // parameters and empty secret. In the common scenario this will
+ // be used in subsequent response with a TSIG indicating a BADKEY
+ // error.
+ impl_ = new TSIGContextImpl(TSIGKey(key_name, algorithm_name,
+ NULL, 0));
+ impl_->error_ = TSIGError::BAD_KEY();
+ } else {
+ impl_ = new TSIGContextImpl(*result.key);
+ }
+}
+
+TSIGContext::~TSIGContext() {
+ delete impl_;
+}
+
+TSIGContext::State
+TSIGContext::getState() const {
+ return (impl_->state_);
+}
+
+TSIGError
+TSIGContext::getError() const {
+ return (impl_->error_);
+}
+
+ConstTSIGRecordPtr
+TSIGContext::sign(const uint16_t qid, const void* const data,
+ const size_t data_len)
+{
+ if (impl_->state_ == VERIFIED_RESPONSE) {
+ isc_throw(TSIGContextError,
+ "TSIG sign attempt after verifying a response");
+ }
+
+ if (data == NULL || data_len == 0) {
+ isc_throw(InvalidParameter, "TSIG sign error: empty data is given");
+ }
+
+ TSIGError error(TSIGError::NOERROR());
+ const uint64_t now = getTSIGTime();
+
+ // For responses adjust the error code.
+ if (impl_->state_ == RECEIVED_REQUEST) {
+ error = impl_->error_;
+ }
+
+ // For errors related to key or MAC, return an unsigned response as
+ // specified in Section 4.3 of RFC2845.
+ if (error == TSIGError::BAD_SIG() || error == TSIGError::BAD_KEY()) {
+ ConstTSIGRecordPtr tsig(new TSIGRecord(
+ impl_->key_.getKeyName(),
+ any::TSIG(impl_->key_.getAlgorithmName(),
+ now, DEFAULT_FUDGE, 0, NULL,
+ qid, error.getCode(), 0, NULL)));
+ impl_->previous_digest_.clear();
+ impl_->state_ = SENT_RESPONSE;
+ return (tsig);
+ }
+
+ HMACPtr hmac(CryptoLink::getCryptoLink().createHMAC(
+ impl_->key_.getSecret(),
+ impl_->key_.getSecretLength(),
+ impl_->key_.getAlgorithm()),
+ deleteHMAC);
+
+ // If the context has previous MAC (either the Request MAC or its own
+ // previous MAC), digest it.
+ if (impl_->state_ != INIT) {
+ impl_->digestPreviousMAC(hmac);
+ }
+
+ // Digest the message (without TSIG)
+ hmac->update(data, data_len);
+
+ // Digest TSIG variables.
+ // First, prepare some non constant variables.
+ const uint64_t time_signed = (error == TSIGError::BAD_TIME()) ?
+ impl_->previous_timesigned_ : now;
+ // For BADTIME error, we include 6 bytes of other data.
+ // (6 bytes = size of time signed value)
+ const uint16_t otherlen = (error == TSIGError::BAD_TIME()) ? 6 : 0;
+ OutputBuffer otherdatabuf(otherlen);
+ if (error == TSIGError::BAD_TIME()) {
+ otherdatabuf.writeUint16(now >> 32);
+ otherdatabuf.writeUint32(now & 0xffffffff);
+ }
+ const void* const otherdata =
+ (otherlen == 0) ? NULL : otherdatabuf.getData();
+ // Then calculate the digest. If state_ is SENT_RESPONSE we are sending
+ // a continued message in the same TCP stream so skip digesting
+ // variables except for time related variables (RFC2845 4.4).
+ impl_->digestTSIGVariables(hmac, TSIGRecord::getClass().getCode(),
+ TSIGRecord::TSIG_TTL, time_signed,
+ DEFAULT_FUDGE, error.getCode(),
+ otherlen, otherdata,
+ impl_->state_ == SENT_RESPONSE);
+
+ // Get the final digest, update internal state, then finish.
+ vector<uint8_t> digest = hmac->sign();
+ assert(digest.size() <= 0xffff); // cryptolink API should have ensured it.
+ ConstTSIGRecordPtr tsig(new TSIGRecord(
+ impl_->key_.getKeyName(),
+ any::TSIG(impl_->key_.getAlgorithmName(),
+ time_signed, DEFAULT_FUDGE,
+ digest.size(), &digest[0],
+ qid, error.getCode(), otherlen,
+ otherdata)));
+ // Exception free from now on.
+ impl_->previous_digest_.swap(digest);
+ impl_->state_ = (impl_->state_ == INIT) ? SENT_REQUEST : SENT_RESPONSE;
+ return (tsig);
+}
+
+TSIGError
+TSIGContext::verify(const TSIGRecord* const record, const void* const data,
+ const size_t data_len)
+{
+ if (impl_->state_ == SENT_RESPONSE) {
+ isc_throw(TSIGContextError,
+ "TSIG verify attempt after sending a response");
+ }
+
+ // This case happens when we sent a signed request and have received an
+ // unsigned response. According to RFC2845 Section 4.6 this case should be
+ // considered a "format error" (although the specific error code
+ // wouldn't matter much for the caller).
+ if (record == NULL) {
+ return (impl_->postVerifyUpdate(TSIGError::FORMERR(), NULL, 0));
+ }
+
+ const any::TSIG& tsig_rdata = record->getRdata();
+
+ // Reject some obviously invalid data
+ if (data_len < MESSAGE_HEADER_LEN + record->getLength()) {
+ isc_throw(InvalidParameter,
+ "TSIG verify: data length is invalid: " << data_len);
+ }
+ if (data == NULL) {
+ isc_throw(InvalidParameter, "TSIG verify: empty data is invalid");
+ }
+
+ // Check key: whether we first verify it with a known key or we verify
+ // it using the consistent key in the context. If the check fails we are
+ // done with BADKEY.
+ if (impl_->state_ == INIT && impl_->error_ == TSIGError::BAD_KEY()) {
+ return (impl_->postVerifyUpdate(TSIGError::BAD_KEY(), NULL, 0));
+ }
+ if (impl_->key_.getKeyName() != record->getName() ||
+ impl_->key_.getAlgorithmName() != tsig_rdata.getAlgorithm()) {
+ return (impl_->postVerifyUpdate(TSIGError::BAD_KEY(), NULL, 0));
+ }
+
+ // Check time: the current time must be in the range of
+ // [time signed - fudge, time signed + fudge]. Otherwise verification
+ // fails with BADTIME. (RFC2845 Section 4.6.2)
+ // Note: for simplicity we don't explicitly catch the case of too small
+ // current time causing underflow. With the fact that fudge is quite
+ // small and (for now) non configurable, it shouldn't be a real concern
+ // in practice.
+ const uint64_t now = getTSIGTime();
+ if (tsig_rdata.getTimeSigned() + DEFAULT_FUDGE < now ||
+ tsig_rdata.getTimeSigned() - DEFAULT_FUDGE > now) {
+ const void* digest = NULL;
+ size_t digest_len = 0;
+ if (impl_->state_ == INIT) {
+ digest = tsig_rdata.getMAC();
+ digest_len = tsig_rdata.getMACSize();
+ impl_->previous_timesigned_ = tsig_rdata.getTimeSigned();
+ }
+ return (impl_->postVerifyUpdate(TSIGError::BAD_TIME(), digest,
+ digest_len));
+ }
+
+ // TODO: signature length check based on RFC4635
+ // (Right now we enforce the standard signature length in libcryptolink)
+
+ // Handling empty MAC. While RFC2845 doesn't explicitly prohibit other
+ // cases, it can only reasonably happen in a response with BADSIG or
+ // BADKEY. We reject other cases as if it were BADSIG to avoid unexpected
+ // acceptance of a bogus signature. This behavior follows the BIND 9
+ // implementation.
+ if (tsig_rdata.getMACSize() == 0) {
+ TSIGError error = TSIGError(tsig_rdata.getError());
+ if (error != TSIGError::BAD_SIG() && error != TSIGError::BAD_KEY()) {
+ error = TSIGError::BAD_SIG();
+ }
+ return (impl_->postVerifyUpdate(error, NULL, 0));
+ }
+
+ HMACPtr hmac(CryptoLink::getCryptoLink().createHMAC(
+ impl_->key_.getSecret(),
+ impl_->key_.getSecretLength(),
+ impl_->key_.getAlgorithm()),
+ deleteHMAC);
+
+ // If the context has previous MAC (either the Request MAC or its own
+ // previous MAC), digest it.
+ if (impl_->state_ != INIT) {
+ impl_->digestPreviousMAC(hmac);
+ }
+
+ //
+ // Digest DNS message (excluding the trailing TSIG RR and adjusting the
+ // QID and ARCOUNT header fields)
+ //
+ impl_->digestDNSMessage(hmac, tsig_rdata.getOriginalID(),
+ data, data_len - record->getLength());
+
+ // Digest TSIG variables. If state_ is VERIFIED_RESPONSE, it's a
+ // continuation of the same TCP stream and skip digesting them except
+ // for time related variables (RFC2845 4.4).
+ // Note: we use the constant values for RR class and TTL specified
+ // in RFC2845, not received values (we reject other values in constructing
+ // the TSIGRecord).
+ impl_->digestTSIGVariables(hmac, TSIGRecord::getClass().getCode(),
+ TSIGRecord::TSIG_TTL,
+ tsig_rdata.getTimeSigned(),
+ tsig_rdata.getFudge(), tsig_rdata.getError(),
+ tsig_rdata.getOtherLen(),
+ tsig_rdata.getOtherData(),
+ impl_->state_ == VERIFIED_RESPONSE);
+
+ // Verify the digest with the received signature.
+ if (hmac->verify(tsig_rdata.getMAC(), tsig_rdata.getMACSize())) {
+ return (impl_->postVerifyUpdate(TSIGError::NOERROR(),
+ tsig_rdata.getMAC(),
+ tsig_rdata.getMACSize()));
+ }
+
+ return (impl_->postVerifyUpdate(TSIGError::BAD_SIG(), NULL, 0));
+}
+
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/tsig.h b/src/lib/dns/tsig.h
new file mode 100644
index 0000000..bceec25
--- /dev/null
+++ b/src/lib/dns/tsig.h
@@ -0,0 +1,394 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __TSIG_H
+#define __TSIG_H 1
+
+#include <boost/noncopyable.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/tsigerror.h>
+#include <dns/tsigkey.h>
+#include <dns/tsigrecord.h>
+
+namespace isc {
+namespace dns {
+
+/// An exception that is thrown for logic errors identified in TSIG
+/// sign/verify operations.
+///
+/// Note that this exception is not thrown for TSIG protocol errors such as
+/// verification failures. In general, this exception indicates an internal
+/// program bug.
+class TSIGContextError : public isc::Exception {
+public:
+ TSIGContextError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// TSIG session context.
+///
+/// The \c TSIGContext class maintains a context of a signed session of
+/// DNS transactions by TSIG. In many cases a TSIG signed session consists
+/// of a single set of request (e.g. normal query) and reply (e.g. normal
+/// response), where the request is initially signed by the client, and the
+/// reply is signed by the server using the initial signature. As mentioned
+/// in RFC2845, a session can consist of multiple exchanges in a TCP
+/// connection. As also mentioned in the RFC, an AXFR response often contains
+/// multiple DNS messages, which can belong to the same TSIG session.
+/// This class supports all these cases.
+///
+/// A \c TSIGContext object is generally constructed with a TSIG key to be
+/// used for the session, and keeps track of various kinds of session specific
+/// information, such as the original digest while waiting for a response or
+/// verification error information that is to be used for a subsequent
+/// response.
+///
+/// This class has two main methods, \c sign() and \c verify().
+/// The \c sign() method signs given data (which is supposed to be a complete
+/// DNS message without the TSIG itself) using the TSIG key and other
+/// related information associated with the \c TSIGContext object.
+/// The \c verify() method verifies a given DNS message that contains a TSIG
+/// RR using the key and other internal information.
+///
+/// In general, a DNS client that wants to send a signed query will construct
+/// a \c TSIGContext object with the TSIG key that the client is intending to
+/// use, and sign the query with the context. The client will keeps the
+/// context, and verify the response with it.
+///
+/// On the other hand, a DNS server will construct a \c TSIGContext object
+/// with the information of the TSIG RR included in a query with a set of
+/// possible keys (in the form of a \c TSIGKeyRing object). The constructor
+/// in this mode will identify the appropriate TSIG key (or internally record
+/// an error if it doesn't find a key). The server will then verify the
+/// query with the context, and generate a signed response using the same
+/// same context.
+///
+/// When multiple messages belong to the same TSIG session, either side
+/// (signer or verifier) will keep using the same context. It records
+/// the latest session state (such as the previous digest) so that repeated
+/// calls to \c sign() or \c verify() work correctly in terms of the TSIG
+/// protocol.
+///
+/// \b Examples
+///
+/// This is a typical client application that sends a TSIG signed query
+/// and verifies the response.
+///
+/// \code
+/// // "renderer" is of MessageRenderer to render the message.
+/// // (TSIGKey would be configured from config or command line in real app)
+/// TSIGContext ctx(TSIGKey("key.example:MSG6Ng=="));
+/// Message message(Message::RENDER);
+/// message.addQuestion(Question(Name("www.example.com"), RRClass::IN(),
+/// RRType::A()));
+/// message.toWire(renderer, ctx);
+///
+/// // sendto, then recvfrom. received result in (data, data_len)
+///
+/// message.clear(Message::PARSE);
+/// InputBuffer buffer(data, data_len);
+/// message.fromWire(buffer);
+/// TSIGError tsig_error = ctx.verify(message.getTSIGRecord(),
+/// data, data_len);
+/// if (tsig_error == TSIGError::NOERROR()) {
+/// // okay. ctx can be continuously used if it's receiving subsequent
+/// // signed responses from a TCP stream.
+/// } else if (message.getRcode() == Rcode::NOTAUTH()) {
+/// // hard error. give up this transaction per RFC2845 4.6.
+/// } else {
+/// // Other error: discard response keep waiting with the same ctx
+/// // for another (again, RFC2845 4.6).
+/// } \endcode
+///
+/// And this is a typical server application that authenticates a signed
+/// query and returns a response according to the result.
+///
+/// \code
+/// // Assume "message" is of type Message for query handling and
+/// // "renderer" is of MessageRenderer to render responses.
+/// Message message(Message::RENDER);
+///
+/// TSIGKeyRing keyring; // this must be configured with keys somewhere
+///
+/// // Receive a query and store it in (data, data_len)
+/// InputBuffer buffer(data, data_len);
+/// message.clear(Message::PARSE);
+/// message.fromWire(buffer);
+///
+/// const TSIGRecord* tsig = message.getTSIGRecord();
+/// if (tsig != NULL) {
+/// TSIGContext ctx(tsig->getName(), tsig->getRdata().getAlgorithm(),
+/// keyring);
+/// ctx.verify(tsig, data, data_len);
+///
+/// // prepare response
+/// message.makeResponse();
+/// //...
+/// message.toWire(renderer, ctx);
+///
+/// // send the response data back to the client.
+/// // If this is a beginning of a signed session over a TCP and
+/// // server has more data to send to the client, this ctx
+/// // will be used to sign subsequent messages.
+/// } \endcode
+///
+/// <b>TCP Consideration</b>
+///
+/// RFC2845 describes the case where a single TSIG session is used for
+/// multiple DNS messages (Section 4.4). This class supports signing and
+/// verifying the messages in this scenario, but does not care if the messages
+/// were delivered over a TCP connection or not. If, for example, the
+/// same \c TSIGContext object is used to sign two independent DNS queries
+/// sent over UDP, they will be considered to belong to the same TSIG
+/// session, and, as a result, verification will be likely to fail.
+///
+/// \b Copyability
+///
+/// This class is currently non copyable based on the observation of the
+/// typical usage as described above. But there is no strong technical
+/// reason why this class cannot be copyable. If we see the need for it
+/// in future we may change the implementation on this point.
+///
+/// <b>Note to developers:</b>
+/// One basic design choice is to make the \c TSIGContext class is as
+/// independent from the \c Message class. This is because the latter is
+/// much more complicated, depending on many other classes, while TSIG is
+/// a very specific part of the entire DNS protocol set. If the \c TSIGContext
+/// class depends on \c \c Message, it will be more vulnerable to changes
+/// to other classes, and will be more difficult to test due to the
+/// direct or indirect dependencies. The interface of \c sign() that takes
+/// opaque data (instead of, e.g., a \c Message or \c MessageRenderer object)
+/// is therefore a deliberate design decision.
+class TSIGContext : boost::noncopyable {
+public:
+ /// Internal state of context
+ ///
+ /// The constants of this enum type define a specific state of
+ /// \c TSIGContext to adjust the behavior. The definition is public
+ /// and the state can be seen via the \c getState() method, but this is
+ /// mostly private information. It's publicly visible mainly for testing
+ /// purposes; there is no API for the application to change the state
+ /// directly.
+ enum State {
+ INIT, ///< Initial state
+ SENT_REQUEST, ///< Client sent a signed request, waiting response
+ RECEIVED_REQUEST, ///< Server received a signed request
+ SENT_RESPONSE, ///< Server sent a signed response
+ VERIFIED_RESPONSE ///< Client successfully verified a response
+ };
+
+ /// \name Constructors and destructor
+ ///
+ //@{
+ /// Constructor from a TSIG key.
+ ///
+ /// \exception std::bad_alloc Resource allocation for internal data fails
+ ///
+ /// \param key The TSIG key to be used for TSIG sessions with this context.
+ explicit TSIGContext(const TSIGKey& key);
+
+ /// Constructor from key parameters and key ring.
+ TSIGContext(const Name& key_name, const Name& algorithm_name,
+ const TSIGKeyRing& keyring);
+
+ /// The destructor.
+ ~TSIGContext();
+ //@}
+
+ /// Sign a DNS message.
+ ///
+ /// This method computes the TSIG MAC for the given data, which is
+ /// generally expected to be a complete, wire-format DNS message
+ /// that doesn't contain a TSIG RR, based on the TSIG key and
+ /// other context information of \c TSIGContext, and returns a
+ /// result in the form of a (pointer object pointing to)
+ /// \c TSIGRecord object.
+ ///
+ /// The caller of this method will use the returned value to render a
+ /// complete TSIG RR into the message that has been signed so that it
+ /// will become a complete TSIG-signed message.
+ ///
+ /// In general, this method is called once by a client to send a
+ /// signed request or one more times by a server to sign
+ /// response(s) to a signed request. To avoid allowing accidental
+ /// misuse, if this method is called after a "client" validates a
+ /// response, an exception of class \c TSIGContextError will be
+ /// thrown.
+ ///
+ /// \note Normal applications are not expected to call this method
+ /// directly; they will usually use the \c Message::toWire() method
+ /// with a \c TSIGContext object being a parameter and have the
+ /// \c Message class create a complete signed message.
+ ///
+ /// This method treats the given data as opaque, even though it's generally
+ /// expected to represent a wire-format DNS message (see also the class
+ /// description), and doesn't inspect it in any way. For example, it
+ /// doesn't check whether the data length is sane for a valid DNS message.
+ /// This is also the reason why this method takes the \c qid parameter,
+ /// which will be used as the original ID of the resulting
+ /// \c TSIGRecordx object, even though this value should be stored in the
+ /// first two octets (in wire format) of the given data.
+ ///
+ /// \note This method still checks and rejects empty data (\c NULL pointer
+ /// data or the specified data length is 0) in order to avoid catastrophic
+ /// effect such as program crash. Empty data is not necessarily invalid
+ /// for HMAC computation, but obviously it doesn't make sense for a DNS
+ /// message.
+ ///
+ /// This method provides the strong exception guarantee; unless the method
+ /// returns (without an exception being thrown), the internal state of
+ /// the \c TSIGContext won't be modified.
+ ///
+ /// \exception TSIGContextError Context already verified a response.
+ /// \exception InvalidParameter \c data is NULL or \c data_len is 0
+ /// \exception cryptolink::LibraryError Some unexpected error in the
+ /// underlying crypto operation
+ /// \exception std::bad_alloc Temporary resource allocation failure
+ ///
+ /// \param qid The QID to be as the value of the original ID field of
+ /// the resulting TSIG record
+ /// \param data Points to the wire-format data to be signed
+ /// \param data_len The length of \c data in bytes
+ ///
+ /// \return A TSIG record for the given data along with the context.
+ ConstTSIGRecordPtr sign(const uint16_t qid, const void* const data,
+ const size_t data_len);
+
+ /// Verify a DNS message.
+ ///
+ /// This method verifies given data along with the context and a given
+ /// TSIG in the form of a \c TSIGRecord object. The data to be verified
+ /// is generally expected to be a complete, wire-format DNS message,
+ /// exactly as received by the host, and ending with a TSIG RR.
+ /// After verification process this method updates its internal state,
+ /// and returns the result in the form of a \c TSIGError object.
+ /// Possible return values are (see the \c TSIGError class description
+ /// for the mnemonics):
+ ///
+ /// - \c NOERROR: The data has been verified correctly.
+ /// - \c FORMERR: \c TSIGRecord is not given (see below).
+ /// - \c BAD_KEY: Appropriate key is not found or specified key doesn't
+ /// match for the data.
+ /// - \c BAD_TIME: The current time doesn't fall in the range specified
+ /// in the TSIG.
+ /// - \c BAD_SIG: The signature given in the TSIG doesn't match against
+ /// the locally computed digest or is the signature is
+ /// invalid in other way.
+ ///
+ /// If this method is called by a DNS client waiting for a signed
+ /// response and the result is not \c NOERROR, the context can be used
+ /// to try validating another signed message as described in RFC2845
+ /// Section 4.6.
+ ///
+ /// If this method is called by a DNS server that tries to authenticate
+ /// a signed request, and if the result is not \c NOERROR, the
+ /// corresponding error condition is recorded in the context so that
+ /// the server can return a response indicating what was wrong by calling
+ /// \c sign() with the updated context.
+ ///
+ /// In general, this method is called once by a server for
+ /// authenticating a signed request or one more times by a client to
+ /// validate signed response(s) to a signed request. To avoid allowing
+ /// accidental misuse, if this method is called after a "server" signs
+ /// a response, an exception of class \c TSIGContextError will be thrown.
+ ///
+ /// The \c record parameter can be NULL; in that case this method simply
+ /// returns \c FORMERR as the case described in Section 4.6 of RFC2845,
+ /// i.e., receiving an unsigned response to a signed request. This way
+ /// a client can transparently pass the result of
+ /// \c Message::getTSIGRecord() without checking whether it's non NULL
+ /// and take an appropriate action based on the result of this method.
+ ///
+ /// This method handles the given data mostly as opaque. It digests
+ /// the data assuming it begins with a DNS header and ends with a TSIG
+ /// RR whose length is given by calling \c TSIGRecord::getLength() on
+ /// \c record, but otherwise it doesn't parse the data to confirm the
+ /// assumption. It's caller's responsibility to ensure the data is
+ /// valid and consistent with \c record. To avoid disruption, this
+ /// method performs minimal validation on the given \c data and \c record:
+ /// \c data must not be NULL; \c data_len must not be smaller than the
+ /// sum of the DNS header length (fixed, 12 octets) and the length of
+ /// the TSIG RR. If this check fails it throws an \c InvalidParameter
+ /// exception.
+ ///
+ /// One unexpected case that is not covered by this method is that a
+ /// client receives a signed response to an unsigned request. RFC2845 is
+ /// silent about such cases; BIND 9 explicitly identifies the case and
+ /// rejects it. With this implementation, the client can know that the
+ /// response contains a TSIG via the result of
+ /// \c Message::getTSIGRecord() and that it is an unexpected TSIG due to
+ /// the fact that it doesn't have a corresponding \c TSIGContext.
+ /// It's up to the client implementation whether to react to such a case
+ /// explicitly (for example, it could either ignore the TSIG and accept
+ /// the response or drop it).
+ ///
+ /// This method provides the strong exception guarantee; unless the method
+ /// returns (without an exception being thrown), the internal state of
+ /// the \c TSIGContext won't be modified.
+ ///
+ /// \todo Support intermediate TCP DNS messages without TSIG (RFC2845 4.4)
+ /// \todo Signature truncation support based on RFC4635
+ ///
+ /// \exception TSIGContextError Context already signed a response.
+ /// \exception InvalidParameter \c data is NULL or \c data_len is too small.
+ ///
+ /// \param record The \c TSIGRecord to be verified with \c data
+ /// \param data Points to the wire-format data (exactly as received) to
+ /// be verified
+ /// \param data_len The length of \c data in bytes
+ /// \return The \c TSIGError that indicates verification result
+ TSIGError verify(const TSIGRecord* const record, const void* const data,
+ const size_t data_len);
+
+ /// Return the current state of the context
+ ///
+ /// \note
+ /// The states are visible in public mainly for testing purposes.
+ /// Normal applications won't have to deal with them.
+ ///
+ /// \exception None
+ State getState() const;
+
+ /// Return the TSIG error as a result of the latest verification
+ ///
+ /// This method can be called even before verifying anything, but the
+ /// returned value is meaningless in that case.
+ ///
+ /// \exception None
+ TSIGError getError() const;
+
+ /// \name Protocol constants and defaults
+ ///
+ //@{
+ /// The recommended fudge value (in seconds) by RFC2845.
+ ///
+ /// Right now fudge is not tunable, and all TSIGs generated by this API
+ /// will have this value of fudge.
+ static const uint16_t DEFAULT_FUDGE = 300;
+ //@}
+
+private:
+ struct TSIGContextImpl;
+ TSIGContextImpl* impl_;
+};
+}
+}
+
+#endif // __TSIG_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/tsigerror.cc b/src/lib/dns/tsigerror.cc
new file mode 100644
index 0000000..36ef47d
--- /dev/null
+++ b/src/lib/dns/tsigerror.cc
@@ -0,0 +1,68 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <ostream>
+#include <string>
+
+#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/rcode.h>
+#include <dns/tsigerror.h>
+
+namespace isc {
+namespace dns {
+namespace {
+const char* const tsigerror_text[] = {
+ "BADSIG",
+ "BADKEY",
+ "BADTIME"
+};
+}
+
+TSIGError::TSIGError(Rcode rcode) : code_(rcode.getCode()) {
+ if (code_ > MAX_RCODE_FOR_TSIGERROR) {
+ isc_throw(OutOfRange, "Invalid RCODE for TSIG Error: " << rcode);
+ }
+}
+
+std::string
+TSIGError::toText() const {
+ if (code_ <= MAX_RCODE_FOR_TSIGERROR) {
+ return (Rcode(code_).toText());
+ } else if (code_ <= BAD_TIME_CODE) {
+ return (tsigerror_text[code_ - (MAX_RCODE_FOR_TSIGERROR + 1)]);
+ } else {
+ return (boost::lexical_cast<std::string>(code_));
+ }
+}
+
+Rcode
+TSIGError::toRcode() const {
+ if (code_ <= MAX_RCODE_FOR_TSIGERROR) {
+ return (Rcode(code_));
+ }
+ if (code_ > BAD_TIME_CODE) {
+ return (Rcode::SERVFAIL());
+ }
+ return (Rcode::NOTAUTH());
+}
+
+std::ostream&
+operator<<(std::ostream& os, const TSIGError& error) {
+ return (os << error.toText());
+}
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/tsigerror.h b/src/lib/dns/tsigerror.h
new file mode 100644
index 0000000..8efd3ae
--- /dev/null
+++ b/src/lib/dns/tsigerror.h
@@ -0,0 +1,338 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __TSIGERROR_H
+#define __TSIGERROR_H 1
+
+#include <ostream>
+#include <string>
+
+#include <dns/rcode.h>
+
+namespace isc {
+namespace dns {
+/// TSIG errors
+///
+/// The \c TSIGError class objects represent standard errors related to
+/// TSIG protocol operations as defined in related specifications, mainly
+/// in RFC2845.
+class TSIGError {
+public:
+ /// Constants for pre-defined TSIG error values.
+ ///
+ /// Code values from 0 through 15 (inclusive) are derived from those of
+ /// RCODE and are not defined here. See the \c Rcode class.
+ ///
+ /// \note Unfortunately some systems define "BADSIG" as a macro in a public
+ /// header file. To avoid conflict with it we add an underscore to our
+ /// definitions.
+ enum CodeValue {
+ BAD_SIG_CODE = 16, ///< 16: TSIG verification failure
+ BAD_KEY_CODE = 17, ///< 17: TSIG key is not recognized
+ BAD_TIME_CODE = 18 ///< 18: Current time and time signed are too different
+ };
+
+ /// \name Constructors
+ ///
+ /// We use the default versions of destructor, copy constructor,
+ /// and assignment operator.
+ //@{
+ /// Constructor from the code value.
+ ///
+ /// \exception None
+ ///
+ /// \param error_code The underlying 16-bit error code value of the \c TSIGError.
+ explicit TSIGError(uint16_t error_code) : code_(error_code) {}
+
+ /// Constructor from \c Rcode.
+ ///
+ /// As defined in RFC2845, error code values from 0 to 15 (inclusive) are
+ /// derived from the DNS RCODEs, which are represented via the \c Rcode
+ /// class in this library. This constructor works as a converter from
+ /// these RCODEs to corresponding TSIGError objects.
+ ///
+ /// \exception isc::OutOfRange Given rcode is not convertible to
+ /// TSIGErrors.
+ ///
+ /// \param rcode the \c Rcode from which the TSIGError should be derived.
+ explicit TSIGError(Rcode rcode);
+ //@}
+
+ /// \brief Returns the \c TSIGCode error code value.
+ ///
+ /// \exception None
+ ///
+ /// \return The underlying code value corresponding to the \c TSIGError.
+ uint16_t getCode() const { return (code_); }
+
+ /// \brief Return true iff two \c TSIGError objects are equal.
+ ///
+ /// Two TSIGError objects are equal iff their error codes are equal.
+ ///
+ /// \exception None
+ ///
+ /// \param other the \c TSIGError object to compare against.
+ /// \return true if the two TSIGError are equal; otherwise false.
+ bool equals(const TSIGError& other) const
+ { return (code_ == other.code_); }
+
+ /// \brief Same as \c equals().
+ bool operator==(const TSIGError& other) const { return (equals(other)); }
+
+ /// \brief Return true iff two \c TSIGError objects are not equal.
+ ///
+ /// \exception None
+ ///
+ /// \param other the \c TSIGError object to compare against.
+ /// \return true if the two TSIGError objects are not equal;
+ /// otherwise false.
+ bool nequals(const TSIGError& other) const
+ { return (code_ != other.code_); }
+
+ /// \brief Same as \c nequals().
+ bool operator!=(const TSIGError& other) const { return (nequals(other)); }
+
+ /// \brief Convert the \c TSIGError to a string.
+ ///
+ /// For codes derived from RCODEs up to 15, this method returns the
+ /// same string as \c Rcode::toText() for the corresponding code.
+ /// For other pre-defined code values (see TSIGError::CodeValue),
+ /// this method returns a string representation of the "mnemonic' used
+ /// for the enum and constant objects as defined in RFC2845.
+ /// For example, the string for code value 16 is "BADSIG", etc.
+ /// For other code values it returns a string representation of the decimal
+ /// number of the value, e.g. "32", "100", etc.
+ ///
+ /// \exception std::bad_alloc Resource allocation for the string fails
+ ///
+ /// \return A string representation of the \c TSIGError.
+ std::string toText() const;
+
+ /// \brief Convert the \c TSIGError to a \c Rcode
+ ///
+ /// This method returns an \c Rcode object that is corresponding to
+ /// the TSIG error. The returned \c Rcode is expected to be used
+ /// by a verifying server to specify the RCODE of a response when
+ /// TSIG verification fails.
+ ///
+ /// Specifically, this method returns \c Rcode::NOTAUTH() for the
+ /// TSIG specific errors, BADSIG, BADKEY, BADTIME, as described in
+ /// RFC2845. For errors derived from the standard Rcode (code 0-15),
+ /// it returns the corresponding \c Rcode. For others, this method
+ /// returns \c Rcode::SERVFAIL() as a last resort.
+ ///
+ /// \exception None
+ Rcode toRcode() const;
+
+ /// A constant TSIG error object derived from \c Rcode::NOERROR()
+ static const TSIGError& NOERROR();
+
+ /// A constant TSIG error object derived from \c Rcode::FORMERR()
+ static const TSIGError& FORMERR();
+
+ /// A constant TSIG error object derived from \c Rcode::SERVFAIL()
+ static const TSIGError& SERVFAIL();
+
+ /// A constant TSIG error object derived from \c Rcode::NXDOMAIN()
+ static const TSIGError& NXDOMAIN();
+
+ /// A constant TSIG error object derived from \c Rcode::NOTIMP()
+ static const TSIGError& NOTIMP();
+
+ /// A constant TSIG error object derived from \c Rcode::REFUSED()
+ static const TSIGError& REFUSED();
+
+ /// A constant TSIG error object derived from \c Rcode::YXDOMAIN()
+ static const TSIGError& YXDOMAIN();
+
+ /// A constant TSIG error object derived from \c Rcode::YXRRSET()
+ static const TSIGError& YXRRSET();
+
+ /// A constant TSIG error object derived from \c Rcode::NXRRSET()
+ static const TSIGError& NXRRSET();
+
+ /// A constant TSIG error object derived from \c Rcode::NOTAUTH()
+ static const TSIGError& NOTAUTH();
+
+ /// A constant TSIG error object derived from \c Rcode::NOTZONE()
+ static const TSIGError& NOTZONE();
+
+ /// A constant TSIG error object derived from \c Rcode::RESERVED11()
+ static const TSIGError& RESERVED11();
+
+ /// A constant TSIG error object derived from \c Rcode::RESERVED12()
+ static const TSIGError& RESERVED12();
+
+ /// A constant TSIG error object derived from \c Rcode::RESERVED13()
+ static const TSIGError& RESERVED13();
+
+ /// A constant TSIG error object derived from \c Rcode::RESERVED14()
+ static const TSIGError& RESERVED14();
+
+ /// A constant TSIG error object derived from \c Rcode::RESERVED15()
+ static const TSIGError& RESERVED15();
+
+ /// A constant TSIG error object for the BADSIG code
+ /// (see \c TSIGError::BAD_SIG_CODE).
+ static const TSIGError& BAD_SIG();
+
+ /// A constant TSIG error object for the BADKEY code
+ /// (see \c TSIGError::BAD_KEY_CODE).
+ static const TSIGError& BAD_KEY();
+
+ /// A constant TSIG error object for the BADTIME code
+ /// (see \c TSIGError::BAD_TIME_CODE).
+ static const TSIGError& BAD_TIME();
+
+private:
+ // This is internally used to specify the maximum possible RCODE value
+ // that can be convertible to TSIGErrors.
+ static const int MAX_RCODE_FOR_TSIGERROR = 15;
+
+ uint16_t code_;
+};
+
+inline const TSIGError&
+TSIGError::NOERROR() {
+ static TSIGError e(Rcode::NOERROR());
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::FORMERR() {
+ static TSIGError e(Rcode::FORMERR());
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::SERVFAIL() {
+ static TSIGError e(Rcode::SERVFAIL());
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::NXDOMAIN() {
+ static TSIGError e(Rcode::NXDOMAIN());
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::NOTIMP() {
+ static TSIGError e(Rcode::NOTIMP());
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::REFUSED() {
+ static TSIGError e(Rcode::REFUSED());
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::YXDOMAIN() {
+ static TSIGError e(Rcode::YXDOMAIN());
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::YXRRSET() {
+ static TSIGError e(Rcode::YXRRSET());
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::NXRRSET() {
+ static TSIGError e(Rcode::NXRRSET());
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::NOTAUTH() {
+ static TSIGError e(Rcode::NOTAUTH());
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::NOTZONE() {
+ static TSIGError e(Rcode::NOTZONE());
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::RESERVED11() {
+ static TSIGError e(Rcode::RESERVED11());
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::RESERVED12() {
+ static TSIGError e(Rcode::RESERVED12());
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::RESERVED13() {
+ static TSIGError e(Rcode::RESERVED13());
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::RESERVED14() {
+ static TSIGError e(Rcode::RESERVED14());
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::RESERVED15() {
+ static TSIGError e(Rcode::RESERVED15());
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::BAD_SIG() {
+ static TSIGError e(BAD_SIG_CODE);
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::BAD_KEY() {
+ static TSIGError e(BAD_KEY_CODE);
+ return (e);
+}
+
+inline const TSIGError&
+TSIGError::BAD_TIME() {
+ static TSIGError e(BAD_TIME_CODE);
+ return (e);
+}
+
+/// Insert the \c TSIGError as a string into stream.
+///
+/// This method convert \c tsig_error into a string and inserts it into the
+/// output stream \c os.
+///
+/// \param os A \c std::ostream object on which the insertion operation is
+/// performed.
+/// \param tsig_error An \c TSIGError object output by the operation.
+/// \return A reference to the same \c std::ostream object referenced by
+/// parameter \c os after the insertion operation.
+std::ostream& operator<<(std::ostream& os, const TSIGError& tsig_error);
+}
+}
+
+#endif // __TSIGERROR_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/tsigkey.cc b/src/lib/dns/tsigkey.cc
index 057191d..d7d60eb 100644
--- a/src/lib/dns/tsigkey.cc
+++ b/src/lib/dns/tsigkey.cc
@@ -15,50 +15,134 @@
#include <map>
#include <utility>
#include <vector>
+#include <sstream>
#include <exceptions/exceptions.h>
+#include <cryptolink/cryptolink.h>
+
#include <dns/name.h>
+#include <util/encode/base64.h>
#include <dns/tsigkey.h>
using namespace std;
+using namespace isc::cryptolink;
namespace isc {
namespace dns {
+namespace {
+ HashAlgorithm
+ convertAlgorithmName(const isc::dns::Name& name) {
+ if (name == TSIGKey::HMACMD5_NAME()) {
+ return (isc::cryptolink::MD5);
+ }
+ if (name == TSIGKey::HMACSHA1_NAME()) {
+ return (isc::cryptolink::SHA1);
+ }
+ if (name == TSIGKey::HMACSHA256_NAME()) {
+ return (isc::cryptolink::SHA256);
+ }
+ if (name == TSIGKey::HMACSHA224_NAME()) {
+ return (isc::cryptolink::SHA224);
+ }
+ if (name == TSIGKey::HMACSHA384_NAME()) {
+ return (isc::cryptolink::SHA384);
+ }
+ if (name == TSIGKey::HMACSHA512_NAME()) {
+ return (isc::cryptolink::SHA512);
+ }
+
+ return (isc::cryptolink::UNKNOWN_HASH);
+ }
+}
+
struct
TSIGKey::TSIGKeyImpl {
TSIGKeyImpl(const Name& key_name, const Name& algorithm_name,
+ isc::cryptolink::HashAlgorithm algorithm,
const void* secret, size_t secret_len) :
key_name_(key_name), algorithm_name_(algorithm_name),
+ algorithm_(algorithm),
secret_(static_cast<const uint8_t*>(secret),
static_cast<const uint8_t*>(secret) + secret_len)
{
- // Convert the name to the canonical form.
+ // Convert the key and algorithm names to the canonical form.
+ key_name_.downcase();
algorithm_name_.downcase();
}
- const Name key_name_;
+ Name key_name_;
Name algorithm_name_;
+ const isc::cryptolink::HashAlgorithm algorithm_;
const vector<uint8_t> secret_;
};
TSIGKey::TSIGKey(const Name& key_name, const Name& algorithm_name,
const void* secret, size_t secret_len) : impl_(NULL)
{
- if (algorithm_name != HMACMD5_NAME() &&
- algorithm_name != HMACSHA1_NAME() &&
- algorithm_name != HMACSHA256_NAME()) {
- isc_throw(InvalidParameter, "Unknown TSIG algorithm is specified: " <<
- algorithm_name);
- }
+ const HashAlgorithm algorithm = convertAlgorithmName(algorithm_name);
if ((secret != NULL && secret_len == 0) ||
(secret == NULL && secret_len != 0)) {
isc_throw(InvalidParameter,
- "TSIGKey secret and its length are inconsistent");
+ "TSIGKey secret and its length are inconsistent: " <<
+ key_name << ":" << algorithm_name);
}
+ if (algorithm == isc::cryptolink::UNKNOWN_HASH && secret_len != 0) {
+ isc_throw(InvalidParameter,
+ "TSIGKey with unknown algorithm has non empty secret: " <<
+ key_name << ":" << algorithm_name);
+ }
+ impl_ = new TSIGKeyImpl(key_name, algorithm_name, algorithm, secret,
+ secret_len);
+}
+
+TSIGKey::TSIGKey(const std::string& str) : impl_(NULL) {
+ try {
+ istringstream iss(str);
- impl_ = new TSIGKeyImpl(key_name, algorithm_name, secret, secret_len);
+ string keyname_str;
+ getline(iss, keyname_str, ':');
+ if (iss.fail() || iss.bad() || iss.eof()) {
+ isc_throw(InvalidParameter, "Invalid TSIG key string: " << str);
+ }
+
+ string secret_str;
+ getline(iss, secret_str, ':');
+ if (iss.fail() || iss.bad()) {
+ isc_throw(InvalidParameter, "Invalid TSIG key string: " << str);
+ }
+
+ string algo_str;
+ if (!iss.eof()) {
+ getline(iss, algo_str);
+ }
+ if (iss.fail() || iss.bad()) {
+ isc_throw(InvalidParameter, "Invalid TSIG key string: " << str);
+ }
+
+ const Name algo_name(algo_str.empty() ? "hmac-md5.sig-alg.reg.int" :
+ algo_str);
+ const HashAlgorithm algorithm = convertAlgorithmName(algo_name);
+
+ vector<uint8_t> secret;
+ isc::util::encode::decodeBase64(secret_str, secret);
+
+ if (algorithm == isc::cryptolink::UNKNOWN_HASH && !secret.empty()) {
+ isc_throw(InvalidParameter,
+ "TSIG key with unknown algorithm has non empty secret: "
+ << str);
+ }
+
+ impl_ = new TSIGKeyImpl(Name(keyname_str), algo_name, algorithm,
+ secret.empty() ? NULL : &secret[0],
+ secret.size());
+ } catch (const Exception& e) {
+ // 'reduce' the several types of exceptions name parsing and
+ // Base64 decoding can throw to just the InvalidParameter
+ isc_throw(InvalidParameter, e.what());
+ }
}
+
TSIGKey::TSIGKey(const TSIGKey& source) : impl_(new TSIGKeyImpl(*source.impl_))
{}
@@ -89,6 +173,11 @@ TSIGKey::getAlgorithmName() const {
return (impl_->algorithm_name_);
}
+isc::cryptolink::HashAlgorithm
+TSIGKey::getAlgorithm() const {
+ return (impl_->algorithm_);
+}
+
const void*
TSIGKey::getSecret() const {
return ((impl_->secret_.size() > 0) ? &impl_->secret_[0] : NULL);
@@ -99,6 +188,17 @@ TSIGKey::getSecretLength() const {
return (impl_->secret_.size());
}
+std::string
+TSIGKey::toText() const {
+ const vector<uint8_t> secret_v(static_cast<const uint8_t*>(getSecret()),
+ static_cast<const uint8_t*>(getSecret()) +
+ getSecretLength());
+ std::string secret_str = isc::util::encode::encodeBase64(secret_v);
+
+ return (getKeyName().toText() + ":" + secret_str + ":" +
+ getAlgorithmName().toText());
+}
+
const
Name& TSIGKey::HMACMD5_NAME() {
static Name alg_name("hmac-md5.sig-alg.reg.int");
@@ -117,6 +217,24 @@ Name& TSIGKey::HMACSHA256_NAME() {
return (alg_name);
}
+const
+Name& TSIGKey::HMACSHA224_NAME() {
+ static Name alg_name("hmac-sha224");
+ return (alg_name);
+}
+
+const
+Name& TSIGKey::HMACSHA384_NAME() {
+ static Name alg_name("hmac-sha384");
+ return (alg_name);
+}
+
+const
+Name& TSIGKey::HMACSHA512_NAME() {
+ static Name alg_name("hmac-sha512");
+ return (alg_name);
+}
+
struct TSIGKeyRing::TSIGKeyRingImpl {
typedef map<Name, TSIGKey> TSIGKeyMap;
typedef pair<Name, TSIGKey> NameAndKey;
@@ -152,10 +270,11 @@ TSIGKeyRing::remove(const Name& key_name) {
}
TSIGKeyRing::FindResult
-TSIGKeyRing::find(const Name& key_name) {
+TSIGKeyRing::find(const Name& key_name, const Name& algorithm_name) const {
TSIGKeyRingImpl::TSIGKeyMap::const_iterator found =
impl_->keys.find(key_name);
- if (found == impl_->keys.end()) {
+ if (found == impl_->keys.end() ||
+ (*found).second.getAlgorithmName() != algorithm_name) {
return (FindResult(NOTFOUND, NULL));
}
return (FindResult(SUCCESS, &((*found).second)));
diff --git a/src/lib/dns/tsigkey.h b/src/lib/dns/tsigkey.h
index e56fa88..31211d1 100644
--- a/src/lib/dns/tsigkey.h
+++ b/src/lib/dns/tsigkey.h
@@ -15,6 +15,8 @@
#ifndef __TSIGKEY_H
#define __TSIGKEY_H 1
+#include <cryptolink/cryptolink.h>
+
namespace isc {
namespace dns {
@@ -66,12 +68,30 @@ public:
//@{
/// \brief Constructor from key parameters
///
- /// In the current implementation, \c algorithm_name must be a known
- /// algorithm to this implementation, which are defined via the
- /// <code>static const</code> member functions. For other names
- /// an exception of class \c InvalidParameter will be thrown.
- /// Note: This restriction may be too strict, and we may revisit it
- /// later.
+ /// \c algorithm_name should generally be a known algorithm to this
+ /// implementation, which are defined via the
+ /// <code>static const</code> member functions.
+ ///
+ /// Other names are still accepted as long as the secret is empty
+ /// (\c secret is \c NULL and \c secret_len is 0), however; in some cases
+ /// we might want to treat just the pair of key name and algorithm name
+ /// opaquely, e.g., when generating a response TSIG with a BADKEY error
+ /// because the algorithm is unknown as specified in Section 3.2 of
+ /// RFC2845 (in which case the algorithm name would be copied from the
+ /// request to the response, and for that purpose it would be convenient
+ /// if a \c TSIGKey object can hold a name for an "unknown" algorithm).
+ ///
+ /// \note RFC2845 does not specify which algorithm name should be used
+ /// in such a BADKEY response. The behavior of using the same algorithm
+ /// is derived from the BIND 9 implementation.
+ ///
+ /// It is unlikely that a TSIG key with an unknown algorithm is of any
+ /// use with actual crypto operation, so care must be taken when dealing
+ /// with such keys. (The restriction for the secret will prevent
+ /// accidental creation of such a dangerous key, e.g., due to misspelling
+ /// in a configuration file).
+ /// If the given algorithm name is unknown and non empty secret is
+ /// specified, an exception of type \c InvalidParameter will be thrown.
///
/// \c secret and \c secret_len must be consistent in that the latter
/// is 0 if and only if the former is \c NULL;
@@ -90,6 +110,28 @@ public:
TSIGKey(const Name& key_name, const Name& algorithm_name,
const void* secret, size_t secret_len);
+ /// \brief Constructor from an input string
+ ///
+ /// The string must be of the form:
+ /// <name>:<secret>[:<algorithm>]
+ /// Where <name> is a domain name for the key, <secret> is a
+ /// base64 representation of the key secret, and the optional
+ /// algorithm is an algorithm identifier as specified in RFC4635.
+ /// The default algorithm is hmac-md5.sig-alg.reg.int.
+ ///
+ /// The same restriction about the algorithm name (and secret) as that
+ /// for the other constructor applies.
+ ///
+ /// Since ':' is used as a separator here, it is not possible to
+ /// use this constructor to create keys with a ':' character in
+ /// their name.
+ ///
+ /// \exception InvalidParameter exception if the input string is
+ /// invalid.
+ ///
+ /// \param str The string to make a TSIGKey from
+ explicit TSIGKey(const std::string& str);
+
/// \brief The copy constructor.
///
/// It internally allocates a resource, and if it fails a corresponding
@@ -123,6 +165,9 @@ public:
/// Return the algorithm name.
const Name& getAlgorithmName() const;
+ /// Return the hash algorithm name in the form of cryptolink::HashAlgorithm
+ isc::cryptolink::HashAlgorithm getAlgorithm() const;
+
/// Return the length of the TSIG secret in bytes.
size_t getSecretLength() const;
@@ -139,6 +184,18 @@ public:
const void* getSecret() const;
//@}
+ /// \brief Converts the TSIGKey to a string value
+ ///
+ /// The resulting string will be of the form
+ /// name:secret:algorithm
+ /// Where <name> is a domain name for the key, <secret> is a
+ /// base64 representation of the key secret, and algorithm is
+ /// an algorithm identifier as specified in RFC4635
+ ///
+ /// \param key the TSIG key to convert
+ /// \return The string representation of the given TSIGKey.
+ std::string toText() const;
+
///
/// \name Well known algorithm names as defined in RFC2845 and RFC4635.
///
@@ -149,6 +206,9 @@ public:
static const Name& HMACMD5_NAME(); ///< HMAC-MD5 (RFC2845)
static const Name& HMACSHA1_NAME(); ///< HMAC-SHA1 (RFC4635)
static const Name& HMACSHA256_NAME(); ///< HMAC-SHA256 (RFC4635)
+ static const Name& HMACSHA224_NAME(); ///< HMAC-SHA256 (RFC4635)
+ static const Name& HMACSHA384_NAME(); ///< HMAC-SHA256 (RFC4635)
+ static const Name& HMACSHA512_NAME(); ///< HMAC-SHA256 (RFC4635)
//@}
private:
@@ -268,7 +328,9 @@ public:
/// Find a \c TSIGKey for the given name in the \c TSIGKeyRing.
///
/// It searches the internal storage for a \c TSIGKey whose name is
- /// \c key_name, and returns the result in the form of a \c FindResult
+ /// \c key_name and that uses the hash algorithm identified by
+ /// \c algorithm_name.
+ /// It returns the result in the form of a \c FindResult
/// object as follows:
/// - \c code: \c SUCCESS if a key is found; otherwise \c NOTFOUND.
/// - \c key: A pointer to the found \c TSIGKey object if one is found;
@@ -282,8 +344,9 @@ public:
/// This method never throws an exception.
///
/// \param key_name The name of the key to be found.
+ /// \param algorithm_name The name of the algorithm of the found key.
/// \return A \c FindResult object enclosing the search result (see above).
- FindResult find(const Name& key_name);
+ FindResult find(const Name& key_name, const Name& algorithm_name) const;
private:
struct TSIGKeyRingImpl;
TSIGKeyRingImpl* impl_;
diff --git a/src/lib/dns/tsigrecord.cc b/src/lib/dns/tsigrecord.cc
new file mode 100644
index 0000000..9dd3f78
--- /dev/null
+++ b/src/lib/dns/tsigrecord.cc
@@ -0,0 +1,147 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <ostream>
+#include <string>
+
+#include <util/buffer.h>
+
+#include <dns/exceptions.h>
+#include <dns/messagerenderer.h>
+#include <dns/rrclass.h>
+#include <dns/rrttl.h>
+#include <dns/tsigrecord.h>
+
+using namespace isc::util;
+using namespace isc::dns::rdata;
+
+namespace {
+// Internally used constants:
+
+// Size in octets for the RR type, class TTL, RDLEN fields.
+const size_t RR_COMMON_LEN = 10;
+
+// Size in octets for the fixed part of TSIG RDATAs.
+// - Time Signed (6)
+// - Fudge (2)
+// - MAC Size (2)
+// - Original ID (2)
+// - Error (2)
+// - Other Len (2)
+const size_t RDATA_COMMON_LEN = 16;
+}
+
+namespace isc {
+namespace dns {
+TSIGRecord::TSIGRecord(const Name& key_name,
+ const rdata::any::TSIG& tsig_rdata) :
+ key_name_(key_name), rdata_(tsig_rdata),
+ length_(RR_COMMON_LEN + RDATA_COMMON_LEN + key_name_.getLength() +
+ rdata_.getAlgorithm().getLength() +
+ rdata_.getMACSize() + rdata_.getOtherLen())
+{}
+
+namespace {
+// This is a straightforward wrapper of dynamic_cast<const any::TSIG&>.
+// We use this so that we can throw the DNSMessageFORMERR exception when
+// unexpected type of RDATA is detected in the member initialization list
+// of the constructor below.
+const any::TSIG&
+castToTSIGRdata(const rdata::Rdata& rdata) {
+ try {
+ return (dynamic_cast<const any::TSIG&>(rdata));
+ } catch (std::bad_cast&) {
+ isc_throw(DNSMessageFORMERR,
+ "TSIG record is being constructed from "
+ "incompatible RDATA:" << rdata.toText());
+ }
+}
+}
+
+TSIGRecord::TSIGRecord(const Name& name, const RRClass& rrclass,
+ const RRTTL& ttl, const rdata::Rdata& rdata,
+ size_t length) :
+ key_name_(name), rdata_(castToTSIGRdata(rdata)), length_(length)
+{
+ if (rrclass != getClass()) {
+ isc_throw(DNSMessageFORMERR, "Unexpected TSIG RR class: " << rrclass);
+ }
+ if (ttl != RRTTL(TSIG_TTL)) {
+ isc_throw(DNSMessageFORMERR, "Unexpected TSIG TTL: " << ttl);
+ }
+}
+
+const RRClass&
+TSIGRecord::getClass() {
+ return (RRClass::ANY());
+}
+
+const RRTTL&
+TSIGRecord::getTTL() {
+ static RRTTL ttl(TSIG_TTL);
+ return (ttl);
+}
+
+namespace {
+template <typename OUTPUT>
+void
+toWireCommon(OUTPUT& output, const rdata::any::TSIG& rdata) {
+ // RR type, class, TTL are fixed constants.
+ RRType::TSIG().toWire(output);
+ TSIGRecord::getClass().toWire(output);
+ output.writeUint32(TSIGRecord::TSIG_TTL);
+
+ // RDLEN
+ output.writeUint16(RDATA_COMMON_LEN + rdata.getAlgorithm().getLength() +
+ rdata.getMACSize() + rdata.getOtherLen());
+
+ // TSIG RDATA
+ rdata.toWire(output);
+}
+}
+
+int
+TSIGRecord::toWire(AbstractMessageRenderer& renderer) const {
+ // If adding the TSIG would exceed the size limit, don't do it.
+ if (renderer.getLength() + length_ > renderer.getLengthLimit()) {
+ renderer.setTruncated();
+ return (0);
+ }
+
+ // key name = owner. note that we disable compression.
+ renderer.writeName(key_name_, false);
+ toWireCommon(renderer, rdata_);
+ return (1);
+}
+
+int
+TSIGRecord::toWire(OutputBuffer& buffer) const {
+ key_name_.toWire(buffer);
+ toWireCommon(buffer, rdata_);
+ return (1);
+}
+
+std::string
+TSIGRecord::toText() const {
+ return (key_name_.toText() + " " + RRTTL(TSIG_TTL).toText() + " " +
+ getClass().toText() + " " + RRType::TSIG().toText() + " " +
+ rdata_.toText() + "\n");
+}
+
+std::ostream&
+operator<<(std::ostream& os, const TSIGRecord& record) {
+ return (os << record.toText());
+}
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/tsigrecord.h b/src/lib/dns/tsigrecord.h
new file mode 100644
index 0000000..03de746
--- /dev/null
+++ b/src/lib/dns/tsigrecord.h
@@ -0,0 +1,308 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __TSIGRECORD_H
+#define __TSIGRECORD_H 1
+
+#include <ostream>
+#include <string>
+
+#include <boost/shared_ptr.hpp>
+
+#include <util/buffer.h>
+
+#include <dns/name.h>
+#include <dns/rdataclass.h>
+
+namespace isc {
+namespace util {
+class OutputBuffer;
+}
+namespace dns {
+class AbstractMessageRenderer;
+
+/// TSIG resource record.
+///
+/// A \c TSIGRecord class object represents a TSIG resource record and is
+/// responsible for conversion to and from wire format TSIG record based on
+/// the protocol specification (RFC2845).
+/// This class is provided so that other classes and applications can handle
+/// TSIG without knowing protocol details of TSIG, such as that it uses a
+/// fixed constant of TTL.
+///
+/// \todo So the plan is to eventually provide the "from wire" constructor.
+/// It's not yet provided in the current phase of development.
+///
+/// \note
+/// This class could be a derived class of \c AbstractRRset. That way
+/// it would be able to be used in a polymorphic way; for example,
+/// an application can construct a TSIG RR by itself and insert it to a
+/// \c Message object as a generic RRset. On the other hand, it would mean
+/// this class would have to implement an \c RdataIterator (even though it
+/// can be done via straightforward forwarding) while the iterator is mostly
+/// redundant since there should be one and only one RDATA for a valid TSIG
+/// RR. Likewise, some methods such as \c setTTL() method wouldn't be well
+/// defined due to such special rules for TSIG as using a fixed TTL.
+/// Overall, TSIG is a very special RR type that simply uses the compatible
+/// resource record format, and it will be unlikely that a user wants to
+/// handle it through a generic interface in a polymorphic way.
+/// We therefore chose to define it as a separate class. This is also
+/// similar to why \c EDNS is a separate class.
+class TSIGRecord {
+public:
+ ///
+ /// \name Constructors
+ ///
+ /// We use the default copy constructor, default copy assignment operator,
+ /// (and default destructor) intentionally.
+ //@{
+ /// Constructor from TSIG key name and RDATA
+ ///
+ /// \exception std::bad_alloc Resource allocation for copying the name or
+ /// RDATA fails
+ TSIGRecord(const Name& key_name, const rdata::any::TSIG& tsig_rdata);
+
+ /// Constructor from resource record (RR) parameters.
+ ///
+ /// This constructor is intended to be used in the context of parsing
+ /// an incoming DNS message that contains a TSIG. The parser would
+ /// first extract the owner name, RR type (which is TSIG) class, TTL and
+ /// the TSIG RDATA from the message. This constructor is expected to
+ /// be given these RR parameters (except the RR type, because it must be
+ /// TSIG).
+ ///
+ /// According to RFC2845, a TSIG RR uses fixed RR class (ANY) and TTL (0).
+ /// If the RR class or TTL is different from the expected one, this
+ /// implementation considers it an invalid record and throws an exception
+ /// of class \c DNSMessageFORMERR.
+ ///
+ /// \note This behavior is not specified in the protocol specification,
+ /// but this implementation rejects unexpected values for the following
+ /// reasons (but in any case, this won't matter much in practice as
+ /// RFC2848 clearly states these fields always have the fixed values and
+ /// any sane implementation of TSIG signer will follow that):
+ /// According to the RFC editor (in a private communication), the intended
+ /// use of the TSIG TTL field is to signal protocol extensions (currently
+ /// no such extension is defined), so this field may actually be
+ /// validly non 0 in future. However, until the implementation supports
+ /// that extension it may not always be able to handle the extended
+ /// TSIG as intended; the extension may even affect digest computation.
+ /// There's a related issue on this point. Different implementations
+ /// interpret the RFC in different ways on the received TTL when
+ /// digesting the message: BIND 9 uses the received value (even if
+ /// it's not 0) as part of the TSIG variables; NLnet Labs' LDNS and NSD
+ /// always use a fixed constant of 0 regardless of the received TTL value.
+ /// This means if and when an extension with non 0 TTL is introduced
+ /// there will be interoperability problems in the form of verification
+ /// failure. By explicitly rejecting it (and subsequently returning
+ /// a response with a format error) we can indicate the source of the
+ /// problem more clearly than a "bad signature" TSIG error, which can
+ /// happen for various reasons. On the other hand, rejecting unexpected
+ /// RR classes is mostly for consistency; the RFC lists these two fields
+ /// in the same way, so it makes more sense to handle them equally.
+ /// (BIND 9 rejects unexpected RR classes for TSIG, but that is part of
+ /// general check on RR classes on received RRs; it generally requests
+ /// all classes are the same, and if the protocol specifies the use of
+ /// a particular class for a particular type of RR, it requests that
+ /// class be used).
+ ///
+ /// Likewise, if \c rdata is not of type \c any::TSIG, an exception of
+ /// class DNSMessageFORMERR will be thrown. When the caller is a
+ /// DNS message parser and builds \c rdata from incoming wire format
+ /// data as described above, this case happens when the RR class is
+ /// different from ANY (in the implementation, the type check takes place
+ /// before the explicit check against the RR class explained in the
+ /// previous paragraph).
+ ///
+ /// The \c length parameter is intended to be the length of the TSIG RR
+ /// (from the beginning of the owner name to the end of the RDATA) when
+ /// the caller is a DNS message parser. Note that it is the actual length
+ /// for the RR in the format; if the owner name or the algorithm name
+ /// (in the RDATA) is compressed (although the latter should not be
+ /// compressed according to RFC3597), the length must be the size of the
+ /// compressed data. The length is recorded inside the class and will
+ /// be returned via subsequent calls to \c getLength(). It's intended to
+ /// be used in the context TSIG verification; in the verify process
+ /// the MAC computation must be performed for the original data without
+ /// TSIG, so, to avoid parsing the entire data in the verify process
+ /// again, it's necessary to record information that can identify the
+ /// length to be digested for the MAC. This parameter serves for that
+ /// purpose.
+ ///
+ /// \note Since the constructor doesn't take the wire format data per se,
+ /// it doesn't (and cannot) check the validity of \c length, and simply
+ /// accepts any given value. It even accepts obviously invalid values
+ /// such as 0. It's caller's responsibility to provide a valid value of
+ /// length, and, the verifier's responsibility to use the length safely.
+ ///
+ /// <b>DISCUSSION:</b> this design is fragile in that it introduces
+ /// a tight coupling between message parsing and TSIG verification via
+ /// the \c TSIGRecord class. In terms of responsibility decoupling,
+ /// the ideal way to have \c TSIGRecord remember the entire wire data
+ /// along with the length of the TSIG. Then in the TSIG verification
+ /// we could refer to the necessary potion of data solely from a
+ /// \c TSIGRecord object. However, this approach would require expensive
+ /// heavy copy of the original data or introduce another kind of coupling
+ /// between the data holder and this class (if the original data is freed
+ /// while a \c TSIGRecord object referencing the data still exists, the
+ /// result will be catastrophic). As a "best current compromise", we
+ /// use the current design. We may reconsider it if it turns out to
+ /// cause a big problem or we come up with a better idea.
+ ///
+ /// \exception DNSMessageFORMERR Given RR parameters are invalid for TSIG.
+ /// \exception std::bad_alloc Internal resource allocation fails.
+ ///
+ /// \param name The owner name of the TSIG RR
+ /// \param rrclass The RR class of the RR. Must be \c RRClass::ANY()
+ /// (see above)
+ /// \param ttl The TTL of the RR. Must be 0 (see above)
+ /// \param rdata The RDATA of the RR. Must be of type \c any::TSIG.
+ /// \param length The size of the RR (see above)
+ TSIGRecord(const Name& name, const RRClass& rrclass, const RRTTL& ttl,
+ const rdata::Rdata& rdata, size_t length);
+ //@}
+
+ /// Return the owner name of the TSIG RR, which is the TSIG key name
+ ///
+ /// \exception None
+ const Name& getName() const { return (key_name_); }
+
+ /// Return the RDATA of the TSIG RR
+ ///
+ /// \exception None
+ const rdata::any::TSIG& getRdata() const { return (rdata_); }
+
+ /// \name Protocol constants and defaults
+ ///
+ //@{
+ /// Return the RR class of TSIG
+ ///
+ /// TSIG always uses the ANY RR class. This static method returns it,
+ /// when, though unlikely, an application wants to know which class TSIG
+ /// is supposed to use.
+ ///
+ /// \exception None
+ static const RRClass& getClass();
+
+ /// Return the TTL value of TSIG
+ ///
+ /// TSIG always uses 0 TTL. This static method returns it,
+ /// when, though unlikely, an application wants to know the TTL TSIG
+ /// is supposed to use.
+ ///
+ /// \exception None
+ static const RRTTL& getTTL();
+ //@}
+
+ /// Return the length of the TSIG record
+ ///
+ /// When constructed from the key name and RDATA, it is the length of
+ /// the record when it is rendered by the \c toWire() method.
+ ///
+ /// \note When constructed "from wire", that will mean the length of
+ /// the wire format data for the TSIG RR. The length will be necessary
+ /// to verify the message once parse is completed.
+ ///
+ /// \exception None
+ size_t getLength() const { return (length_); }
+
+ /// \brief Render the \c TSIG RR in the wire format.
+ ///
+ /// This method renders the TSIG record as a form of a DNS TSIG RR
+ /// via \c renderer, which encapsulates output buffer and other rendering
+ /// contexts.
+ ///
+ /// Normally this version of \c toWire() method tries to compress the
+ /// owner name of the RR whenever possible, but this method intentionally
+ /// skips owner name compression. This is due to a report that some
+ /// Windows clients refuse a TSIG if its owner name is compressed
+ /// (See http://marc.info/?l=bind-workers&m=126637138430819&w=2).
+ /// Reportedly this seemed to be specific to GSS-TSIG, but this
+ /// implementation skip compression regardless of the algorithm.
+ ///
+ /// If by adding the TSIG RR the message size would exceed the limit
+ /// maintained in \c renderer, this method skips rendering the RR
+ /// and returns 0 and mark \c renderer as "truncated" (so that a
+ /// subsequent call to \c isTruncated() on \c renderer will result in
+ /// \c true); otherwise it returns 1, which is the number of RR
+ /// rendered.
+ ///
+ /// \note If the caller follows the specification of adding TSIG
+ /// as described in RFC2845, this should not happen; the caller is
+ /// generally expected to leave a sufficient room in the message for
+ /// the TSIG. But this method checks the unexpected case nevertheless.
+ ///
+ /// \exception std::bad_alloc Internal resource allocation fails (this
+ /// should be rare).
+ ///
+ /// \param renderer DNS message rendering context that encapsulates the
+ /// output buffer and name compression information.
+ /// \return 1 if the TSIG RR fits in the message size limit; otherwise 0.
+ int toWire(AbstractMessageRenderer& renderer) const;
+
+ /// \brief Render the \c TSIG RR in the wire format.
+ ///
+ /// This method is same as \c toWire(AbstractMessageRenderer&)const
+ /// except it renders the RR in an \c OutputBuffer and therefore
+ /// does not care about message size limit.
+ /// As a consequence it always returns 1.
+ int toWire(isc::util::OutputBuffer& buffer) const;
+
+ /// Convert the TSIG record to a string.
+ ///
+ /// The output format is the same as the result of \c toText() for
+ /// other normal types of RRsets (with always using the same RR class
+ /// and TTL). It also ends with a newline.
+ ///
+ /// \exception std::bad_alloc Internal resource allocation fails (this
+ /// should be rare).
+ ///
+ /// \return A string representation of \c TSIG record
+ std::string toText() const;
+
+ /// The TTL value to be used in TSIG RRs.
+ static const uint32_t TSIG_TTL = 0;
+ //@}
+
+private:
+ const Name key_name_;
+ const rdata::any::TSIG rdata_;
+ const size_t length_;
+};
+
+/// A pointer-like type pointing to a \c TSIGRecord object.
+typedef boost::shared_ptr<TSIGRecord> TSIGRecordPtr;
+
+/// A pointer-like type pointing to an immutable \c TSIGRecord object.
+typedef boost::shared_ptr<const TSIGRecord> ConstTSIGRecordPtr;
+
+/// Insert the \c TSIGRecord as a string into stream.
+///
+/// This method convert \c record into a string and inserts it into the
+/// output stream \c os.
+///
+/// \param os A \c std::ostream object on which the insertion operation is
+/// performed.
+/// \param record A \c TSIGRecord object output by the operation.
+/// \return A reference to the same \c std::ostream object referenced by
+/// parameter \c os after the insertion operation.
+std::ostream& operator<<(std::ostream& os, const TSIGRecord& record);
+}
+}
+
+#endif // __TSIGRECORD_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/util/README b/src/lib/dns/util/README
deleted file mode 100644
index 1b9d1e8..0000000
--- a/src/lib/dns/util/README
+++ /dev/null
@@ -1,31 +0,0 @@
-This "util" directory is provided for utility header files and
-implementations that are internally used in the DNS library
-(libdns++).
-
-The functionality provided in these tools is generally available in
-other external or perhaps system supplied libraries. The basic
-development policy of BIND 10 is to avoid "reinventing wheels" unless
-they belong to the exact technology area that BIND 10 targets (e.g.,
-DNS). However, libdns++ is a very core part of BIND 10, and is also
-intended to be used as a public library, so dependency from libdns++
-to external libraries should be minimized. The utilities in this
-directory are provided balancing two policies and as a kind of
-compromise.
-
-The header files in this directory are therefore not intended to be
-installed. Likewise, classes and public functions defined in this
-directory are not intended to be used outside libdns++, although we
-cannot prohibit it at the language level.
-
-They are not even expected to be used in other modules of BIND 10 than
-libdns++ based on the basic policy explained above. Other modules
-should only rely on the DNS specific interface that may internally
-rely on these utility interfaces, or should use external libraries if
-the other module really needs to use the utility feature directly.
-There seem to be some violations as of this writing, but we should
-eventually fix it. A notable example is the SHA1 interfaces. They
-are defined here in the context of NSEC3 processing, but, in fact,
-they are not even used from any of the other libdns++ classes or
-functions. The SHA1 related interfaces should be moved to the
-application that needs it or the application should only access it
-through DNS specific interfaces defined in libdns++.
diff --git a/src/lib/dns/util/base16_from_binary.h b/src/lib/dns/util/base16_from_binary.h
deleted file mode 100644
index 8606c61..0000000
--- a/src/lib/dns/util/base16_from_binary.h
+++ /dev/null
@@ -1,108 +0,0 @@
-#ifndef BOOST_ARCHIVE_ITERATORS_BASE16_FROM_BINARY_HPP
-#define BOOST_ARCHIVE_ITERATORS_BASE16_FROM_BINARY_HPP
-
-// MS compatible compilers support #pragma once
-#if defined(_MSC_VER) && (_MSC_VER >= 1020)
-# pragma once
-#endif
-
-/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
-// base16_from_binary.h (derived from boost base64_from_binary.hpp)
-
-// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
-// Use, modification and distribution is subject to the Boost Software
-// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-
-// See http://www.boost.org for updates, documentation, and revision history.
-
-#include <cassert>
-
-#include <cstddef> // size_t
-#include <boost/config.hpp> // for BOOST_DEDUCED_TYPENAME
-#if defined(BOOST_NO_STDC_NAMESPACE)
-namespace std{
- using ::size_t;
-} // namespace std
-#endif
-
-// See base32hex_from_binary.h for why we need base64_from...hpp here.
-#include <boost/archive/iterators/base64_from_binary.hpp>
-
-namespace boost {
-namespace archive {
-namespace iterators {
-
-/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
-// convert binary integers to base16 characters
-
-namespace detail {
-
-template<class CharType>
-struct from_4_bit {
- typedef CharType result_type;
- CharType operator()(CharType t) const{
- const char * lookup_table =
- "0123456789"
- "ABCDEF";
- assert(t < 16);
- return (lookup_table[static_cast<size_t>(t)]);
- }
-};
-
-} // namespace detail
-
-// note: what we would like to do is
-// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
-// typedef transform_iterator<
-// from_4_bit<CharType>,
-// transform_width<Base, 4, sizeof(Base::value_type) * 8, CharType>
-// > base16_from_binary;
-// but C++ won't accept this. Rather than using a "type generator" and
-// using a different syntax, make a derivation which should be equivalent.
-//
-// Another issue addressed here is that the transform_iterator doesn't have
-// a templated constructor. This makes it incompatible with the dataflow
-// ideal. This is also addressed here.
-
-//template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
-template<
- class Base,
- class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
->
-class base16_from_binary :
- public transform_iterator<
- detail::from_4_bit<CharType>,
- Base
- >
-{
- friend class boost::iterator_core_access;
- typedef transform_iterator<
- BOOST_DEDUCED_TYPENAME detail::from_4_bit<CharType>,
- Base
- > super_t;
-
-public:
- // make composible buy using templated constructor
- template<class T>
- base16_from_binary(BOOST_PFTO_WRAPPER(T) start) :
- super_t(
- Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))),
- detail::from_4_bit<CharType>()
- )
- {}
- // intel 7.1 doesn't like default copy constructor
- base16_from_binary(const base16_from_binary & rhs) :
- super_t(
- Base(rhs.base_reference()),
- detail::from_4_bit<CharType>()
- )
- {}
-// base16_from_binary(){};
-};
-
-} // namespace iterators
-} // namespace archive
-} // namespace boost
-
-#endif // BOOST_ARCHIVE_ITERATORS_BASE16_FROM_BINARY_HPP
diff --git a/src/lib/dns/util/base32hex.h b/src/lib/dns/util/base32hex.h
deleted file mode 100644
index cba172e..0000000
--- a/src/lib/dns/util/base32hex.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2009 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 __BASE32HEX_H
-#define __BASE32HEX_H 1
-
-#include <stdint.h>
-#include <string>
-#include <vector>
-
-//
-// Note: this helper module isn't specific to the DNS protocol per se.
-// We should probably move this to somewhere else, possibly in some common
-// utility area.
-//
-
-namespace isc {
-namespace dns {
-
-/// \brief Encode binary data in the base32hex format.
-///
-/// The underlying implementation is shared with \c encodeBase64, and all
-/// description except the format (base32hex) equally applies.
-///
-/// Note: the encoding format is base32hex, not base32.
-///
-/// \param binary A vector object storing the data to be encoded.
-/// \return A newly created string that stores base32hex encoded value for
-/// binary.
-std::string encodeBase32Hex(const std::vector<uint8_t>& binary);
-
-/// \brief Decode a text encoded in the base32hex format into the
-/// original %data.
-///
-/// The underlying implementation is shared with \c decodeBase64, and all
-/// description except the format (base32hex) equally applies.
-///
-/// Note: the encoding format is base32hex, not base32.
-///
-/// \param input A text encoded in the base32hex format.
-/// \param result A vector in which the decoded %data is to be stored.
-void decodeBase32Hex(const std::string& input, std::vector<uint8_t>& result);
-}
-}
-
-#endif // __BASE32HEX_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/dns/util/base32hex_from_binary.h b/src/lib/dns/util/base32hex_from_binary.h
deleted file mode 100644
index 5d16d04..0000000
--- a/src/lib/dns/util/base32hex_from_binary.h
+++ /dev/null
@@ -1,110 +0,0 @@
-#ifndef BOOST_ARCHIVE_ITERATORS_BASE32HEX_FROM_BINARY_HPP
-#define BOOST_ARCHIVE_ITERATORS_BASE32HEX_FROM_BINARY_HPP
-
-// MS compatible compilers support #pragma once
-#if defined(_MSC_VER) && (_MSC_VER >= 1020)
-# pragma once
-#endif
-
-/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
-// base32hex_from_binary.h (derived from boost base64_from_binary.hpp)
-
-// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
-// Use, modification and distribution is subject to the Boost Software
-// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-
-// See http://www.boost.org for updates, documentation, and revision history.
-
-#include <cassert>
-
-#include <cstddef> // size_t
-#include <boost/config.hpp> // for BOOST_DEDUCED_TYPENAME
-#if defined(BOOST_NO_STDC_NAMESPACE)
-namespace std{
- using ::size_t;
-} // namespace std
-#endif
-
-// We use the same boost header files used in "base64_from_". Since the
-// precise path to these headers may vary depending on the boost version we
-// simply include the base64 header here.
-#include <boost/archive/iterators/base64_from_binary.hpp>
-
-namespace boost {
-namespace archive {
-namespace iterators {
-
-/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
-// convert binary integers to base32hex characters
-
-namespace detail {
-
-template<class CharType>
-struct from_5_bit {
- typedef CharType result_type;
- CharType operator()(CharType t) const{
- const char * lookup_table =
- "0123456789"
- "ABCDEFGHIJKLMNOPQRSTUV";
- assert(t < 32);
- return (lookup_table[static_cast<size_t>(t)]);
- }
-};
-
-} // namespace detail
-
-// note: what we would like to do is
-// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
-// typedef transform_iterator<
-// from_5_bit<CharType>,
-// transform_width<Base, 5, sizeof(Base::value_type) * 8, CharType>
-// > base32hex_from_binary;
-// but C++ won't accept this. Rather than using a "type generator" and
-// using a different syntax, make a derivation which should be equivalent.
-//
-// Another issue addressed here is that the transform_iterator doesn't have
-// a templated constructor. This makes it incompatible with the dataflow
-// ideal. This is also addressed here.
-
-//template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
-template<
- class Base,
- class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
->
-class base32hex_from_binary :
- public transform_iterator<
- detail::from_5_bit<CharType>,
- Base
- >
-{
- friend class boost::iterator_core_access;
- typedef transform_iterator<
- BOOST_DEDUCED_TYPENAME detail::from_5_bit<CharType>,
- Base
- > super_t;
-
-public:
- // make composible buy using templated constructor
- template<class T>
- base32hex_from_binary(BOOST_PFTO_WRAPPER(T) start) :
- super_t(
- Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))),
- detail::from_5_bit<CharType>()
- )
- {}
- // intel 7.1 doesn't like default copy constructor
- base32hex_from_binary(const base32hex_from_binary & rhs) :
- super_t(
- Base(rhs.base_reference()),
- detail::from_5_bit<CharType>()
- )
- {}
-// base32hex_from_binary(){};
-};
-
-} // namespace iterators
-} // namespace archive
-} // namespace boost
-
-#endif // BOOST_ARCHIVE_ITERATORS_BASE32HEX_FROM_BINARY_HPP
diff --git a/src/lib/dns/util/base64.h b/src/lib/dns/util/base64.h
deleted file mode 100644
index 46e10a6..0000000
--- a/src/lib/dns/util/base64.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (C) 2009 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 __BASE64_H
-#define __BASE64_H 1
-
-#include <stdint.h>
-#include <string>
-#include <vector>
-
-//
-// Note: this helper module isn't specific to the DNS protocol per se.
-// We should probably move this to somewhere else, possibly in some common
-// utility area.
-//
-
-namespace isc {
-namespace dns {
-
-/// \brief Encode binary data in the base64 format.
-///
-/// This function returns a new \c std::string object that stores a text
-/// encoded in the base64 format for the given \c binary %data.
-/// The resulting string will be a valid, canonical form of base64
-/// representation as specified in RFC4648.
-///
-/// If memory allocation for the returned string fails, a corresponding
-/// standard exception will be thrown. This function never throws exceptions
-/// otherwise.
-///
-/// \param binary A vector object storing the data to be encoded.
-/// \return A newly created string that stores base64 encoded value for binary.
-std::string encodeBase64(const std::vector<uint8_t>& binary);
-
-/// \brief Decode a text encoded in the base64 format into the original %data.
-///
-/// The \c input argument must be a valid string represented in the base64
-/// format as specified in RFC4648. Space characters (spaces, tabs, newlines)
-/// can be included in \c input and will be ignored. Without spaces, the
-/// length of string must be a multiple of 4 bytes with necessary paddings.
-/// Also it must be encoded using the canonical encoding (see RFC4648).
-/// If any of these conditions is not met, an exception of class
-/// \c isc::BadValue will be thrown.
-///
-/// If \c result doesn't have sufficient capacity to store all decoded %data
-/// and memory allocation fails, a corresponding standard exception will be
-/// thrown. If the caller knows the necessary length (which can in theory
-/// be calculated from the input string), this situation can be avoided by
-/// reserving sufficient space for \c result beforehand.
-///
-/// Any existing %data in \c result will be removed. This is the case in some
-/// of the cases where an exception is thrown; that is, this function only
-/// provides the basic exception guarantee.
-///
-/// \param input A text encoded in the base64 format.
-/// \param result A vector in which the decoded %data is to be stored.
-void decodeBase64(const std::string& input, std::vector<uint8_t>& result);
-}
-}
-
-#endif // __BASE64_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/dns/util/base_n.cc b/src/lib/dns/util/base_n.cc
deleted file mode 100644
index 9d0c777..0000000
--- a/src/lib/dns/util/base_n.cc
+++ /dev/null
@@ -1,398 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <stdint.h>
-#include <cassert>
-#include <iterator>
-#include <string>
-#include <vector>
-
-#include <boost/archive/iterators/base64_from_binary.hpp>
-#include <boost/archive/iterators/binary_from_base64.hpp>
-#include <boost/archive/iterators/transform_width.hpp>
-#include <boost/math/common_factor.hpp>
-
-#include <dns/util/base32hex_from_binary.h>
-#include <dns/util/binary_from_base32hex.h>
-
-#include <dns/util/base16_from_binary.h>
-#include <dns/util/binary_from_base16.h>
-
-#include <exceptions/exceptions.h>
-
-#include <dns/util/base32hex.h>
-#include <dns/util/base64.h>
-
-using namespace std;
-using namespace boost::archive::iterators;
-
-namespace isc {
-namespace dns {
-
-// In the following anonymous namespace, we provide a generic framework
-// to encode/decode baseN format. We use the following tools:
-// - boost base64_from_binary/binary_from_base64: provide mapping table for
-// base64.
-// These classes take another iterator (Base) as a template argument, and
-// their dereference operator (operator*()) first retrieves an input value
-// from Base via Base::operator* and converts the value using their mapping
-// table. The converted value is returned as their own operator*.
-// - base{32hex,16}_from_binary/binary_from_base{32hex,16}: provide mapping
-// table for base32hex and base16. A straightforward variation of their
-// base64 counterparts.
-// - EncodeNormalizer/DecodeNormalizer: supplemental filter handling baseN
-// padding characters (=)
-// - boost transform_width: an iterator framework for handling data stream
-// per bit-group. It takes another iterator (Base) and output/input bit
-// numbers (BitsOut/BitsIn) template arguments. A transform_width object
-// internally maintains a bit stream, which can be retrieved per BitsOut
-// bits via its dereference operator (operator*()). It builds the stream
-// by internally iterating over the Base object via Base::operator++ and
-// Base::operator*, using the least BitsIn bits of the result of
-// Base::operator*. In our usage BitsIn for encoding and BitsOut for
-// decoding are always 8 (# of bits for one byte).
-//
-// Its dereference operator
-// retrieves BitsIn bits from the result of "*Base" (if necessary it
-// internally calls ++Base)
-//
-// A conceptual description of how the encoding and decoding work is as
-// follows:
-// Encoding:
-// input binary data => Normalizer (append sufficient number of 0 bits)
-// => transform_width (extract bit groups from the original
-// stream)
-// => baseXX_from_binary (convert each bit group to an
-// encoded byte using the mapping)
-// Decoding:
-// input baseXX text => Normalizer (convert '='s to the encoded characters
-// corresponding to 0, e.g. 'A's in base64)
-// => binary_from_baseXX (convert each encoded byte into
-// the original group bit)
-// => transform_width (build original byte stream by
-// concatenating the decoded bit
-// stream)
-//
-// Below, we define a set of templated classes to handle different parameters
-// for different encoding algorithms.
-namespace {
-// Common constants used for all baseN encoding.
-const char BASE_PADDING_CHAR = '=';
-const uint8_t BINARY_ZERO_CODE = 0;
-
-// EncodeNormalizer is an input iterator intended to be used as a filter
-// between the binary stream and baseXX_from_binary translator (via
-// transform_width). An EncodeNormalizer object is configured with two
-// iterators (base and base_end), specifying the head and end of the input
-// stream. It internally iterators over the original stream, and return
-// each byte of the stream intact via its dereference operator until it
-// reaches the end of the stream. After that the EncodeNormalizer object
-// will return 0 no matter how many times it is subsequently incremented.
-// This is necessary because the input binary stream may not contain
-// sufficient bits for a full encoded text while baseXX_from_binary expects
-// a sufficient length of input.
-// Note: this class is intended to be used within this implementation file,
-// and assumes "base < base_end" on construction without validating the
-// arguments. The behavior is undefined if this assumption doesn't hold.
-class EncodeNormalizer : public iterator<input_iterator_tag, uint8_t> {
-public:
- EncodeNormalizer(const vector<uint8_t>::const_iterator& base,
- const vector<uint8_t>::const_iterator& base_end) :
- base_(base), base_end_(base_end), in_pad_(false)
- {}
- EncodeNormalizer& operator++() {
- if (!in_pad_) {
- ++base_;
- }
- if (base_ == base_end_) {
- in_pad_ = true;
- }
- return (*this);
- }
- const uint8_t& operator*() const {
- if (in_pad_) {
- return (BINARY_ZERO_CODE);
- } else {
- return (*base_);
- }
- }
- bool operator==(const EncodeNormalizer& other) const {
- return (base_ == other.base_);
- }
-private:
- vector<uint8_t>::const_iterator base_;
- const vector<uint8_t>::const_iterator base_end_;
- bool in_pad_;
-};
-
-// DecodeNormalizer is an input iterator intended to be used as a filter
-// between the encoded baseX stream and binary_from_baseXX.
-// A DecodeNormalizer object is configured with three string iterators
-// (base, base_beinpad, and base_beginpad), specifying the head of the string,
-// the beginning position of baseX padding (when there's padding), and
-// end of the string, respectively. It internally iterators over the original
-// stream, and return each character of the encoded string via its dereference
-// operator until it reaches base_beginpad. After that the DecodeNormalizer
-// will return the encoding character corresponding to the all-0 value
-// (which is specified on construction via base_zero_code. see also
-// BaseZeroCode below). This translation is necessary because
-// binary_from_baseXX doesn't accept the padding character (i.e. '=').
-// Note: this class is intended to be used within this implementation file,
-// and for simplicity assumes "base < base_beginpad <= base_end" on
-// construction without validating the arguments. The behavior is undefined
-// if this assumption doesn't hold.
-class DecodeNormalizer : public iterator<input_iterator_tag, char> {
-public:
- DecodeNormalizer(const char base_zero_code,
- const string::const_iterator& base,
- const string::const_iterator& base_beginpad,
- const string::const_iterator& base_end) :
- base_zero_code_(base_zero_code),
- base_(base), base_beginpad_(base_beginpad), base_end_(base_end),
- in_pad_(false)
- {}
- DecodeNormalizer& operator++() {
- ++base_;
- while (base_ != base_end_ && isspace(*base_)) {
- ++base_;
- }
- if (base_ == base_beginpad_) {
- in_pad_ = true;
- }
- return (*this);
- }
- const char& operator*() const {
- if (in_pad_ && *base_ == BASE_PADDING_CHAR) {
- return (base_zero_code_);
- } else {
- return (*base_);
- }
- }
- bool operator==(const DecodeNormalizer& other) const {
- return (base_ == other.base_);
- }
-private:
- const char base_zero_code_;
- string::const_iterator base_;
- const string::const_iterator base_beginpad_;
- const string::const_iterator base_end_;
- bool in_pad_;
-};
-
-// BitsPerChunk: number of bits to be converted using the baseN mapping table.
-// e.g. 6 for base64.
-// BaseZeroCode: the byte character that represents a value of 0 in
-// the corresponding encoding. e.g. 'A' for base64.
-// Encoder: baseX_from_binary<transform_width<EncodeNormalizer,
-// BitsPerChunk, 8> >
-// Decoder: transform_width<binary_from_baseX<DecodeNormalizer>,
-// 8, BitsPerChunk>
-template <int BitsPerChunk, char BaseZeroCode,
- typename Encoder, typename Decoder>
-struct BaseNTransformer {
- static string encode(const vector<uint8_t>& binary);
- static void decode(const char* algorithm,
- const string& base64, vector<uint8_t>& result);
-
- // BITS_PER_GROUP is the number of bits for the smallest possible (non
- // empty) bit string that can be converted to a valid baseN encoded text
- // without padding. It's the least common multiple of 8 and BitsPerChunk,
- // e.g. 24 for base64.
- static const int BITS_PER_GROUP =
- boost::math::static_lcm<BitsPerChunk, 8>::value;
-
- // MAX_PADDING_CHARS is the maximum number of padding characters
- // that can appear in a valid baseN encoded text.
- // It's group_len - chars_for_byte, where group_len is the number of
- // encoded characters to represent BITS_PER_GROUP bits, and
- // chars_for_byte is the number of encoded character that is needed to
- // represent a single byte, which is ceil(8 / BitsPerChunk).
- // For example, for base64 we need two encoded characters to represent a
- // byte, and each group consists of 4 encoded characters, so
- // MAX_PADDING_CHARS is 4 - 2 = 2.
- static const int MAX_PADDING_CHARS =
- BITS_PER_GROUP / BitsPerChunk -
- (8 / BitsPerChunk + ((8 % BitsPerChunk) == 0 ? 0 : 1));
-};
-
-template <int BitsPerChunk, char BaseZeroCode,
- typename Encoder, typename Decoder>
-string
-BaseNTransformer<BitsPerChunk, BaseZeroCode, Encoder, Decoder>::encode(
- const vector<uint8_t>& binary)
-{
- // calculate the resulting length.
- size_t bits = binary.size() * 8;
- if (bits % BITS_PER_GROUP > 0) {
- bits += (BITS_PER_GROUP - (bits % BITS_PER_GROUP));
- }
- const size_t len = bits / BitsPerChunk;
-
- string result;
- result.reserve(len);
- result.assign(Encoder(EncodeNormalizer(binary.begin(), binary.end())),
- Encoder(EncodeNormalizer(binary.end(), binary.end())));
- assert(len >= result.length());
- result.append(len - result.length(), BASE_PADDING_CHAR);
- return (result);
-}
-
-template <int BitsPerChunk, char BaseZeroCode,
- typename Encoder, typename Decoder>
-void
-BaseNTransformer<BitsPerChunk, BaseZeroCode, Encoder, Decoder>::decode(
- const char* const algorithm,
- const string& input,
- vector<uint8_t>& result)
-{
- // enumerate the number of trailing padding characters (=), ignoring
- // white spaces. since baseN_from_binary doesn't accept padding,
- // we handle it explicitly.
- size_t padchars = 0;
- string::const_reverse_iterator srit = input.rbegin();
- string::const_reverse_iterator srit_end = input.rend();
- while (srit != srit_end) {
- char ch = *srit;
- if (ch == BASE_PADDING_CHAR) {
- if (++padchars > MAX_PADDING_CHARS) {
- isc_throw(BadValue, "Too many " << algorithm
- << " padding characters: " << input);
- }
- } else if (!isspace(ch)) {
- break;
- }
- ++srit;
- }
- // then calculate the number of padding bits corresponding to the padding
- // characters. In general, the padding bits consist of all-zero
- // trailing bits of the last encoded character followed by zero bits
- // represented by the padding characters:
- // 1st pad 2nd pad 3rd pad...
- // +++===== ======= ===... (+: from encoded chars, =: from pad chars)
- // 0000...0 0......0 000...
- // 0 7 8 15 16.... (bits)
- // The number of bits for the '==...' part is padchars * BitsPerChunk.
- // So the total number of padding bits is the smallest multiple of 8
- // that is >= padchars * BitsPerChunk.
- // (Below, note the common idiom of the bitwise AND with ~7. It clears the
- // lowest three bits, so has the effect of rounding the result down to the
- // nearest multiple of 8)
- const size_t padbits = (padchars * BitsPerChunk + 7) & ~7;
-
- // In some encoding algorithm, it could happen that a padding byte would
- // contain a full set of encoded bits, which is not allowed by definition
- // of padding. For example, if BitsPerChunk is 5, the following
- // representation could happen:
- // ++00000= (+: from encoded chars, 0: encoded char for '0', =: pad chars)
- // 0 7 (bits)
- // This must actually be encoded as follows:
- // ++======
- // 0 7 (bits)
- // The following check rejects this type of invalid encoding.
- if (padbits > BitsPerChunk * (padchars + 1)) {
- isc_throw(BadValue, "Invalid " << algorithm << "padding: " << input);
- }
-
- // convert the number of bits in bytes for convenience.
- const size_t padbytes = padbits / 8;
-
- try {
- result.assign(Decoder(DecodeNormalizer(BaseZeroCode, input.begin(),
- srit.base(), input.end())),
- Decoder(DecodeNormalizer(BaseZeroCode, input.end(),
- input.end(), input.end())));
- } catch (const dataflow_exception& ex) {
- // convert any boost exceptions into our local one.
- isc_throw(BadValue, ex.what());
- }
-
- // Confirm the original BaseX text is the canonical encoding of the
- // data, that is, that the first byte of padding is indeed 0.
- // (DecodeNormalizer and binary_from_baseXX ensure that the rest of the
- // padding is all zero).
- assert(result.size() >= padbytes);
- if (padbytes > 0 && *(result.end() - padbytes) != 0) {
- isc_throw(BadValue, "Non 0 bits included in " << algorithm
- << " padding: " << input);
- }
-
- // strip the padded zero-bit fields
- result.resize(result.size() - padbytes);
-}
-
-//
-// Instantiation for BASE-64
-//
-typedef
-base64_from_binary<transform_width<EncodeNormalizer, 6, 8> > base64_encoder;
-typedef
-transform_width<binary_from_base64<DecodeNormalizer>, 8, 6> base64_decoder;
-typedef BaseNTransformer<6, 'A', base64_encoder, base64_decoder>
-Base64Transformer;
-
-//
-// Instantiation for BASE-32HEX
-//
-typedef
-base32hex_from_binary<transform_width<EncodeNormalizer, 5, 8> >
-base32hex_encoder;
-typedef
-transform_width<binary_from_base32hex<DecodeNormalizer>, 8, 5>
-base32hex_decoder;
-typedef BaseNTransformer<5, '0', base32hex_encoder, base32hex_decoder>
-Base32HexTransformer;
-
-//
-// Instantiation for BASE-16 (HEX)
-//
-typedef
-base16_from_binary<transform_width<EncodeNormalizer, 4, 8> > base16_encoder;
-typedef
-transform_width<binary_from_base16<DecodeNormalizer>, 8, 4> base16_decoder;
-typedef BaseNTransformer<4, '0', base16_encoder, base16_decoder>
-Base16Transformer;
-}
-
-string
-encodeBase64(const vector<uint8_t>& binary) {
- return (Base64Transformer::encode(binary));
-}
-
-void
-decodeBase64(const string& input, vector<uint8_t>& result) {
- Base64Transformer::decode("base64", input, result);
-}
-
-string
-encodeBase32Hex(const vector<uint8_t>& binary) {
- return (Base32HexTransformer::encode(binary));
-}
-
-void
-decodeBase32Hex(const string& input, vector<uint8_t>& result) {
- Base32HexTransformer::decode("base32hex", input, result);
-}
-
-string
-encodeHex(const vector<uint8_t>& binary) {
- return (Base16Transformer::encode(binary));
-}
-
-void
-decodeHex(const string& input, vector<uint8_t>& result) {
- Base16Transformer::decode("base16", input, result);
-}
-
-}
-}
diff --git a/src/lib/dns/util/binary_from_base16.h b/src/lib/dns/util/binary_from_base16.h
deleted file mode 100644
index 50342f1..0000000
--- a/src/lib/dns/util/binary_from_base16.h
+++ /dev/null
@@ -1,120 +0,0 @@
-#ifndef BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE16_HPP
-#define BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE16_HPP
-
-// MS compatible compilers support #pragma once
-#if defined(_MSC_VER) && (_MSC_VER >= 1020)
-# pragma once
-#endif
-
-/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
-// binary_from_base16.h (derived from boost binary_from_base64.hpp)
-
-// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
-// Use, modification and distribution is subject to the Boost Software
-// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-
-// See http://www.boost.org for updates, documentation, and revision history.
-
-#include <cassert>
-
-// See binary_from_base32hex.h for why we need _from_base64.hpp here.
-#include <boost/archive/iterators/binary_from_base64.hpp>
-
-#include <exceptions/exceptions.h>
-
-namespace boost {
-namespace archive {
-namespace iterators {
-
-/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
-// convert base16 characters to binary data
-
-namespace detail {
-
-template<class CharType>
-struct to_4_bit {
- typedef CharType result_type;
- CharType operator()(CharType t) const{
- const char lookup_table[] = {
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 00-0f
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 10-1f
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 20-2f
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 30-3f
- -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 40-4f
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 50-5f
- -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 60-6f
- };
- // metrowerks trips this assertion - how come?
- #if ! defined(__MWERKS__)
- BOOST_STATIC_ASSERT(0x70 == sizeof(lookup_table));
- #endif
- signed char value = -1;
- if((unsigned)t < sizeof(lookup_table))
- value = lookup_table[(unsigned)t];
- if(-1 == value) {
- isc_throw(isc::BadValue,
- "attempt to decode a value not in base16 char set");
- }
- return (value);
- }
-};
-
-} // namespace detail
-
-// note: what we would like to do is
-// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
-// typedef transform_iterator<
-// from_4_bit<CharType>,
-// transform_width<Base, 4, sizeof(Base::value_type) * 8, CharType>
-// > base16_from_binary;
-// but C++ won't accept this. Rather than using a "type generator" and
-// using a different syntax, make a derivation which should be equivalent.
-//
-// Another issue addressed here is that the transform_iterator doesn't have
-// a templated constructor. This makes it incompatible with the dataflow
-// ideal. This is also addressed here.
-
-template<
- class Base,
- class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
->
-class binary_from_base16 : public
- transform_iterator<
- detail::to_4_bit<CharType>,
- Base
- >
-{
- friend class boost::iterator_core_access;
- typedef transform_iterator<
- detail::to_4_bit<CharType>,
- Base
- > super_t;
-public:
- // make composible buy using templated constructor
- template<class T>
- binary_from_base16(BOOST_PFTO_WRAPPER(T) start) :
- super_t(
- Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))),
- detail::to_4_bit<CharType>()
- )
- {}
- // intel 7.1 doesn't like default copy constructor
- binary_from_base16(const binary_from_base16 & rhs) :
- super_t(
- Base(rhs.base_reference()),
- detail::to_4_bit<CharType>()
- )
- {}
-// binary_from_base16(){};
-};
-
-} // namespace iterators
-} // namespace archive
-} // namespace boost
-
-#endif // BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE16_HPP
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/dns/util/binary_from_base32hex.h b/src/lib/dns/util/binary_from_base32hex.h
deleted file mode 100644
index 1d83f54..0000000
--- a/src/lib/dns/util/binary_from_base32hex.h
+++ /dev/null
@@ -1,123 +0,0 @@
-#ifndef BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE32HEX_HPP
-#define BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE32HEX_HPP
-
-// MS compatible compilers support #pragma once
-#if defined(_MSC_VER) && (_MSC_VER >= 1020)
-# pragma once
-#endif
-
-/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
-// binary_from_base32hex.h (derived from boost binary_from_base64.hpp)
-
-// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
-// Use, modification and distribution is subject to the Boost Software
-// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-
-// See http://www.boost.org for updates, documentation, and revision history.
-
-#include <cassert>
-
-// We use the same boost header files used in "_from_base64". Since the
-// precise path to these headers may vary depending on the boost version we
-// simply include the base64 header here.
-#include <boost/archive/iterators/binary_from_base64.hpp>
-
-#include <exceptions/exceptions.h>
-
-namespace boost {
-namespace archive {
-namespace iterators {
-
-/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
-// convert base32hex characters to binary data
-
-namespace detail {
-
-template<class CharType>
-struct to_5_bit {
- typedef CharType result_type;
- CharType operator()(CharType t) const{
- const char lookup_table[] = {
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 00-0f
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 10-1f
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 20-2f
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 30-3f
- -1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, // 40-4f
- 25,26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 50-5f
- -1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, // 60-6f
- 25,26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 70-7f
- };
- // metrowerks trips this assertion - how come?
- #if ! defined(__MWERKS__)
- BOOST_STATIC_ASSERT(0x80 == sizeof(lookup_table));
- #endif
- signed char value = -1;
- if((unsigned)t < sizeof(lookup_table))
- value = lookup_table[(unsigned)t];
- if(-1 == value) {
- isc_throw(isc::BadValue,
- "attempt to decode a value not in base32hex char set");
- }
- return (value);
- }
-};
-
-} // namespace detail
-
-// note: what we would like to do is
-// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
-// typedef transform_iterator<
-// from_5_bit<CharType>,
-// transform_width<Base, 5, sizeof(Base::value_type) * 8, CharType>
-// > base32hex_from_binary;
-// but C++ won't accept this. Rather than using a "type generator" and
-// using a different syntax, make a derivation which should be equivalent.
-//
-// Another issue addressed here is that the transform_iterator doesn't have
-// a templated constructor. This makes it incompatible with the dataflow
-// ideal. This is also addressed here.
-
-template<
- class Base,
- class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
->
-class binary_from_base32hex : public
- transform_iterator<
- detail::to_5_bit<CharType>,
- Base
- >
-{
- friend class boost::iterator_core_access;
- typedef transform_iterator<
- detail::to_5_bit<CharType>,
- Base
- > super_t;
-public:
- // make composible buy using templated constructor
- template<class T>
- binary_from_base32hex(BOOST_PFTO_WRAPPER(T) start) :
- super_t(
- Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))),
- detail::to_5_bit<CharType>()
- )
- {}
- // intel 7.1 doesn't like default copy constructor
- binary_from_base32hex(const binary_from_base32hex & rhs) :
- super_t(
- Base(rhs.base_reference()),
- detail::to_5_bit<CharType>()
- )
- {}
-// binary_from_base32hex(){};
-};
-
-} // namespace iterators
-} // namespace archive
-} // namespace boost
-
-#endif // BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE32HEX_HPP
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/dns/util/hex.h b/src/lib/dns/util/hex.h
deleted file mode 100644
index e2626bf..0000000
--- a/src/lib/dns/util/hex.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2009 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 __HEX_H
-#define __HEX_H 1
-
-#include <stdint.h>
-#include <string>
-#include <vector>
-
-//
-// Note: this helper module isn't specific to the DNS protocol per se.
-// We should probably move this to somewhere else, possibly in some common
-// utility area.
-//
-
-namespace isc {
-namespace dns {
-/// \brief Encode binary data in the base16 ('hex') format.
-///
-/// The underlying implementation is shared with \c encodeBase64, and most of
-/// the description except the format (base16) equally applies.
-/// Another notable exception is that the base16 encoding doesn't require
-/// padding, so padding related considerations and the notion of canonical
-/// encoding don't apply.
-///
-/// \param binary A vector object storing the data to be encoded.
-/// \return A newly created string that stores base16 encoded value for
-/// binary.
-std::string encodeHex(const std::vector<uint8_t>& binary);
-
-/// \brief Decode a text encoded in the base16 ('hex') format into the
-/// original %data.
-///
-/// The underlying implementation is shared with \c decodeBase64, and most
-/// of the description except the format (base16) equally applies.
-/// Another notable exception is that the base16 encoding doesn't require
-/// padding, so padding related considerations and the notion of canonical
-/// encoding don't apply.
-///
-/// \param input A text encoded in the base16 format.
-/// \param result A vector in which the decoded %data is to be stored.
-void decodeHex(const std::string& input, std::vector<uint8_t>& result);
-}
-}
-
-#endif // __HEX_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/dns/util/sha1.cc b/src/lib/dns/util/sha1.cc
deleted file mode 100644
index 62e885b..0000000
--- a/src/lib/dns/util/sha1.cc
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
- * Description:
- * This file implements the Secure Hash Signature Standard
- * algorithms as defined in the National Institute of Standards
- * and Technology Federal Information Processing Standards
- * Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2
- * published on August 1, 2002, and the FIPS PUB 180-2 Change
- * Notice published on February 28, 2004.
- *
- * A combined document showing all algorithms is available at
- * http://csrc.nist.gov/publications/fips/
- * fips180-2/fips180-2withchangenotice.pdf
- *
- * The SHA-1 algorithm produces a 160-bit message digest for a
- * given data stream. It should take about 2**n steps to find a
- * message with the same digest as a given message and
- * 2**(n/2) to find any two messages with the same digest,
- * when n is the digest size in bits. Therefore, this
- * algorithm can serve as a means of providing a
- * "fingerprint" for a message.
- *
- * Portability Issues:
- * SHA-1 is defined in terms of 32-bit "words". This code
- * uses <stdint.h> (included via "sha.h") to define 32 and 8
- * bit unsigned integer types. If your C compiler does not
- * support 32 bit unsigned integers, this code is not
- * appropriate.
- *
- * Caveats:
- * SHA-1 is designed to work with messages less than 2^64 bits
- * long. This implementation uses SHA1Input() to hash the bits
- * that are a multiple of the size of an 8-bit character, and then
- * uses SHA1FinalBits() to hash the final few bits of the input.
- *
- * Authorship:
- * This file is adapted from RFC 4634, by D. Eastlake et al.
- * Copyright (C) The Internet Society (2006).
- *
- * Permission is granted for all uses, commercial and non-commercial,
- * of the sample code found in Section 8. Royalty free license to
- * use, copy, modify and distribute the software found in Section 8 is
- * granted, provided that this document is identified in all material
- * mentioning or referencing this software, and provided that
- * redistributed derivative works do not contain misleading author or
- * version information.
- *
- * The authors make no representations concerning either the
- * merchantability of this software or the suitability of this
- * software for any particular purpose. It is provided "as is"
- * without express or implied warranty of any kind.
- *
- */
-#include <dns/util/sha1.h>
-
-/* Local Function Prototyptes */
-static void SHA1Finalize(SHA1Context *, uint8_t Pad_Byte);
-static void SHA1PadMessage(SHA1Context *, uint8_t Pad_Byte);
-static void SHA1ProcessMessageBlock(SHA1Context *);
-
-/*
- * Define functions used by SHA1 hash
- */
-static inline uint32_t
-SHA_Ch(const uint32_t x, const uint32_t y, const uint32_t z) {
- return (((x) & ((y) ^ (z))) ^ (z));
-}
-
-static inline uint32_t
-SHA_Maj(const uint32_t x, const uint32_t y, const uint32_t z) {
- return (((x) & ((y) | (z))) | ((y) & (z)));
-}
-
-static inline uint32_t
-SHA_Parity(const uint32_t x, const uint32_t y, const uint32_t z) {
- return ((x) ^ (y) ^ (z));
-}
-
-static inline int
-SHA1CircularShift(uint8_t bits, uint32_t word) {
- return ((word << bits) | (word >> (32 - bits)));
-}
-
-static inline bool
-SHA1AddLength(SHA1Context *context, uint32_t length) {
- uint32_t addTemp = context->Length_Low;
- context->Length_Low += length;
- if (context->Length_Low < addTemp && ++context->Length_High == 0) {
- return (true);
- } else {
- return (false);
- }
-}
-
-/*
- * SHA1Reset
- *
- * Description:
- * This function will initialize the SHA1Context in preparation
- * for computing a new SHA1 message digest.
- *
- * Parameters:
- * context: [in/out]
- * The context to reset.
- *
- * Returns:
- * sha Error Code.
- *
- */
-int
-SHA1Reset(SHA1Context *context) {
- if (!context) {
- return (SHA_NULL);
- }
-
- context->Length_Low = 0;
- context->Length_High = 0;
- context->Message_Block_Index = 0;
-
- context->Intermediate_Hash[0] = 0x67452301;
- context->Intermediate_Hash[1] = 0xEFCDAB89;
- context->Intermediate_Hash[2] = 0x98BADCFE;
- context->Intermediate_Hash[3] = 0x10325476;
- context->Intermediate_Hash[4] = 0xC3D2E1F0;
-
- context->Computed = 0;
- context->Corrupted = 0;
- return (SHA_SUCCESS);
-}
-
-
-/*
- * SHA1Input
- *
- * Description:
- * This function accepts an array of octets as the next portion
- * of the message.
- *
- * Parameters:
- * context: [in/out]
- * The SHA context to update
- * message_array: [in]
- * An array of characters representing the next portion of
- * the message.
- * length: [in]
- * The length of the message in message_array
- *
- * Returns:
- * sha Error Code.
- *
- */
-int
-SHA1Input(SHA1Context *context, const uint8_t *message_array, unsigned length) {
- if (!length) {
- return (SHA_SUCCESS);
- }
-
- if (!context || !message_array) {
- return (SHA_NULL);
- }
-
- if (context->Computed) {
- context->Corrupted = SHA_STATEERROR;
- return (SHA_STATEERROR);
- }
-
- if (context->Corrupted) {
- return (context->Corrupted);
- }
-
- while(length-- && !context->Corrupted) {
- context->Message_Block[context->Message_Block_Index++] =
- (*message_array & 0xFF);
-
- if (!SHA1AddLength(context, 8) &&
- (context->Message_Block_Index == SHA1_BLOCKSIZE))
- {
- SHA1ProcessMessageBlock(context);
- }
-
- message_array++;
- }
-
- return (SHA_SUCCESS);
-}
-
-/*
- * SHA1FinalBits
- *
- * Description:
- * This function will add in any final bits of the message.
- *
- * Parameters:
- * context: [in/out]
- * The SHA context to update
- * message_bits: [in]
- * The final bits of the message, in the upper portion of the
- * byte. (Use 0b###00000 instead of 0b00000### to input the
- * three bits ###.)
- * length: [in]
- * The number of bits in message_bits, between 1 and 7.
- *
- * Returns:
- * sha Error Code.
- */
-int SHA1FinalBits(SHA1Context *context, const uint8_t message_bits,
- unsigned int length)
-{
- uint8_t masks[8] = {
- /* 0 0b00000000 */ 0x00,
- /* 1 0b10000000 */ 0x80,
- /* 2 0b11000000 */ 0xC0,
- /* 3 0b11100000 */ 0xE0,
- /* 4 0b11110000 */ 0xF0,
- /* 5 0b11111000 */ 0xF8,
- /* 6 0b11111100 */ 0xFC,
- /* 7 0b11111110 */ 0xFE
- };
- uint8_t markbit[8] = {
- /* 0 0b10000000 */ 0x80,
- /* 1 0b01000000 */ 0x40,
- /* 2 0b00100000 */ 0x20,
- /* 3 0b00010000 */ 0x10,
- /* 4 0b00001000 */ 0x08,
- /* 5 0b00000100 */ 0x04,
- /* 6 0b00000010 */ 0x02,
- /* 7 0b00000001 */ 0x01
- };
-
- if (!length) {
- return (SHA_SUCCESS);
- }
-
- if (!context) {
- return (SHA_NULL);
- }
-
- if (context->Computed || (length >= 8) || (length == 0)) {
- context->Corrupted = SHA_STATEERROR;
- return (SHA_STATEERROR);
- }
-
- if (context->Corrupted) {
- return (context->Corrupted);
- }
-
- SHA1AddLength(context, length);
- SHA1Finalize(context,
- (uint8_t) ((message_bits & masks[length]) | markbit[length]));
-
- return (SHA_SUCCESS);
-}
-
-/*
- * SHA1Result
- *
- * Description:
- * This function will return the 160-bit message digest into the
- * Message_Digest array provided by the caller.
- * NOTE: The first octet of hash is stored in the 0th element,
- * the last octet of hash in the 19th element.
- *
- * Parameters:
- * context: [in/out]
- * The context to use to calculate the SHA-1 hash.
- * Message_Digest: [out]
- * Where the digest is returned.
- *
- * Returns:
- * sha Error Code.
- *
- */
-int
-SHA1Result(SHA1Context *context, uint8_t Message_Digest[SHA1_HASHSIZE]) {
- int i;
-
- if (!context || !Message_Digest) {
- return (SHA_NULL);
- }
-
- if (context->Corrupted) {
- return (context->Corrupted);
- }
-
- if (!context->Computed) {
- SHA1Finalize(context, 0x80);
- }
-
- for(i = 0; i < SHA1_HASHSIZE; ++i) {
- Message_Digest[i] = context->Intermediate_Hash[i>>2]
- >> 8 * (3 - (i & 0x03));
- }
-
- return (SHA_SUCCESS);
-}
-
-/*
- * SHA1Finalize
- *
- * Description:
- * This helper function finishes off the digest calculations.
- *
- * Parameters:
- * context: [in/out]
- * The SHA context to update
- * Pad_Byte: [in]
- * The last byte to add to the digest before the 0-padding
- * and length. This will contain the last bits of the message
- * followed by another single bit. If the message was an
- * exact multiple of 8-bits long, Pad_Byte will be 0x80.
- *
- * Returns:
- * sha Error Code.
- *
- */
-static void SHA1Finalize(SHA1Context *context, uint8_t Pad_Byte) {
- int i;
- SHA1PadMessage(context, Pad_Byte);
- /* message may be sensitive, clear it out */
- for (i = 0; i < SHA1_BLOCKSIZE; ++i)
- context->Message_Block[i] = 0;
- context->Length_Low = 0; /* and clear length */
- context->Length_High = 0;
- context->Computed = 1;
-}
-
-/*
- * SHA1PadMessage
- *
- * Description:
- * According to the standard, the message must be padded to an even
- * 512 bits. The first padding bit must be a '1'. The last 64
- * bits represent the length of the original message. All bits in
- * between should be 0. This function will pad the message
- * according to those rules by filling the Message_Block array
- * accordingly. It will also call the ProcessMessageBlock function
- * provided appropriately. When it returns, it can be assumed that
- * the message digest has been computed.
- *
- * Parameters:
- * context: [in/out]
- * The context to pad
- * Pad_Byte: [in]
- * The last byte to add to the digest before the 0-padding
- * and length. This will contain the last bits of the message
- * followed by another single bit. If the message was an
- * exact multiple of 8-bits long, Pad_Byte will be 0x80.
- *
- * Returns:
- * Nothing.
- *
- */
-static void SHA1PadMessage(SHA1Context *context, uint8_t Pad_Byte) {
- /*
- * Check to see if the current message block is too small to hold
- * the initial padding bits and length. If so, we will pad the
- * block, process it, and then continue padding into a second
- * block.
- */
- if (context->Message_Block_Index >= (SHA1_BLOCKSIZE - 8)) {
- context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
- while (context->Message_Block_Index < SHA1_BLOCKSIZE) {
- context->Message_Block[context->Message_Block_Index++] = 0;
- }
-
- SHA1ProcessMessageBlock(context);
- } else
- context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
-
- while (context->Message_Block_Index < (SHA1_BLOCKSIZE - 8))
- context->Message_Block[context->Message_Block_Index++] = 0;
-
- /*
- * Store the message length as the last 8 octets
- */
- context->Message_Block[56] = (uint8_t) (context->Length_High >> 24);
- context->Message_Block[57] = (uint8_t) (context->Length_High >> 16);
- context->Message_Block[58] = (uint8_t) (context->Length_High >> 8);
- context->Message_Block[59] = (uint8_t) (context->Length_High);
- context->Message_Block[60] = (uint8_t) (context->Length_Low >> 24);
- context->Message_Block[61] = (uint8_t) (context->Length_Low >> 16);
- context->Message_Block[62] = (uint8_t) (context->Length_Low >> 8);
- context->Message_Block[63] = (uint8_t) (context->Length_Low);
-
- SHA1ProcessMessageBlock(context);
-}
-
-/*
- * SHA1ProcessMessageBlock
- *
- * Description:
- * This helper function will process the next 512 bits of the
- * message stored in the Message_Block array.
- *
- * Parameters:
- * None.
- *
- * Returns:
- * Nothing.
- *
- * Comments:
- * Many of the variable names in this code, especially the
- * single character names, were used because those were the
- * names used in the publication.
- *
- *
- */
-static void
-SHA1ProcessMessageBlock(SHA1Context *context) {
- /* Constants defined in FIPS-180-2, section 4.2.1 */
- const uint32_t K[] = {
- 0x5A827999,
- 0x6ED9EBA1,
- 0x8F1BBCDC,
- 0xCA62C1D6
- };
- int t; /* Loop counter */
- uint32_t temp; /* Temporary word value */
- uint32_t W[80]; /* Word sequence */
- uint32_t A, B, C, D, E; /* Word buffers */
-
- /*
- * Initialize the first 16 words in the array W
- */
- for (t = 0; t < 16; t++) {
- W[t] = ((uint32_t)context->Message_Block[t * 4]) << 24;
- W[t] |= ((uint32_t)context->Message_Block[t * 4 + 1]) << 16;
- W[t] |= ((uint32_t)context->Message_Block[t * 4 + 2]) << 8;
- W[t] |= ((uint32_t)context->Message_Block[t * 4 + 3]);
- }
-
- for (t = 16; t < 80; t++) {
- W[t] = SHA1CircularShift(1, W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
- }
-
- A = context->Intermediate_Hash[0];
- B = context->Intermediate_Hash[1];
- C = context->Intermediate_Hash[2];
- D = context->Intermediate_Hash[3];
- E = context->Intermediate_Hash[4];
-
- for (t = 0; t < 20; t++) {
- temp = SHA1CircularShift(5,A) + SHA_Ch(B, C, D) + E + W[t] + K[0];
- E = D;
- D = C;
- C = SHA1CircularShift(30,B);
- B = A;
- A = temp;
- }
-
- for (t = 20; t < 40; t++) {
- temp = SHA1CircularShift(5,A) + SHA_Parity(B, C, D) + E + W[t] + K[1];
- E = D;
- D = C;
- C = SHA1CircularShift(30,B);
- B = A;
- A = temp;
- }
-
- for (t = 40; t < 60; t++) {
- temp = SHA1CircularShift(5,A) + SHA_Maj(B, C, D) + E + W[t] + K[2];
- E = D;
- D = C;
- C = SHA1CircularShift(30,B);
- B = A;
- A = temp;
- }
-
- for (t = 60; t < 80; t++) {
- temp = SHA1CircularShift(5,A) + SHA_Parity(B, C, D) + E + W[t] + K[3];
- E = D;
- D = C;
- C = SHA1CircularShift(30,B);
- B = A;
- A = temp;
- }
-
- context->Intermediate_Hash[0] += A;
- context->Intermediate_Hash[1] += B;
- context->Intermediate_Hash[2] += C;
- context->Intermediate_Hash[3] += D;
- context->Intermediate_Hash[4] += E;
-
- context->Message_Block_Index = 0;
-}
diff --git a/src/lib/dns/util/sha1.h b/src/lib/dns/util/sha1.h
deleted file mode 100644
index f0f45f3..0000000
--- a/src/lib/dns/util/sha1.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * sha1.h
- *
- * Description:
- * This is the header file for code which implements the Secure
- * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
- * April 17, 1995.
- *
- * Many of the variable names in this code, especially the
- * single character names, were used because those were the names
- * used in the publication.
- *
- * Please read the file sha1.cc for more information.
- *
- * Authorship:
- * This file is adapted from RFC 4634, by D. Eastlake et al.
- * Copyright (C) The Internet Society (2006).
- *
- * Permission is granted for all uses, commercial and non-commercial,
- * of the sample code found in Section 8. Royalty free license to
- * use, copy, modify and distribute the software found in Section 8 is
- * granted, provided that this document is identified in all material
- * mentioning or referencing this software, and provided that
- * redistributed derivative works do not contain misleading author or
- * version information.
- *
- * The authors make no representations concerning either the
- * merchantability of this software or the suitability of this
- * software for any particular purpose. It is provided "as is"
- * without express or implied warranty of any kind.
- */
-
-#ifndef _SHA1_H_
-#define _SHA1_H_
-
-#include <stdint.h>
-/*
- * If you do not have the ISO standard stdint.h header file, then you
- * must typdef the following:
- * name meaning
- * uint32_t unsigned 32 bit integer
- * uint8_t unsigned 8 bit integer (i.e., unsigned char)
- * int_least16_t integer of >= 16 bits
- *
- */
-
-enum {
- SHA_SUCCESS = 0,
- SHA_NULL, /* Null pointer parameter */
- SHA_STATEERROR /* called Input after Result */
-};
-
-enum {
- SHA1_HASHSIZE = 20,
- SHA1_HASHBITS = 20,
- SHA1_BLOCKSIZE = 64
-};
-
-/*
- * This structure will hold context information for the SHA-1
- * hashing operation
- */
-typedef struct SHA1Context
-{
- uint32_t Intermediate_Hash[SHA1_HASHSIZE/4]; /* Message Digest */
- uint32_t Length_Low; /* Message length in bits */
- uint32_t Length_High; /* Message length in bits */
- int_least16_t Message_Block_Index; /* Index into message block array */
- uint8_t Message_Block[64]; /* 512-bit message blocks */
- int Computed; /* Is the digest computed? */
- int Corrupted; /* Is the message digest corrupted? */
-} SHA1Context;
-
-/*
- * Function Prototypes
- */
-extern int SHA1Reset(SHA1Context *);
-extern int SHA1Input(SHA1Context *, const uint8_t *bytes,
- unsigned int bytecount);
-extern int SHA1FinalBits(SHA1Context *, const uint8_t bits,
- unsigned int bitcount);
-extern int SHA1Result(SHA1Context *, uint8_t Message_Digest[SHA1_HASHSIZE]);
-
-#endif
diff --git a/src/lib/exceptions/exceptions.h b/src/lib/exceptions/exceptions.h
index a42037b..d0f1d74 100644
--- a/src/lib/exceptions/exceptions.h
+++ b/src/lib/exceptions/exceptions.h
@@ -163,6 +163,17 @@ public:
oss__ << stream; \
throw type(__FILE__, __LINE__, oss__.str().c_str()); \
} while (1)
+
+///
+/// Similar as isc_throw, but allows the exception to have one additional
+/// parameter (the stream/text goes first)
+#define isc_throw_1(type, stream, param1) \
+ do { \
+ std::ostringstream oss__; \
+ oss__ << stream; \
+ throw type(__FILE__, __LINE__, oss__.str().c_str(), param1); \
+ } while (1)
+
}
#endif // __EXCEPTIONS_H
diff --git a/src/lib/exceptions/tests/run_unittests.cc b/src/lib/exceptions/tests/run_unittests.cc
index 0908071..6a0de4f 100644
--- a/src/lib/exceptions/tests/run_unittests.cc
+++ b/src/lib/exceptions/tests/run_unittests.cc
@@ -17,5 +17,8 @@
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
+
+ // Unlike other tests we cannot use our wrapper for RUN_ALL_TESTS()
+ // due to dependency.
return (RUN_ALL_TESTS());
}
diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am
index d941b01..63b1dfb 100644
--- a/src/lib/log/Makefile.am
+++ b/src/lib/log/Makefile.am
@@ -2,31 +2,36 @@ SUBDIRS = . compiler tests
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = liblog.la
liblog_la_SOURCES =
-liblog_la_SOURCES += debug_levels.h logger_levels.h
liblog_la_SOURCES += dummylog.h dummylog.cc
-liblog_la_SOURCES += filename.h filename.cc
+liblog_la_SOURCES += logimpl_messages.cc logimpl_messages.h
+liblog_la_SOURCES += log_formatter.h log_formatter.cc
liblog_la_SOURCES += logger.cc logger.h
liblog_la_SOURCES += logger_impl.cc logger_impl.h
+liblog_la_SOURCES += logger_level.h
+liblog_la_SOURCES += logger_level.cc logger_level.h
+liblog_la_SOURCES += logger_level_impl.cc logger_level_impl.h
+liblog_la_SOURCES += logger_manager.cc logger_manager.h
+liblog_la_SOURCES += logger_manager_impl.cc logger_manager_impl.h
+liblog_la_SOURCES += logger_name.cc logger_name.h
+liblog_la_SOURCES += logger_specification.h
liblog_la_SOURCES += logger_support.cc logger_support.h
-liblog_la_SOURCES += messagedef.cc messagedef.h
+liblog_la_SOURCES += macros.h
+liblog_la_SOURCES += log_messages.cc log_messages.h
liblog_la_SOURCES += message_dictionary.cc message_dictionary.h
-liblog_la_SOURCES += message_exception.h message_exception.cc
+liblog_la_SOURCES += message_exception.h
liblog_la_SOURCES += message_initializer.cc message_initializer.h
liblog_la_SOURCES += message_reader.cc message_reader.h
liblog_la_SOURCES += message_types.h
-liblog_la_SOURCES += root_logger_name.cc root_logger_name.h
-liblog_la_SOURCES += strutil.h strutil.cc
+liblog_la_SOURCES += output_option.cc output_option.h
EXTRA_DIST = README
-EXTRA_DIST += messagedef.mes
-EXTRA_DIST += logger_impl_log4cxx.cc logger_impl_log4cxx.h
-EXTRA_DIST += xdebuglevel.cc xdebuglevel.h
+EXTRA_DIST += logimpl_messages.mes
+EXTRA_DIST += log_messages.mes
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
@@ -38,4 +43,6 @@ if USE_CLANGPP
# Same for clang++, but we need to turn off -Werror completely.
liblog_la_CXXFLAGS += -Wno-error
endif
-liblog_la_CPPFLAGS = $(AM_CPPFLAGS)
+liblog_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+liblog_la_LDFLAGS = $(LOG4CPLUS_LDFLAGS)
+liblog_la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
diff --git a/src/lib/log/README b/src/lib/log/README
index 072649e..3747cb1 100644
--- a/src/lib/log/README
+++ b/src/lib/log/README
@@ -1,11 +1,12 @@
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 definitions and text
+* Separation of message use from message text
Hierarchical Logging System
@@ -28,23 +29,26 @@ 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 Definitions And Text
-===========================================
-The reason for this is to allow the message text to be overridden by versions
-in a local language. To do this, each message is identified by an identifier
-e.g. "OPENIN". 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").
-substitutes any message parameters (in this example, the string that is an
-invalid filename) and logs it to the destination.
+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.
-In the BIND-10 system, a set of default messages are linked into the
-program. At run-time. each program reads a message file, updating the
-stored definitions; this updated text is logged. However, to aid support,
-the message identifier so in the example above, the message finally logged
-would be something like:
+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.
- OPENIN, unable to open a.txt for input
+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
@@ -52,20 +56,19 @@ 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, typically 6-12 characters long - and a message.
- The file is described in more detail below.
-
- Ideally the file should have a file type of ".msg".
+ 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.
-2. Run it through the message compiler to produce the .h and .cc files. It
- is intended that this step be included in the build process. However,
- for now run the compiler (found in the "compiler" subdirectory) manually.
- The only argument is the name of the message file: it will produce as
- output two files, having the same name as the input file but with file
- types of ".h" and ".cc".
+ Ideally the file should have a file type of ".mes".
- The compiler is built in the "compiler" subdirectory of the "src/lib/log"
- directory.
+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 -
@@ -75,9 +78,7 @@ The steps in using the system are:
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. This is a temporary solution
- for Year 2, and will be replaced at a later date, the information coming
- from the configuration database.
+ in logger_support.h) in the main program unit.
Message Files
@@ -92,15 +93,16 @@ An example file could be:
-- BEGIN --
# Example message file
-# $ID:$
-$PREFIX TEST_
$NAMESPACE isc::log
-TEST1 message %s is much too large
-+ This message is a test for the general message code
-UNKNOWN unknown message
-+ Issued when the message is unknown.
+% 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 --
@@ -115,60 +117,82 @@ Points to note:
a line by themselves - inline comments will be interpreted as part of the
text of the line.
-* Lines starting $ are directives. At present, two directives are recognised:
+* Lines starting $ are directives. At present, just one directive is
+ recognised:
- * $PREFIX, which has one argument: the string used to prefix symbols. If
- absent, there is no prefix to the symbols. (Prefixes are explained below.)
* $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 global namespace.
-
-* Lines starting + indicate an explanation for the preceding message. These
+ 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. However they are treated like comments by the
- message compiler. As with comments, these must be on a line by themselves;
- if inline, the text (including the leading "+") will be interpreted as
- part of the line.
-
-* Message lines. These comprise a symbol name and a message, which may
- include zero or more printf-style tokens. Symbol names will be upper-cased
- by the compiler.
-
+ 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] <message-file>
+ 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.
-("-v" prints the version number and exits; "-h" prints brief help text.)
-The message compiler processes the message file to produce two files:
+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 PREFIX_IDENTIFIER;
+ extern const isc::log::MessageID LOG_WRITE_ERROR;
:
}
-The symbols define the keys in the global message dictionary.
+The symbols define the keys in the global message dictionary, with the
+namespace enclosing the symbols set by the $NAMESPACE directive.
-The namespace enclosing the symbols is set by the $NAMESPACE directive.
-
-The "PREFIX_" part of the symbol name is the string defined in the $PREFIX
-the argument to the directive. So "$PREFIX MSG_" would prefix the identifer
-ABC with "MSG_" to give the symbol MSG_ABC. Similarly "$PREFIX E" would
-prefix it with "E" to give the symbol EABC. If no $PREFIX is given, no
-prefix appears (so the symbol in this example would be ABC).
+(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 MSG_DUPLNS = "DUPLNS";
+ 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.)
@@ -194,123 +218,252 @@ 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")
-Using the Logging
-=================
-To use the current version of the logging:
+(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. In the main module of the program, declare an instance of the
- RootLoggerName class to define the name of the program's root logger, e.g.
+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.
- #include <log/root_logger_name.h>
+ 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.)
- isc::log::RootLoggerName("b10-auth");
+ The settings remain in effect until the logging configuration is read,
+ and so provide the default logging during program initialization.
- This can be declared inside or outside an execution unit.
+3. Declare a logger through which the message will be logged.
-2. In the code that needs to do logging, declare a logger with a given name,
- e.g.
+ isc::log::Logger logger("name");
- #include <log/logger.h>
- :
- isc::log::Logger logger("myname"); // "myname" can be anything
+ 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.
- The above example assumes declaration outside a function. If declaring
- non-statically within a function, declare it as:
+4. Issue logging calls using supplied macros in "log/macros.h", e.g.
- isc::log::Logger logger("myname", true);
+ LOG_ERROR(logger, LOG_WRITE_ERROR).arg("output.txt");
- (The argument is required to support a possible future implementation of
- logging. Currently it has no effect.)
+ (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.)
-3. The main program unit should include a call to isc::log::initLogger()
- (defined in logger_support.h) to set the logging severity, debug log level,
- and external message file.
+Using the Logging - Python
+==========================
+1. Build message module as describe above.
- a) The logging severity is one of the enum defined in logger.h, i.e.
+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:
- isc::log::DEBUG
- isc::log::INFO
- isc::log::WARN
- isc::log::ERROR
- isc::log::FATAL
- isc::log::NONE
+ 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.
+ 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) Name of an external message file. 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.) If a message is replaced, the
- message should include the same printf-format directives in the same order
- as the original message.
+ 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.)
-4. Issue logging calls using methods on logger, e.g.
+ The settings remain in effect until the logging configuration is read,
+ and so provide the default logging during program initialization.
- logger.error(DPS_NSTIMEOUT, "isc.org");
+3. Declare a logger through which the message will be logged.
- (where, in the example above we might have defined the symbol in the message
- file with something along the lines of:
+ isc.log.Logger logger("name")
- $PREFIX DPS_
- :
- NSTIMEOUT queries to all nameservers for %s have timed out
+ 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.
- At present, the only logging is to the console.
+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);
+
+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.
+
+
+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.
+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 (via a call to abort()) or
-shortly afterwards, after dumping some diagnostic information.
+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.
+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,
+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.
+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 51 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:
+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
@@ -326,51 +479,34 @@ levels above it - will be logged.
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 authoritiative 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.
+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.
+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.
+(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.
-
-
-Outstanding Issues
-==================
-* Ability to configure system according to configuration database.
-* Update the build procedure to create .cc and .h files from the .msg file
- during the build process. (Requires that the message compiler is built
- first.)
-
-
-log4cxx Issues
-==============
-Some experimental code to utilise log4cxx as an underlying implementation
-is present in the source code directory although it is not currently used.
-The files are:
-
- logger_impl_log4cxx.{cc,h}
- xdebuglevel.{cc,h}
+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/compiler/Makefile.am b/src/lib/log/compiler/Makefile.am
index 9343793..1f47ba9 100644
--- a/src/lib/log/compiler/Makefile.am
+++ b/src/lib/log/compiler/Makefile.am
@@ -1,7 +1,6 @@
SUBDIRS = .
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -13,6 +12,7 @@ endif
CLEANFILES = *.gcno *.gcda
noinst_PROGRAMS = message
-message_SOURCES = message.cc
-message_LDADD = $(top_builddir)/src/lib/log/liblog.la
+message_SOURCES = message.cc
+message_LDADD = $(top_builddir)/src/lib/log/liblog.la
+message_LDADD += $(top_builddir)/src/lib/util/libutil.la
diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc
index 6f9c4e0..68335dc 100644
--- a/src/lib/log/compiler/message.cc
+++ b/src/lib/log/compiler/message.cc
@@ -25,17 +25,21 @@
#include <time.h>
#include <unistd.h>
-#include <log/filename.h>
+#include <util/filename.h>
+#include <util/strutil.h>
+
+#include <log/log_messages.h>
#include <log/message_dictionary.h>
#include <log/message_exception.h>
#include <log/message_reader.h>
-#include <log/messagedef.h>
-#include <log/strutil.h>
#include <log/logger.h>
+#include <boost/foreach.hpp>
+
using namespace std;
using namespace isc::log;
+using namespace isc::util;
static const char* VERSION = "1.0-0";
@@ -48,17 +52,13 @@ static const char* VERSION = "1.0-0";
/// \li A .cc file containing code that adds the messages to the program's
/// message dictionary at start-up time.
///
-/// Alternatively, the program can produce a .py file that contains the
-/// message definitions.
-///
-
/// \b Invocation<BR>
/// The program is invoked with the command:
///
/// <tt>message [-v | -h | \<message-file\>]</tt>
///
-/// It reads the message file and writes out two files of the same name but with
-/// extensions of .h and .cc.
+/// It reads the message file and writes out two files of the same name in the
+/// default directory but with extensions of .h and .cc.
///
/// \-v causes it to print the version number and exit. \-h prints a help
/// message (and exits).
@@ -80,10 +80,11 @@ version() {
void
usage() {
cout <<
- "Usage: message [-h] [-v] <message-file>\n" <<
+ "Usage: message [-h] [-v] [-p] <message-file>\n" <<
"\n" <<
"-h Print this message and exit\n" <<
"-v Print the program version and exit\n" <<
+ "-p Output python source instead of C++ ones\n" <<
"\n" <<
"<message-file> is the name of the input message file.\n";
}
@@ -105,7 +106,7 @@ currentTime() {
// Convert to string and strip out the trailing newline
string current_time = buffer;
- return isc::strutil::trim(current_time);
+ return isc::util::str::trim(current_time);
}
@@ -125,7 +126,7 @@ sentinel(Filename& file) {
string name = file.name();
string ext = file.extension();
string sentinel_text = "__" + name + "_" + ext.substr(1);
- isc::strutil::uppercase(sentinel_text);
+ isc::util::str::uppercase(sentinel_text);
return sentinel_text;
}
@@ -206,7 +207,7 @@ splitNamespace(string ns) {
// ... and return the vector of namespace components split on the single
// colon.
- return isc::strutil::tokens(ns, ":");
+ return isc::util::str::tokens(ns, ":");
}
@@ -239,6 +240,44 @@ writeClosingNamespace(ostream& output, const vector<string>& ns) {
}
}
+/// \breif Write python file
+///
+/// Writes the python file containing the symbol definitions as module level
+/// constants. These are objects which register themself at creation time,
+/// so they can be replaced by dictionary later.
+///
+/// \param file Name of the message file. The source code is written to a file
+/// file of the same name but with a .py suffix.
+/// \param dictionary The dictionary holding the message definitions.
+///
+/// \note We don't use the namespace as in C++. We don't need it, because
+/// python file/module works as implicit namespace as well.
+
+void
+writePythonFile(const string& file, MessageDictionary& dictionary) {
+ Filename message_file(file);
+ Filename python_file(Filename(message_file.name()).useAsDefault(".py"));
+
+ // Open the file for writing
+ ofstream pyfile(python_file.fullName().c_str());
+
+ // Write the comment and imports
+ pyfile <<
+ "# File created from " << message_file.fullName() << " on " <<
+ currentTime() << "\n" <<
+ "\n" <<
+ "import isc.log\n" <<
+ "\n";
+
+ vector<string> idents(sortedIdentifiers(dictionary));
+ BOOST_FOREACH(const string& ident, idents) {
+ pyfile << ident << " = isc.log.create_message(\"" <<
+ ident << "\", \"" << quoteString(dictionary.getText(ident)) <<
+ "\")\n";
+ }
+
+ pyfile.close();
+}
/// \brief Write Header File
///
@@ -249,17 +288,16 @@ writeClosingNamespace(ostream& output, const vector<string>& ns) {
///
/// \param file Name of the message file. The header file is written to a
/// file of the same name but with a .h suffix.
-/// \param prefix Prefix string to use in symbols
/// \param ns Namespace in which the definitions are to be placed. An empty
/// string indicates no namespace.
/// \param dictionary Dictionary holding the message definitions.
void
-writeHeaderFile(const string& file, const string& prefix,
- const vector<string>& ns_components, MessageDictionary& dictionary)
+writeHeaderFile(const string& file, const vector<string>& ns_components,
+ MessageDictionary& dictionary)
{
Filename message_file(file);
- Filename header_file(message_file.useAsDefault(".h"));
+ Filename header_file(Filename(message_file.name()).useAsDefault(".h"));
// Text to use as the sentinels.
string sentinel_text = sentinel(header_file);
@@ -267,52 +305,46 @@ writeHeaderFile(const string& file, const string& prefix,
// Open the output file for writing
ofstream hfile(header_file.fullName().c_str());
- try {
- if (hfile.fail()) {
- throw MessageException(MSG_OPNMSGOUT, header_file.fullName(),
- strerror(errno));
- }
-
- // Write the header preamble. If there is an error, we'll pick it up
- // after the last write.
-
- hfile <<
- "// File created from " << message_file.fullName() << " on " <<
- currentTime() << "\n" <<
- "\n" <<
- "#ifndef " << sentinel_text << "\n" <<
- "#define " << sentinel_text << "\n" <<
- "\n" <<
- "#include <log/message_types.h>\n" <<
- "\n";
-
- // Write the message identifiers, bounded by a namespace declaration
- writeOpeningNamespace(hfile, ns_components);
-
- vector<string> idents = sortedIdentifiers(dictionary);
- for (vector<string>::const_iterator j = idents.begin();
- j != idents.end(); ++j) {
- hfile << "extern const isc::log::MessageID " << prefix << *j << ";\n";
- }
- hfile << "\n";
+ if (hfile.fail()) {
+ throw MessageException(LOG_OPEN_OUTPUT_FAIL, header_file.fullName(),
+ strerror(errno));
+ }
- writeClosingNamespace(hfile, ns_components);
+ // Write the header preamble. If there is an error, we'll pick it up
+ // after the last write.
+
+ hfile <<
+ "// File created from " << message_file.fullName() << " on " <<
+ currentTime() << "\n" <<
+ "\n" <<
+ "#ifndef " << sentinel_text << "\n" <<
+ "#define " << sentinel_text << "\n" <<
+ "\n" <<
+ "#include <log/message_types.h>\n" <<
+ "\n";
+
+ // Write the message identifiers, bounded by a namespace declaration
+ writeOpeningNamespace(hfile, ns_components);
+
+ vector<string> idents = sortedIdentifiers(dictionary);
+ for (vector<string>::const_iterator j = idents.begin();
+ j != idents.end(); ++j) {
+ hfile << "extern const isc::log::MessageID " << *j << ";\n";
+ }
+ hfile << "\n";
- // ... and finally the postamble
- hfile << "#endif // " << sentinel_text << "\n";
+ writeClosingNamespace(hfile, ns_components);
- // Report errors (if any) and exit
- if (hfile.fail()) {
- throw MessageException(MSG_MSGWRTERR, header_file.fullName(),
- strerror(errno));
- }
+ // ... and finally the postamble
+ hfile << "#endif // " << sentinel_text << "\n";
- hfile.close();
- }
- catch (MessageException&) {
- hfile.close();
- throw;
+ // Report errors (if any) and exit
+ if (hfile.fail()) {
+ throw MessageException(LOG_WRITE_ERROR, header_file.fullName(),
+ strerror(errno));
}
+
+ hfile.close();
}
@@ -352,84 +384,79 @@ replaceNonAlphaNum(char c) {
/// to it. But until BIND-10 is ported to Windows, we won't know.
void
-writeProgramFile(const string& file, const string& prefix,
- const vector<string>& ns_components, MessageDictionary& dictionary)
+writeProgramFile(const string& file, const vector<string>& ns_components,
+ MessageDictionary& dictionary)
{
Filename message_file(file);
- Filename program_file(message_file.useAsDefault(".cc"));
+ Filename program_file(Filename(message_file.name()).useAsDefault(".cc"));
// Open the output file for writing
ofstream ccfile(program_file.fullName().c_str());
- try {
- if (ccfile.fail()) {
- throw MessageException(MSG_OPNMSGOUT, program_file.fullName(),
- strerror(errno));
- }
- // Write the preamble. If there is an error, we'll pick it up after
- // the last write.
+ if (ccfile.fail()) {
+ throw MessageException(LOG_OPEN_OUTPUT_FAIL, program_file.fullName(),
+ strerror(errno));
+ }
- ccfile <<
- "// File created from " << message_file.fullName() << " on " <<
- currentTime() << "\n" <<
- "\n" <<
- "#include <cstddef>\n" <<
- "#include <log/message_types.h>\n" <<
- "#include <log/message_initializer.h>\n" <<
- "\n";
+ // Write the preamble. If there is an error, we'll pick it up after
+ // the last write.
- // Declare the message symbols themselves.
+ ccfile <<
+ "// File created from " << message_file.fullName() << " on " <<
+ currentTime() << "\n" <<
+ "\n" <<
+ "#include <cstddef>\n" <<
+ "#include <log/message_types.h>\n" <<
+ "#include <log/message_initializer.h>\n" <<
+ "\n";
- writeOpeningNamespace(ccfile, ns_components);
+ // Declare the message symbols themselves.
- vector<string> idents = sortedIdentifiers(dictionary);
- for (vector<string>::const_iterator j = idents.begin();
- j != idents.end(); ++j) {
- ccfile << "extern const isc::log::MessageID " << prefix << *j <<
- " = \"" << *j << "\";\n";
- }
- ccfile << "\n";
+ writeOpeningNamespace(ccfile, ns_components);
- writeClosingNamespace(ccfile, ns_components);
+ vector<string> idents = sortedIdentifiers(dictionary);
+ for (vector<string>::const_iterator j = idents.begin();
+ j != idents.end(); ++j) {
+ ccfile << "extern const isc::log::MessageID " << *j <<
+ " = \"" << *j << "\";\n";
+ }
+ ccfile << "\n";
- // Now the code for the message initialization.
+ writeClosingNamespace(ccfile, ns_components);
- ccfile <<
- "namespace {\n" <<
- "\n" <<
- "const char* values[] = {\n";
+ // Now the code for the message initialization.
- // Output the identifiers and the associated text.
- idents = sortedIdentifiers(dictionary);
- for (vector<string>::const_iterator i = idents.begin();
- i != idents.end(); ++i) {
- ccfile << " \"" << *i << "\", \"" <<
- quoteString(dictionary.getText(*i)) << "\",\n";
- }
+ ccfile <<
+ "namespace {\n" <<
+ "\n" <<
+ "const char* values[] = {\n";
+ // Output the identifiers and the associated text.
+ idents = sortedIdentifiers(dictionary);
+ for (vector<string>::const_iterator i = idents.begin();
+ i != idents.end(); ++i) {
+ ccfile << " \"" << *i << "\", \"" <<
+ quoteString(dictionary.getText(*i)) << "\",\n";
+ }
- // ... and the postamble
- ccfile <<
- " NULL\n" <<
- "};\n" <<
- "\n" <<
- "const isc::log::MessageInitializer initializer(values);\n" <<
- "\n" <<
- "} // Anonymous namespace\n" <<
- "\n";
-
- // Report errors (if any) and exit
- if (ccfile.fail()) {
- throw MessageException(MSG_MSGWRTERR, program_file.fullName(),
- strerror(errno));
- }
- ccfile.close();
- }
- catch (MessageException&) {
- ccfile.close();
- throw;
+ // ... and the postamble
+ ccfile <<
+ " NULL\n" <<
+ "};\n" <<
+ "\n" <<
+ "const isc::log::MessageInitializer initializer(values);\n" <<
+ "\n" <<
+ "} // Anonymous namespace\n" <<
+ "\n";
+
+ // Report errors (if any) and exit
+ if (ccfile.fail()) {
+ throw MessageException(LOG_WRITE_ERROR, program_file.fullName(),
+ strerror(errno));
}
+
+ ccfile.close();
}
@@ -469,13 +496,19 @@ warnDuplicates(MessageReader& reader) {
int
main(int argc, char* argv[]) {
- const char* soptions = "hv"; // Short options
+ const char* soptions = "hvp"; // Short options
optind = 1; // Ensure we start a new scan
int opt; // Value of the option
+ bool doPython = false;
+
while ((opt = getopt(argc, argv, soptions)) != -1) {
switch (opt) {
+ case 'p':
+ doPython = true;
+ break;
+
case 'h':
usage();
return 0;
@@ -511,18 +544,27 @@ main(int argc, char* argv[]) {
MessageReader reader(&dictionary);
reader.readFile(message_file);
- // Get the namespace into which the message definitions will be put and
- // split it into components.
- vector<string> ns_components = splitNamespace(reader.getNamespace());
-
- // Write the header file.
- writeHeaderFile(message_file, reader.getPrefix(), ns_components,
- dictionary);
-
- // Write the file that defines the message symbols and text
- writeProgramFile(message_file, reader.getPrefix(), ns_components,
- dictionary);
-
+ if (doPython) {
+ // Warn in case of ignored directives
+ if (!reader.getNamespace().empty()) {
+ cerr << "Python mode, ignoring the $NAMESPACE directive" <<
+ endl;
+ }
+
+ // Write the whole python file
+ writePythonFile(message_file, dictionary);
+ } else {
+ // Get the namespace into which the message definitions will be put and
+ // split it into components.
+ vector<string> ns_components =
+ splitNamespace(reader.getNamespace());
+
+ // Write the header file.
+ writeHeaderFile(message_file, ns_components, dictionary);
+
+ // Write the file that defines the message symbols and text
+ writeProgramFile(message_file, ns_components, dictionary);
+ }
// Finally, warn of any duplicates encountered.
warnDuplicates(reader);
@@ -533,9 +575,12 @@ main(int argc, char* argv[]) {
string text = e.id();
text += ", ";
text += global.getText(e.id());
-
// Format with arguments
- text = isc::strutil::format(text, e.arguments());
+ vector<string> args(e.arguments());
+ for (size_t i(0); i < args.size(); ++ i) {
+ replacePlaceholder(&text, args[i], i + 1);
+ }
+
cerr << text << "\n";
return 1;
diff --git a/src/lib/log/debug_levels.h b/src/lib/log/debug_levels.h
deleted file mode 100644
index bb2b524..0000000
--- a/src/lib/log/debug_levels.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __DEBUG_LEVELS_H
-#define __DEBUG_LEVELS_H
-
-/// \brief Defines Debug Levels
-///
-/// Defines the maximum and minimum debug levels and the number of levels.
-/// These are defined using #define as they are referenced in the construction
-/// of variables declared outside execution units. (In this way we avoid the
-/// "static initialization fiasco" problem.)
-
-#define MIN_DEBUG_LEVEL (0)
-#define MAX_DEBUG_LEVEL (99)
-#define NUM_DEBUG_LEVEL (MAX_DEBUG_LEVEL - MIN_DEBUG_LEVEL + 1)
-
-#endif // __DEBUG_LEVELS_H
diff --git a/src/lib/log/dummylog.cc b/src/lib/log/dummylog.cc
index dda0578..5f025e1 100644
--- a/src/lib/log/dummylog.cc
+++ b/src/lib/log/dummylog.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 CZ NIC
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
diff --git a/src/lib/log/dummylog.h b/src/lib/log/dummylog.h
index 87da9ac..ef5af13 100644
--- a/src/lib/log/dummylog.h
+++ b/src/lib/log/dummylog.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 CZ NIC
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -38,7 +38,7 @@ extern std::string dprefix;
* places where logging should happen. When it is removed, compiler will do
* our work of finding the places.
*
- * The only thing it does is printing the dprogram prefix, message and
+ * The only thing it does is printing the program prefix, message and
* a newline if denabled is true.
*
* There are no tests for this function, since it is only temporary and
diff --git a/src/lib/log/filename.cc b/src/lib/log/filename.cc
deleted file mode 100644
index 91835af..0000000
--- a/src/lib/log/filename.cc
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <iostream>
-#include <algorithm>
-#include <string>
-
-#include <ctype.h>
-
-#include <log/filename.h>
-#include <log/strutil.h>
-
-using namespace std;
-
-
-namespace isc {
-namespace log {
-
-// Split string into components. Any backslashes are assumed to have
-// been replaced by forward slashes.
-
-void
-Filename::split(const string& full_name, string& directory,
- string& name, string& extension) const
-{
- directory = name = extension = "";
- bool dir_present = false;
- if (!full_name.empty()) {
-
- // Find the directory.
- size_t last_slash = full_name.find_last_of('/');
- if (last_slash != string::npos) {
-
- // Found the last slash, so extract directory component and
- // set where the scan for the last_dot should terminate.
- directory = full_name.substr(0, last_slash + 1);
- if (last_slash == full_name.size()) {
-
- // The entire string was a directory, so exit not and don't
- // do any more searching.
- return;
- }
-
- // Found a directory so note the fact.
- dir_present = true;
- }
-
- // Now search backwards for the last ".".
- size_t last_dot = full_name.find_last_of('.');
- if ((last_dot == string::npos) ||
- (dir_present && (last_dot < last_slash))) {
-
- // Last "." either not found or it occurs to the left of the last
- // slash if a directory was present (so it is part of a directory
- // name). In this case, the remainder of the string after the slash
- // is the name part.
- name = full_name.substr(last_slash + 1);
- return;
- }
-
- // Did find a valid dot, so it and everything to the right is the
- // extension...
- extension = full_name.substr(last_dot);
-
- // ... and the name of the file is everything in between.
- if ((last_dot - last_slash) > 1) {
- name = full_name.substr(last_slash + 1, last_dot - last_slash - 1);
- }
- }
-
-}
-
-// Expand the stored filename with the default.
-
-string
-Filename::expandWithDefault(const string& defname) const {
-
- string def_directory("");
- string def_name("");
- string def_extension("");
-
- // Normalize the input string.
- string copy_defname = isc::strutil::trim(defname);
-#ifdef WIN32
- isc::strutil::normalizeSlash(copy_defname);
-#endif
-
- // Split into the components
- split(copy_defname, def_directory, def_name, def_extension);
-
- // Now construct the result.
- string retstring =
- (directory_.empty() ? def_directory : directory_) +
- (name_.empty() ? def_name : name_) +
- (extension_.empty() ? def_extension : extension_);
- return (retstring);
-}
-
-// Use the stored name as default for a given name
-
-string
-Filename::useAsDefault(const string& name) const {
-
- string name_directory("");
- string name_name("");
- string name_extension("");
-
- // Normalize the input string.
- string copy_name = isc::strutil::trim(name);
-#ifdef WIN32
- isc::strutil::normalizeSlash(copy_name);
-#endif
-
- // Split into the components
- split(copy_name, name_directory, name_name, name_extension);
-
- // Now construct the result.
- string retstring =
- (name_directory.empty() ? directory_ : name_directory) +
- (name_name.empty() ? name_ : name_name) +
- (name_extension.empty() ? extension_ : name_extension);
- return (retstring);
-}
-
-
-} // namespace log
-} // namespace isc
diff --git a/src/lib/log/filename.h b/src/lib/log/filename.h
deleted file mode 100644
index e3cda16..0000000
--- a/src/lib/log/filename.h
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __FILENAME_H
-#define __FILENAME_H
-
-#include <string>
-
-#include <strutil.h>
-
-namespace isc {
-namespace log {
-
-/// \brief Class to Manipulate Filenames
-///
-/// This is a utility class to manipulate filenames. It repeats some of the
-/// features found in the Boost filename class, but is self-contained so avoids
-/// the need to link in the Boost library.
-///
-/// A Unix-style filename comprises three parts:
-///
-/// Directory - everything up to and including the last "/". If there is no
-/// "/" in the string, there is no directory component. Note that the
-/// requirement of a trailing slash eliminates the ambiguity of whether a
-/// component is a directory or not, e.g. in /alpha/beta", "beta" could be the
-/// name of a directory or is could be a file. The interpretation here is that
-/// "beta" is the name of a file (although that file could be a directory).
-///
-/// Note: Under Windows, the drive letter is considered to be part of the
-/// directory specification. Unless this class becomes more widely-used on
-/// Windows, there is no point in adding redundant code.
-///
-/// Name - everthing from the character after the last "/" up to but not
-/// including the last ".".
-///
-/// Extension - everthing from the right-most "." (after the right-most "/") to
-/// the end of the string. If there is no "." after the last "/", there is
-/// no file extension.
-///
-/// (Note that on Windows, this function will replace all "\" characters
-/// with "/" characters on input strings.)
-///
-/// This class provides functions for extracting the components and for
-/// substituting components.
-
-
-class Filename {
-public:
-
- /// \brief Constructor
- Filename(const std::string& name) :
- full_name_(""), directory_(""), name_(""), extension_("")
- {
- setName(name);
- }
-
- /// \brief Sets Stored Filename
- ///
- /// \param name New name to replaced currently stored name
- void setName(const std::string& name) {
- full_name_ = isc::strutil::trim(name);
-#ifdef WIN32
- isc::strutil::normalizeSlash(full_name_);
-#endif
- split(full_name_, directory_, name_, extension_);
- }
-
- /// \return Stored Filename
- std::string fullName() const {
- return (full_name_);
- }
-
- /// \return Directory of Given File Name
- std::string directory() const {
- return (directory_);
- }
-
- /// \return Name of Given File Name
- std::string name() const {
- return (name_);
- }
-
- /// \return Extension of Given File Name
- std::string extension() const {
- return (extension_);
- }
-
- /// \brief Expand Name with Default
- ///
- /// A default file specified is supplied and used to fill in any missing
- /// fields. For example, if the name stored is "/a/b" and the supplied
- /// name is "c.d", the result is "/a/b.d": the only field missing from the
- /// stored name is the extension, which is supplied by the default.
- /// Another example would be to store "a.b" and to supply a default of
- /// "/c/d/" - the result is "/c/d/a.b". (Note that if the supplied default
- /// was "/c/d", the result would be "/c/a.b", even if "/c/d" were actually
- /// a directory.)
- ///
- /// \param defname Default name
- ///
- /// \return Name expanded with defname.
- std::string expandWithDefault(const std::string& defname) const;
-
- /// \brief Use as Default and Substitute into String
- ///
- /// Does essentially the inverse of expand(); that filled in the stored
- /// name with a default and returned the result. This treats the stored
- /// name as the default and uses it to fill in a given name. In essence,
- /// the code:
- /// \code
- /// Filename f("/a/b");
- /// result = f.expandWithdefault("c.d");
- /// \endcode
- /// gives as a result "/a/b.d". This is the same as:
- /// \code
- /// Filename f("c.d");
- /// result = f.useAsDefault("/a/b");
- /// \endcode
- ///
- /// \param name Name to expand
- ///
- /// \return Name expanded with stored name
- std::string useAsDefault(const std::string& name) const;
-
-private:
- /// \brief Split Name into Components
- ///
- /// Splits the file name into the directory, name and extension parts.
- /// The name is assumed to have had back slashes replaced by forward
- /// slashes (if appropriate).
- ///
- /// \param full_name Name to split
- /// \param directory Returned directory part
- /// \param name Returned name part
- /// \param extension Returned extension part
- void split(const std::string& full_name, std::string& directory,
- std::string& name, std::string& extension) const;
-
- // Members
-
- std::string full_name_; ///< Given name
- std::string directory_; ///< Directory part
- std::string name_; ///< Name part
- std::string extension_; ///< Extension part
-};
-
-} // namespace log
-} // namespace isc
-
-#endif // __FILENAME_H
diff --git a/src/lib/log/log_formatter.cc b/src/lib/log/log_formatter.cc
new file mode 100644
index 0000000..18c4741
--- /dev/null
+++ b/src/lib/log/log_formatter.cc
@@ -0,0 +1,42 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <log/log_formatter.h>
+
+using namespace std;
+using namespace boost;
+
+namespace isc {
+namespace log {
+
+void
+replacePlaceholder(string* message, const string& arg,
+ const unsigned placeholder)
+{
+ string mark("%" + lexical_cast<string>(placeholder));
+ size_t pos(message->find(mark));
+ if (pos != string::npos) {
+ do {
+ message->replace(pos, mark.size(), arg);
+ pos = message->find(mark, pos + arg.size());
+ } while (pos != string::npos);
+ } else {
+ // We're missing the placeholder, so add some complain
+ message->append(" @@Missing placeholder " + mark + " for '" + arg +
+ "'@@");
+ }
+}
+
+}
+}
diff --git a/src/lib/log/log_formatter.h b/src/lib/log/log_formatter.h
new file mode 100644
index 0000000..ca23844
--- /dev/null
+++ b/src/lib/log/log_formatter.h
@@ -0,0 +1,219 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOG_FORMATTER_H
+#define __LOG_FORMMATER_H
+
+#include <cstddef>
+#include <string>
+#include <iostream>
+
+#include <exceptions/exceptions.h>
+#include <boost/lexical_cast.hpp>
+#include <log/logger_level.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Format Failure
+///
+/// This exception is used to wrap a bad_lexical_cast exception thrown during
+/// formatting an argument.
+
+class FormatFailure : public isc::Exception {
+public:
+ FormatFailure(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what)
+ {}
+};
+
+
+///
+/// \brief The internal replacement routine
+///
+/// This is used internally by the Formatter. Replaces a placeholder
+/// in the message by replacement. If the placeholder is not found,
+/// it adds a complain at the end.
+void
+replacePlaceholder(std::string* message, const std::string& replacement,
+ const unsigned placeholder);
+
+///
+/// \brief The log message formatter
+///
+/// This class allows us to format logging messages conveniently. We
+/// call something like logger.warn(WARN_MSG).arg(15).arg(dnsMsg). This
+/// outputs some text with placeholders replaced by the arguments, if
+/// the logging verbosity is at WARN level or more.
+///
+/// To make this work, we use the Formatter. The warn (or whatever logging
+/// function) returns a Formatter object. That one holds the string to be
+/// output with the placeholders. It also remembers if there should be any
+/// output at all (eg. if the logging is enabled for this level). When there's
+/// no .arg call on the object, it is destroyed right away and we use the
+/// destructor to output the text (but only in case we should output anything).
+///
+/// If there's an .arg call, we return reference to the same object, so another
+/// .arg can be called on it. After the last .arg call is done, the object is
+/// destroyed and, again, we can produce the output.
+///
+/// Of course, if the logging is turned off, we don't bother with any replacing
+/// and just return.
+///
+/// User of logging code should not really care much about this class, only
+/// call the .arg method to generate the correct output.
+///
+/// The class is a template to allow easy testing. Also, we want everything
+/// here in the header anyway and it doesn't depend on the details of what
+/// Logger really is, so it doesn't hurt anything.
+///
+/// Also, if you are interested in the internals, you might find the copy
+/// constructor a bit strange. It deactivates the original formatter. We don't
+/// really want to support copying of the Formatter by user, but C++ needs a
+/// copy constructor when returning from the logging functions, so we need one.
+/// And if we did not deactivate the original Formatter, that one would get
+/// destroyed before any call to .arg, producing an output, and then the one
+/// the .arg calls are called on would get destroyed as well, producing output
+/// again. So, think of this behaviour as soul moving from one to another.
+template<class Logger> class Formatter {
+private:
+ /// \brief The logger we will use to output the final message.
+ ///
+ /// If NULL, we are not active and should not produce anything.
+ mutable Logger* logger_;
+
+ /// \brief Message severity
+ Severity severity_;
+
+ /// \brief The messages with %1, %2... placeholders
+ std::string* message_;
+
+ /// \brief Which will be the next placeholder to replace
+ unsigned nextPlaceholder_;
+
+
+public:
+ /// \brief Constructor of "active" formatter
+ ///
+ /// This will create a formatter. If the arguments are set, it
+ /// will be active (will produce output). If you leave them all as NULL,
+ /// it will create an inactive Formatter -- one that'll produce no output.
+ ///
+ /// It is not expected to be called by user of logging system directly.
+ ///
+ /// \param severity The severity of the message (DEBUG, ERROR etc.)
+ /// \param message The message with placeholders. We take ownership of
+ /// it and we will modify the string. Must not be NULL unless
+ /// logger is also NULL, but it's not checked.
+ /// \param logger The logger where the final output will go, or NULL
+ /// if no output is wanted.
+ Formatter(const Severity& severity = NONE, std::string* message = NULL,
+ Logger* logger = NULL) :
+ logger_(logger), severity_(severity), message_(message),
+ nextPlaceholder_(0)
+ {
+ }
+
+ /// \brief Copy constructor
+ ///
+ /// "Control" is passed to the created object in that it is the created object
+ /// that will have responsibility for outputting the formatted message - the
+ /// object being copied relinquishes that responsibility.
+ Formatter(const Formatter& other) :
+ logger_(other.logger_), severity_(other.severity_),
+ message_(other.message_), nextPlaceholder_(other.nextPlaceholder_)
+ {
+ other.logger_ = NULL;
+ }
+
+ /// \brief Destructor.
+ //
+ /// This is the place where output happens if the formatter is active.
+ ~ Formatter() {
+ if (logger_) {
+ logger_->output(severity_, *message_);
+ delete message_;
+ }
+ }
+
+ /// \brief Assignment operator
+ ///
+ /// Essentially the same function as the assignment operator - the object being
+ /// assigned to takes responsibility for outputting the message.
+ Formatter& operator =(const Formatter& other) {
+ if (&other != this) {
+ logger_ = other.logger_;
+ severity_ = other.severity_;
+ message_ = other.message_;
+ nextPlaceholder_ = other.nextPlaceholder_;
+ other.logger_ = NULL;
+ }
+
+ return *this;
+ }
+
+ /// \brief Replaces another placeholder
+ ///
+ /// Replaces another placeholder and returns a new formatter with it.
+ /// Deactivates the current formatter. In case the formatter is not active,
+ /// only produces another inactive formatter.
+ ///
+ /// \param arg The argument to place into the placeholder.
+ template<class Arg> Formatter& arg(const Arg& value) {
+ if (logger_) {
+ try {
+ return (arg(boost::lexical_cast<std::string>(value)));
+ } catch (const boost::bad_lexical_cast& ex) {
+
+ // A bad_lexical_cast during a conversion to a string is
+ // *extremely* unlikely to fail. However, there is nothing
+ // in the documentation that rules it out, so we need to handle
+ // it. As it is a potentially very serious problem, throw the
+ // exception detailing the problem with as much information as
+ // we can. (Note that this does not include 'value' -
+ // boost::lexical_cast failed to convert it to a string, so an
+ // attempt to do so here would probably fail as well.)
+ isc_throw(FormatFailure, "bad_lexical_cast in call to "
+ "Formatter::arg(): " << ex.what());
+ }
+ } else {
+ return (*this);
+ }
+ }
+
+ /// \brief String version of arg.
+ ///
+ /// \param arg The text to place into the placeholder.
+ Formatter& arg(const std::string& arg) {
+ if (logger_) {
+ // Note that this method does a replacement and returns the
+ // modified string. If there are multiple invocations of arg() (e.g.
+ // logger.info(msgid).arg(xxx).arg(yyy)...), each invocation
+ // operates on the string returned by the previous one. This
+ // sequential operation means that if we had a message like "%1 %2",
+ // and called .arg("%2").arg(42), we would get "42 42"; the first
+ // call replaces the %1" with "%2" and the second replaces all
+ // occurrences of "%2" with 42. (Conversely, the sequence
+ // .arg(42).arg("%1") would return "42 %1" - there are no recursive
+ // replacements).
+ replacePlaceholder(message_, arg, ++nextPlaceholder_ );
+ }
+ return (*this);
+ }
+};
+
+}
+}
+
+#endif
diff --git a/src/lib/log/log_messages.cc b/src/lib/log/log_messages.cc
new file mode 100644
index 0000000..f60898c
--- /dev/null
+++ b/src/lib/log/log_messages.cc
@@ -0,0 +1,63 @@
+// File created from log_messages.mes on Thu Jul 7 15:32:06 2011
+
+#include <cstddef>
+#include <log/message_types.h>
+#include <log/message_initializer.h>
+
+namespace isc {
+namespace log {
+
+extern const isc::log::MessageID LOG_BAD_DESTINATION = "LOG_BAD_DESTINATION";
+extern const isc::log::MessageID LOG_BAD_SEVERITY = "LOG_BAD_SEVERITY";
+extern const isc::log::MessageID LOG_BAD_STREAM = "LOG_BAD_STREAM";
+extern const isc::log::MessageID LOG_DUPLICATE_MESSAGE_ID = "LOG_DUPLICATE_MESSAGE_ID";
+extern const isc::log::MessageID LOG_DUPLICATE_NAMESPACE = "LOG_DUPLICATE_NAMESPACE";
+extern const isc::log::MessageID LOG_INPUT_OPEN_FAIL = "LOG_INPUT_OPEN_FAIL";
+extern const isc::log::MessageID LOG_INVALID_MESSAGE_ID = "LOG_INVALID_MESSAGE_ID";
+extern const isc::log::MessageID LOG_NAMESPACE_EXTRA_ARGS = "LOG_NAMESPACE_EXTRA_ARGS";
+extern const isc::log::MessageID LOG_NAMESPACE_INVALID_ARG = "LOG_NAMESPACE_INVALID_ARG";
+extern const isc::log::MessageID LOG_NAMESPACE_NO_ARGS = "LOG_NAMESPACE_NO_ARGS";
+extern const isc::log::MessageID LOG_NO_MESSAGE_ID = "LOG_NO_MESSAGE_ID";
+extern const isc::log::MessageID LOG_NO_MESSAGE_TEXT = "LOG_NO_MESSAGE_TEXT";
+extern const isc::log::MessageID LOG_NO_SUCH_MESSAGE = "LOG_NO_SUCH_MESSAGE";
+extern const isc::log::MessageID LOG_OPEN_OUTPUT_FAIL = "LOG_OPEN_OUTPUT_FAIL";
+extern const isc::log::MessageID LOG_PREFIX_EXTRA_ARGS = "LOG_PREFIX_EXTRA_ARGS";
+extern const isc::log::MessageID LOG_PREFIX_INVALID_ARG = "LOG_PREFIX_INVALID_ARG";
+extern const isc::log::MessageID LOG_READING_LOCAL_FILE = "LOG_READING_LOCAL_FILE";
+extern const isc::log::MessageID LOG_READ_ERROR = "LOG_READ_ERROR";
+extern const isc::log::MessageID LOG_UNRECOGNISED_DIRECTIVE = "LOG_UNRECOGNISED_DIRECTIVE";
+extern const isc::log::MessageID LOG_WRITE_ERROR = "LOG_WRITE_ERROR";
+
+} // namespace log
+} // namespace isc
+
+namespace {
+
+const char* values[] = {
+ "LOG_BAD_DESTINATION", "unrecognized log destination: %1",
+ "LOG_BAD_SEVERITY", "unrecognized log severity: %1",
+ "LOG_BAD_STREAM", "bad log console output stream: %1",
+ "LOG_DUPLICATE_MESSAGE_ID", "duplicate message ID (%1) in compiled code",
+ "LOG_DUPLICATE_NAMESPACE", "line %1: duplicate $NAMESPACE directive found",
+ "LOG_INPUT_OPEN_FAIL", "unable to open message file %1 for input: %2",
+ "LOG_INVALID_MESSAGE_ID", "line %1: invalid message identification '%2'",
+ "LOG_NAMESPACE_EXTRA_ARGS", "line %1: $NAMESPACE directive has too many arguments",
+ "LOG_NAMESPACE_INVALID_ARG", "line %1: $NAMESPACE directive has an invalid argument ('%2')",
+ "LOG_NAMESPACE_NO_ARGS", "line %1: no arguments were given to the $NAMESPACE directive",
+ "LOG_NO_MESSAGE_ID", "line %1: message definition line found without a message ID",
+ "LOG_NO_MESSAGE_TEXT", "line %1: line found containing a message ID ('%2') and no text",
+ "LOG_NO_SUCH_MESSAGE", "could not replace message text for '%1': no such message",
+ "LOG_OPEN_OUTPUT_FAIL", "unable to open %1 for output: %2",
+ "LOG_PREFIX_EXTRA_ARGS", "line %1: $PREFIX directive has too many arguments",
+ "LOG_PREFIX_INVALID_ARG", "line %1: $PREFIX directive has an invalid argument ('%2')",
+ "LOG_READING_LOCAL_FILE", "reading local message file %1",
+ "LOG_READ_ERROR", "error reading from message file %1: %2",
+ "LOG_UNRECOGNISED_DIRECTIVE", "line %1: unrecognised directive '%2'",
+ "LOG_WRITE_ERROR", "error writing to %1: %2",
+ NULL
+};
+
+const isc::log::MessageInitializer initializer(values);
+
+} // Anonymous namespace
+
diff --git a/src/lib/log/log_messages.h b/src/lib/log/log_messages.h
new file mode 100644
index 0000000..10e1501
--- /dev/null
+++ b/src/lib/log/log_messages.h
@@ -0,0 +1,35 @@
+// File created from log_messages.mes on Thu Jul 7 15:32:06 2011
+
+#ifndef __LOG_MESSAGES_H
+#define __LOG_MESSAGES_H
+
+#include <log/message_types.h>
+
+namespace isc {
+namespace log {
+
+extern const isc::log::MessageID LOG_BAD_DESTINATION;
+extern const isc::log::MessageID LOG_BAD_SEVERITY;
+extern const isc::log::MessageID LOG_BAD_STREAM;
+extern const isc::log::MessageID LOG_DUPLICATE_MESSAGE_ID;
+extern const isc::log::MessageID LOG_DUPLICATE_NAMESPACE;
+extern const isc::log::MessageID LOG_INPUT_OPEN_FAIL;
+extern const isc::log::MessageID LOG_INVALID_MESSAGE_ID;
+extern const isc::log::MessageID LOG_NAMESPACE_EXTRA_ARGS;
+extern const isc::log::MessageID LOG_NAMESPACE_INVALID_ARG;
+extern const isc::log::MessageID LOG_NAMESPACE_NO_ARGS;
+extern const isc::log::MessageID LOG_NO_MESSAGE_ID;
+extern const isc::log::MessageID LOG_NO_MESSAGE_TEXT;
+extern const isc::log::MessageID LOG_NO_SUCH_MESSAGE;
+extern const isc::log::MessageID LOG_OPEN_OUTPUT_FAIL;
+extern const isc::log::MessageID LOG_PREFIX_EXTRA_ARGS;
+extern const isc::log::MessageID LOG_PREFIX_INVALID_ARG;
+extern const isc::log::MessageID LOG_READING_LOCAL_FILE;
+extern const isc::log::MessageID LOG_READ_ERROR;
+extern const isc::log::MessageID LOG_UNRECOGNISED_DIRECTIVE;
+extern const isc::log::MessageID LOG_WRITE_ERROR;
+
+} // namespace log
+} // namespace isc
+
+#endif // __LOG_MESSAGES_H
diff --git a/src/lib/log/log_messages.mes b/src/lib/log/log_messages.mes
new file mode 100644
index 0000000..f150f39
--- /dev/null
+++ b/src/lib/log/log_messages.mes
@@ -0,0 +1,146 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# \brief Message Utility Message File
+#
+# This is the source of the set of messages generated by the message and
+# logging components. The associated .h and .cc files are created by hand from
+# this file though and are not built during the build process; this is to avoid
+# the chicken-and-egg situation where we need the files to build the message
+# compiler, yet we need the compiler to build the files.
+
+$NAMESPACE isc::log
+
+% LOG_BAD_DESTINATION unrecognized log destination: %1
+A logger destination value was given that was not recognized. The
+destination should be one of "console", "file", or "syslog".
+
+% LOG_BAD_SEVERITY unrecognized log severity: %1
+A logger severity value was given that was not recognized. The severity
+should be one of "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or "NONE".
+
+% LOG_BAD_STREAM bad log console output stream: %1
+Logging has been configured so that output is written to the terminal
+(console) but the stream on which it is to be written is not recognised.
+Allowed values are "stdout" and "stderr".
+
+% LOG_DUPLICATE_MESSAGE_ID duplicate message ID (%1) in compiled code
+During start-up, BIND 10 detected that the given message identification
+had been defined multiple times in the BIND 10 code. This indicates a
+programming error; please submit a bug report.
+
+% LOG_DUPLICATE_NAMESPACE line %1: duplicate $NAMESPACE directive found
+When reading a message file, more than one $NAMESPACE directive was found.
+(This directive is used to set a C++ namespace when generating header
+files during software development.) Such a condition is regarded as an
+error and the read will be abandoned.
+
+% LOG_INPUT_OPEN_FAIL unable to open message file %1 for input: %2
+The program was not able to open the specified input message file for
+the reason given.
+
+% LOG_INVALID_MESSAGE_ID line %1: invalid message identification '%2'
+An invalid message identification (ID) has been found during the read of
+a message file. Message IDs should comprise only alphanumeric characters
+and the underscore, and should not start with a digit.
+
+% LOG_NAMESPACE_EXTRA_ARGS line %1: $NAMESPACE directive has too many arguments
+The $NAMESPACE directive in a message file takes a single argument, a
+namespace in which all the generated symbol names are placed. This error
+is generated when the compiler finds a $NAMESPACE directive with more
+than one argument.
+
+% LOG_NAMESPACE_INVALID_ARG line %1: $NAMESPACE directive has an invalid argument ('%2')
+The $NAMESPACE argument in a message file should be a valid C++ namespace.
+This message is output if the simple check on the syntax of the string
+carried out by the reader fails.
+
+% LOG_NAMESPACE_NO_ARGS line %1: no arguments were given to the $NAMESPACE directive
+The $NAMESPACE directive in a message file takes a single argument,
+a C++ namespace in which all the generated symbol names are placed.
+This error is generated when the compiler finds a $NAMESPACE directive
+with no arguments.
+
+% LOG_NO_MESSAGE_ID line %1: message definition line found without a message ID
+Within a message file, message are defined by lines starting with a "%".
+The rest of the line should comprise the message ID and text describing
+the message. This error indicates the message compiler found a line in
+the message file comprising just the "%" and nothing else.
+
+% LOG_NO_MESSAGE_TEXT line %1: line found containing a message ID ('%2') and no text
+Within a message file, message are defined by lines starting with a "%".
+The rest of the line should comprise the message ID and text describing
+the message. This error indicates the message compiler found a line
+in the message file comprising just the "%" and message identification,
+but no text.
+
+% LOG_NO_SUCH_MESSAGE could not replace message text for '%1': no such message
+During start-up a local message file was read. A line with the listed
+message identification was found in the file, but the identification is
+not one contained in the compiled-in message dictionary. This message
+may appear a number of times in the file, once for every such unknown
+message identification.
+
+There may be several reasons why this message may appear:
+
+- The message ID has been mis-spelled in the local message file.
+
+- The program outputting the message may not use that particular message
+(e.g. it originates in a module not used by the program.)
+
+- The local file was written for an earlier version of the BIND 10 software
+and the later version no longer generates that message.
+
+Whatever the reason, there is no impact on the operation of BIND 10.
+
+% LOG_OPEN_OUTPUT_FAIL unable to open %1 for output: %2
+Originating within the logging code, the program was not able to open
+the specified output file for the reason given.
+
+% LOG_PREFIX_EXTRA_ARGS line %1: $PREFIX directive has too many arguments
+Within a message file, the $PREFIX directive takes a single argument,
+a prefix to be added to the symbol names when a C++ file is created.
+This error is generated when the compiler finds a $PREFIX directive with
+more than one argument.
+
+Note: the $PREFIX directive is deprecated and will be removed in a future
+version of BIND 10.
+
+% LOG_PREFIX_INVALID_ARG line %1: $PREFIX directive has an invalid argument ('%2')
+Within a message file, the $PREFIX directive takes a single argument,
+a prefix to be added to the symbol names when a C++ file is created.
+As such, it must adhere to restrictions on C++ symbol names (e.g. may
+only contain alphanumeric characters or underscores, and may nor start
+with a digit). A $PREFIX directive was found with an argument (given
+in the message) that violates those restrictions.
+
+Note: the $PREFIX directive is deprecated and will be removed in a future
+version of BIND 10.
+
+% LOG_READING_LOCAL_FILE reading local message file %1
+This is an informational message output by BIND 10 when it starts to read
+a local message file. (A local message file may replace the text of
+one of more messages; the ID of the message will not be changed though.)
+
+% LOG_READ_ERROR error reading from message file %1: %2
+The specified error was encountered reading from the named message file.
+
+% LOG_UNRECOGNISED_DIRECTIVE line %1: unrecognised directive '%2'
+Within a message file, a line starting with a dollar symbol was found
+(indicating the presence of a directive) but the first word on the line
+(shown in the message) was not recognised.
+
+% LOG_WRITE_ERROR error writing to %1: %2
+The specified error was encountered by the message compiler when writing
+to the named output file.
diff --git a/src/lib/log/logger.cc b/src/lib/log/logger.cc
index a2465de..d10e979 100644
--- a/src/lib/log/logger.cc
+++ b/src/lib/log/logger.cc
@@ -17,20 +17,26 @@
#include <log/logger.h>
#include <log/logger_impl.h>
+#include <log/logger_name.h>
+#include <log/logger_support.h>
#include <log/message_dictionary.h>
#include <log/message_types.h>
-#include <log/root_logger_name.h>
-#include <log/strutil.h>
+
+#include <util/strutil.h>
using namespace std;
namespace isc {
namespace log {
-// Initialize Logger implementation. Does not check whether the implementation
-// has already been initialized - that was done by the caller (getLoggerPtr()).
+// Initialize underlying logger, but only if logging has been initialized.
void Logger::initLoggerImpl() {
- loggerptr_ = new LoggerImpl(name_, infunc_);
+ if (isLoggingInitialized()) {
+ loggerptr_ = new LoggerImpl(name_);
+ } else {
+ isc_throw(LoggingNotInitialized, "attempt to access logging function "
+ "before logging has been initialized");
+ }
}
// Destructor.
@@ -74,6 +80,14 @@ Logger::getDebugLevel() {
return (getLoggerPtr()->getDebugLevel());
}
+// Effective debug level (only relevant if messages of severity DEBUG are being
+// logged).
+
+int
+Logger::getEffectiveDebugLevel() {
+ return (getLoggerPtr()->getEffectiveDebugLevel());
+}
+
// Check on the current severity settings
bool
@@ -111,64 +125,65 @@ Logger::isFatalEnabled() {
// Output methods
void
-Logger::debug(int dbglevel, const isc::log::MessageID& ident, ...) {
+Logger::output(const Severity& severity, const std::string& message) {
+ getLoggerPtr()->outputRaw(severity, message);
+}
+
+Logger::Formatter
+Logger::debug(int dbglevel, const isc::log::MessageID& ident) {
if (isDebugEnabled(dbglevel)) {
- va_list ap;
- va_start(ap, ident);
- getLoggerPtr()->debug(ident, ap);
- va_end(ap);
+ return (Formatter(DEBUG, getLoggerPtr()->lookupMessage(ident),
+ this));
+ } else {
+ return (Formatter());
}
}
-void
-Logger::info(const isc::log::MessageID& ident, ...) {
+Logger::Formatter
+Logger::info(const isc::log::MessageID& ident) {
if (isInfoEnabled()) {
- va_list ap;
- va_start(ap, ident);
- getLoggerPtr()->info(ident, ap);
- va_end(ap);
+ return (Formatter(INFO, getLoggerPtr()->lookupMessage(ident),
+ this));
+ } else {
+ return (Formatter());
}
}
-void
-Logger::warn(const isc::log::MessageID& ident, ...) {
+Logger::Formatter
+Logger::warn(const isc::log::MessageID& ident) {
if (isWarnEnabled()) {
- va_list ap;
- va_start(ap, ident);
- getLoggerPtr()->warn(ident, ap);
- va_end(ap);
+ return (Formatter(WARN, getLoggerPtr()->lookupMessage(ident),
+ this));
+ } else {
+ return (Formatter());
}
}
-void
-Logger::error(const isc::log::MessageID& ident, ...) {
+Logger::Formatter
+Logger::error(const isc::log::MessageID& ident) {
if (isErrorEnabled()) {
- va_list ap;
- va_start(ap, ident);
- getLoggerPtr()->error(ident, ap);
- va_end(ap);
+ return (Formatter(ERROR, getLoggerPtr()->lookupMessage(ident),
+ this));
+ } else {
+ return (Formatter());
}
}
-void
-Logger::fatal(const isc::log::MessageID& ident, ...) {
+Logger::Formatter
+Logger::fatal(const isc::log::MessageID& ident) {
if (isFatalEnabled()) {
- va_list ap;
- va_start(ap, ident);
- getLoggerPtr()->fatal(ident, ap);
- va_end(ap);
+ return (Formatter(FATAL, getLoggerPtr()->lookupMessage(ident),
+ this));
+ } else {
+ return (Formatter());
}
}
-bool Logger::operator==(Logger& other) {
- return (*getLoggerPtr() == *other.getLoggerPtr());
-}
+// Comparison (testing only)
-// Protected methods (used for testing)
-
-void
-Logger::reset() {
- LoggerImpl::reset();
+bool
+Logger::operator==(Logger& other) {
+ return (*getLoggerPtr() == *other.getLoggerPtr());
}
} // namespace log
diff --git a/src/lib/log/logger.h b/src/lib/log/logger.h
index 691eb73..96168c0 100644
--- a/src/lib/log/logger.h
+++ b/src/lib/log/logger.h
@@ -18,33 +18,85 @@
#include <cstdlib>
#include <string>
-#include <log/debug_levels.h>
-#include <log/logger_levels.h>
+#include <exceptions/exceptions.h>
+#include <log/logger_level.h>
#include <log/message_types.h>
+#include <log/log_formatter.h>
namespace isc {
namespace log {
-/// \brief Logging API
-///
-/// This module forms the interface into the logging subsystem. Features of the
-/// system and its implementation are:
-///
-/// # Multiple logging objects can be created, each given a name; those with the
-/// same name share characteristics (like destination, level being logged
-/// etc.)
-/// # Messages can be logged at severity levels of FATAL, ERROR, WARN, INFO or
-/// DEBUG. The DEBUG level has further sub-levels numbered 0 (least
-/// informative) to 99 (most informative).
-/// # Each logger has a severity level set associated with it. When a message
-/// is logged, it is output only if it is logged at a level equal to the
-/// logger severity level or greater, e.g. if the logger's severity is WARN,
-/// only messages logged at WARN, ERROR or FATAL will be output.
-/// # Messages are identified by message identifiers, which are keys into a
-/// message dictionary.
+/// \page LoggingApi Logging API
+/// \section LoggingApiOverview Overview
+/// BIND 10 logging uses the concepts of the widely-used Java logging
+/// package log4j (http://logging.apache.log/log4j), albeit implemented
+/// in C++ using an open-source port. Features of the system are:
+///
+/// - Within the code objects - known as loggers - can be created and
+/// used to log messages. These loggers have names; those with the
+/// same name share characteristics (such as output destination).
+/// - Loggers have a hierarchical relationship, with each logger being
+/// the child of another logger, except for the top of the hierarchy, the
+/// root logger. If a logger does not log a message, it is passed to the
+/// parent logger.
+/// - Messages can be logged at severity levels of FATAL, ERROR, WARN, INFO
+/// or DEBUG. The DEBUG level has further sub-levels numbered 0 (least
+/// informative) to 99 (most informative).
+/// - Each logger has a severity level set associated with it. When a
+/// message is logged, it is output only if it is logged at a level equal
+/// to the logger severity level or greater, e.g. if the logger's severity
+/// is WARN, only messages logged at WARN, ERROR or FATAL will be output.
+///
+/// \section LoggingApiLoggerNames BIND 10 Logger Names
+/// Within BIND 10, the root logger root logger is given the name of the
+/// program (via the stand-alone function setRootLoggerName()). Other loggers
+/// are children of the root logger and are named "<program>.<sublogger>".
+/// This name appears in logging output, allowing users to identify both
+/// the BIND 10 program and the component within the program that generated
+/// the message.
+///
+/// When creating a logger, the abbreviated name "<sublogger>" can be used;
+/// the program name will be prepended to it when the logger is created.
+/// In this way, individual libraries can have their own loggers without
+/// worrying about the program in which they are used, but:
+/// - The origin of the message will be clearly identified.
+/// - The same component can have different options (e.g. logging severity)
+/// in different programs at the same time.
+///
+/// \section LoggingApiLoggingMessages Logging Messages
+/// Instead of embedding the text of messages within the code, each message
+/// is referred to using a symbolic name. The logging code uses this name as
+/// a key in a dictionary from which the message text is obtained. Such a
+/// system allows for the optional replacement of message text at run time.
+/// More details about the message disction (and the compiler used to create
+/// the symbol definitions) can be found in other modules in the src/lib/log
+/// directory.
class LoggerImpl; // Forward declaration of the implementation class
+/// \brief Logging Not Initialized
+///
+/// Exception thrown if an attempt is made to access a logging function
+/// if the logging system has not been initialized.
+class LoggingNotInitialized : public isc::Exception {
+public:
+ LoggingNotInitialized(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what)
+ {}
+};
+
+/// \brief Logger Class
+///
+/// This class is the main class used for logging. Use comprises:
+///
+/// 1. Constructing a logger by instantiating it with a specific name. (If the
+/// same logger is in multiple functions within a file, overhead can be
+/// minimised by declaring it as a file-wide static variable.)
+/// 2. Using the error(), info() etc. methods to log an error. (However, it is
+/// recommended to use the LOG_ERROR, LOG_INFO etc. macros defined in macros.h.
+/// These will avoid the potentially-expensive evaluation of arguments if the
+/// severity is such that the message will be suppressed.)
+
class Logger {
public:
@@ -55,45 +107,20 @@ public:
/// \param name Name of the logger. If the name is that of the root name,
/// this creates an instance of the root logger; otherwise it creates a
/// child of the root logger.
- ///
- /// \param infunc This argument is present to get round a bug in some
- /// implementations of the logging system. If the logger is declared in
- /// a function (such that it will be deleted when the function exits,
- /// before the program ends), set this true. If declared outside a
- /// function (such that it gets deleted during program rundown), set false
- /// (the default).\n
- /// \n
- /// The problems encountered was that during program rundown, one logging
- /// implementation (log4cxx) threw a MutexException (this is described in
- /// https://issues.apache.org/jira/browse/LOGCXX-322). As this only occurs
- /// during program rundown, the issue is not serious - it just looks bad to
- /// have the program crash instead of shut down cleanly.\n
- /// \n
- /// If log4cxx is chosen as the implementation, this flag controls the
- /// deletion of the underlying log4cxx data structures when the logger is
- /// deleted. Setting it false for externally-declared loggers inhibits
- /// their deletion; so at program exit the memory is not reclaimed during
- /// program rundown, only when the process is delected. Setting it true
- /// for loggers that will be deleted in the normal running of the program
- /// enables their deletion - which causes no issues as the problem only
- /// manifests itself during program rundown.
- /// \n
- /// The flag has no effect on non-log4cxx implementations.
- Logger(const std::string& name, bool infunc = false) :
- loggerptr_(NULL), name_(name), infunc_(infunc)
+ Logger(const std::string& name) : loggerptr_(NULL), name_(name)
{}
-
/// \brief Destructor
virtual ~Logger();
+ /// \brief The formatter used to replace placeholders
+ typedef isc::log::Formatter<Logger> Formatter;
/// \brief Get Name of Logger
///
/// \return The full name of the logger (including the root name)
virtual std::string getName();
-
/// \brief Set Severity Level for Logger
///
/// Sets the level at which this logger will log messages. If none is set,
@@ -105,14 +132,12 @@ public:
/// outside these limits is silently coerced to the nearest boundary.
virtual void setSeverity(isc::log::Severity severity, int dbglevel = 1);
-
/// \brief Get Severity Level for Logger
///
/// \return The current logging level of this logger. In most cases though,
/// the effective logging level is what is required.
virtual isc::log::Severity getSeverity();
-
/// \brief Get Effective Severity Level for Logger
///
/// \return The effective severity level of the logger. This is the same
@@ -120,13 +145,18 @@ public:
/// is the severity of the parent.
virtual isc::log::Severity getEffectiveSeverity();
-
/// \brief Return DEBUG Level
///
/// \return Current setting of debug level. This is returned regardless of
/// whether the severity is set to debug.
virtual int getDebugLevel();
+ /// \brief Get Effective Debug Level for Logger
+ ///
+ /// \return The effective debug level of the logger. This is the same
+ /// as getDebugLevel() if the logger has a debug level set, but otherwise
+ /// is the debug level of the parent.
+ virtual int getEffectiveDebugLevel();
/// \brief Returns if Debug Message Should Be Output
///
@@ -135,76 +165,63 @@ public:
/// checked is less than or equal to the debug level set for the logger.
virtual bool isDebugEnabled(int dbglevel = MIN_DEBUG_LEVEL);
-
/// \brief Is INFO Enabled?
virtual bool isInfoEnabled();
-
/// \brief Is WARNING Enabled?
virtual bool isWarnEnabled();
-
/// \brief Is ERROR Enabled?
virtual bool isErrorEnabled();
-
/// \brief Is FATAL Enabled?
virtual bool isFatalEnabled();
-
/// \brief Output Debug Message
///
/// \param dbglevel Debug level, ranging between 0 and 99. Higher numbers
/// are used for more verbose output.
/// \param ident Message identification.
- /// \param ... Optional arguments for the message.
- void debug(int dbglevel, const MessageID& ident, ...);
-
+ Formatter debug(int dbglevel, const MessageID& ident);
/// \brief Output Informational Message
///
/// \param ident Message identification.
- /// \param ... Optional arguments for the message.
- void info(const MessageID& ident, ...);
-
+ Formatter info(const MessageID& ident);
/// \brief Output Warning Message
///
/// \param ident Message identification.
- /// \param ... Optional arguments for the message.
- void warn(const MessageID& ident, ...);
-
+ Formatter warn(const MessageID& ident);
/// \brief Output Error Message
///
/// \param ident Message identification.
- /// \param ... Optional arguments for the message.
- void error(const MessageID& ident, ...);
-
+ Formatter error(const MessageID& ident);
/// \brief Output Fatal Message
///
/// \param ident Message identification.
- /// \param ... Optional arguments for the message.
- void fatal(const MessageID& ident, ...);
+ Formatter fatal(const MessageID& ident);
/// \brief Equality
///
/// Check if two instances of this logger refer to the same stream.
- /// (This method is principally for testing.)
///
/// \return true if the logger objects are instances of the same logger.
bool operator==(Logger& other);
-protected:
+private:
+ friend class isc::log::Formatter<Logger>;
- /// \brief Reset Global Data
+ /// \brief Raw output function
///
- /// Used for testing, this calls upon the underlying logger implementation
- /// to clear any global data.
- static void reset();
+ /// This is used by the formatter to output formatted output.
+ ///
+ /// \param severity Severity of the message being output.
+ /// \param message Text of the message to be output.
+ void output(const Severity& severity, const std::string& message);
-private:
/// \brief Copy Constructor
///
/// Disabled (marked private) as it makes no sense to copy the logger -
@@ -219,15 +236,14 @@ private:
/// \brief Initialize Implementation
///
- /// Returns the logger pointer. If not yet set, the underlying
- /// implementation class is initialized.\n
- /// \n
- /// The reason for this indirection is to avoid the "static initialization
- /// fiacso", whereby we cannot rely on the order of static initializations.
- /// The main problem is the root logger name - declared statically - which
- /// is referenced by various loggers. By deferring a reference to it until
- /// after the program starts executing - by which time the root name object
- /// will be initialized - we avoid this problem.
+ /// Returns the logger pointer. If not yet set, the implementation class is
+ /// initialized.
+ ///
+ /// The main reason for this is to allow loggers to be declared statically
+ /// before the underlying logging system is initialized. However, any
+ /// attempt to access a logging method on any logger before initialization -
+ /// regardless of whether is is statically or automatically declared - will
+ /// cause a "LoggingNotInitialized" exception to be thrown.
///
/// \return Returns pointer to implementation
LoggerImpl* getLoggerPtr() {
@@ -242,7 +258,6 @@ private:
LoggerImpl* loggerptr_; ///< Pointer to the underlying logger
std::string name_; ///< Copy of the logger name
- bool infunc_; ///< Copy of the infunc argument
};
} // namespace log
diff --git a/src/lib/log/logger_impl.cc b/src/lib/log/logger_impl.cc
index 4b19360..046da13 100644
--- a/src/lib/log/logger_impl.cc
+++ b/src/lib/log/logger_impl.cc
@@ -13,42 +13,43 @@
// PERFORMANCE OF THIS SOFTWARE
#include <iostream>
+#include <iomanip>
#include <algorithm>
#include <stdarg.h>
#include <stdio.h>
#include <boost/lexical_cast.hpp>
+#include <boost/static_assert.hpp>
+
+#include <log4cplus/configurator.h>
-#include <log/debug_levels.h>
-#include <log/root_logger_name.h>
#include <log/logger.h>
#include <log/logger_impl.h>
+#include <log/logger_level.h>
+#include <log/logger_level_impl.h>
+#include <log/logger_name.h>
#include <log/message_dictionary.h>
#include <log/message_types.h>
-#include <log/root_logger_name.h>
-#include <log/strutil.h>
+
+#include <util/strutil.h>
+
+// Note: as log4cplus and the BIND 10 logger have many concepts in common, and
+// thus many similar names, to disambiguate types we don't "use" the log4cplus
+// namespace: instead, all log4cplus types are explicitly qualified.
using namespace std;
namespace isc {
namespace log {
-// Static initializations
-
-LoggerImpl::LoggerInfoMap LoggerImpl::logger_info_;
-LoggerImpl::LoggerInfo LoggerImpl::root_logger_info_(isc::log::INFO, 0);
-
-// Constructor
-LoggerImpl::LoggerImpl(const std::string& name, bool)
+// Constructor. The setting of logger_ must be done when the variable is
+// constructed (instead of being left to the body of the function); at least
+// one compiler requires that all member variables be constructed before the
+// constructor is run, but log4cplus::Logger (the type of logger_) has no
+// default constructor.
+LoggerImpl::LoggerImpl(const string& name) : name_(expandLoggerName(name)),
+ logger_(log4cplus::Logger::getInstance(name_))
{
- // Are we the root logger?
- if (name == getRootLoggerName()) {
- is_root_ = true;
- name_ = name;
- } else {
- is_root_ = false;
- name_ = getRootLoggerName() + "." + name;
- }
}
// Destructor. (Here because of virtual declaration.)
@@ -57,164 +58,72 @@ LoggerImpl::~LoggerImpl() {
}
// Set the severity for logging.
-
void
LoggerImpl::setSeverity(isc::log::Severity severity, int dbglevel) {
-
- // Silently coerce the debug level into the valid range of 0 to 99
-
- int debug_level = max(MIN_DEBUG_LEVEL, min(MAX_DEBUG_LEVEL, dbglevel));
- if (is_root_) {
-
- // Can only set severity for the root logger, you can't disable it.
- // Any attempt to do so is silently ignored.
- if (severity != isc::log::DEFAULT) {
- root_logger_info_ = LoggerInfo(severity, debug_level);
- }
-
- } else if (severity == isc::log::DEFAULT) {
-
- // Want to set to default; this means removing the information
- // about this logger from the logger_info_ if it is set.
- LoggerInfoMap::iterator i = logger_info_.find(name_);
- if (i != logger_info_.end()) {
- logger_info_.erase(i);
- }
-
- } else {
-
- // Want to set this information
- logger_info_[name_] = LoggerInfo(severity, debug_level);
- }
+ Level level(severity, dbglevel);
+ logger_.setLogLevel(LoggerLevelImpl::convertFromBindLevel(level));
}
// Return severity level
-
isc::log::Severity
LoggerImpl::getSeverity() {
+ Level level = LoggerLevelImpl::convertToBindLevel(logger_.getLogLevel());
+ return level.severity;
+}
- if (is_root_) {
- return (root_logger_info_.severity);
- }
- else {
- LoggerInfoMap::iterator i = logger_info_.find(name_);
- if (i != logger_info_.end()) {
- return ((i->second).severity);
- }
- else {
- return (isc::log::DEFAULT);
- }
- }
+// Return current debug level (only valid if current severity level is DEBUG).
+int
+LoggerImpl::getDebugLevel() {
+ Level level = LoggerLevelImpl::convertToBindLevel(logger_.getLogLevel());
+ return level.dbglevel;
}
// Get effective severity. Either the current severity or, if not set, the
// severity of the root level.
-
isc::log::Severity
LoggerImpl::getEffectiveSeverity() {
-
- if (!is_root_ && !logger_info_.empty()) {
-
- // Not root logger and there is at least one item in the info map for a
- // logger.
- LoggerInfoMap::iterator i = logger_info_.find(name_);
- if (i != logger_info_.end()) {
-
- // Found, so return the severity.
- return ((i->second).severity);
- }
- }
-
- // Must be the root logger, or this logger is defaulting to the root logger
- // settings.
- return (root_logger_info_.severity);
+ Level level = LoggerLevelImpl::convertToBindLevel(logger_.getChainedLogLevel());
+ return level.severity;
}
-// Get the debug level. This returns 0 unless the severity is DEBUG.
-
+// Return effective debug level (only valid if current effective severity level
+// is DEBUG).
int
-LoggerImpl::getDebugLevel() {
-
- if (!is_root_ && !logger_info_.empty()) {
-
- // Not root logger and there is something in the map, check if there
- // is a setting for this one.
- LoggerInfoMap::iterator i = logger_info_.find(name_);
- if (i != logger_info_.end()) {
-
- // Found, so return the debug level.
- if ((i->second).severity == isc::log::DEBUG) {
- return ((i->second).dbglevel);
- } else {
- return (0);
- }
- }
- }
-
- // Must be the root logger, or this logger is defaulting to the root logger
- // settings.
- if (root_logger_info_.severity == isc::log::DEBUG) {
- return (root_logger_info_.dbglevel);
- } else {
- return (0);
- }
+LoggerImpl::getEffectiveDebugLevel() {
+ Level level = LoggerLevelImpl::convertToBindLevel(logger_.getChainedLogLevel());
+ return level.dbglevel;
}
-// The code for isXxxEnabled is quite simple and is in the header. The only
-// exception is isDebugEnabled() where we have the complication of the debug
-// levels.
-
-bool
-LoggerImpl::isDebugEnabled(int dbglevel) {
-
- if (!is_root_ && !logger_info_.empty()) {
-
- // Not root logger and there is something in the map, check if there
- // is a setting for this one.
- LoggerInfoMap::iterator i = logger_info_.find(name_);
- if (i != logger_info_.end()) {
-
- // Found, so return the debug level.
- if ((i->second).severity <= isc::log::DEBUG) {
- return ((i->second).dbglevel >= dbglevel);
- } else {
- return (false); // Nothing lower than debug
- }
- }
- }
-
- // Must be the root logger, or this logger is defaulting to the root logger
- // settings.
- if (root_logger_info_.severity <= isc::log::DEBUG) {
- return (root_logger_info_.dbglevel >= dbglevel);
- } else {
- return (false);
- }
-}
// Output a general message
+string*
+LoggerImpl::lookupMessage(const MessageID& ident) {
+ return (new string(string(ident) + " " +
+ MessageDictionary::globalDictionary().getText(ident)));
+}
void
-LoggerImpl::output(const char* sev_text, const MessageID& ident,
- va_list ap)
-{
- char message[512]; // Should be large enough for any message
-
- // Obtain text of the message and substitute arguments.
- const string format = MessageDictionary::globalDictionary().getText(ident);
- vsnprintf(message, sizeof(message), format.c_str(), ap);
-
- // Get the time in a struct tm format, and convert to text
- time_t t_time;
- time(&t_time);
- struct tm* tm_time = localtime(&t_time);
-
- char chr_time[32];
- (void) strftime(chr_time, sizeof(chr_time), "%Y-%m-%d %H:%M:%S", tm_time);
-
- // Now output.
- std::cout << chr_time << " " << sev_text << " [" << getName() << "] " <<
- ident << ", " << message << "\n";
+LoggerImpl::outputRaw(const Severity& severity, const string& message) {
+ switch (severity) {
+ case DEBUG:
+ LOG4CPLUS_DEBUG(logger_, message);
+ break;
+
+ case INFO:
+ LOG4CPLUS_INFO(logger_, message);
+ break;
+
+ case WARN:
+ LOG4CPLUS_WARN(logger_, message);
+ break;
+
+ case ERROR:
+ LOG4CPLUS_ERROR(logger_, message);
+ break;
+
+ case FATAL:
+ LOG4CPLUS_FATAL(logger_, message);
+ }
}
} // namespace log
diff --git a/src/lib/log/logger_impl.h b/src/lib/log/logger_impl.h
index 9fc9cf9..90bd41a 100644
--- a/src/lib/log/logger_impl.h
+++ b/src/lib/log/logger_impl.h
@@ -18,15 +18,19 @@
#include <stdarg.h>
#include <time.h>
+#include <iostream>
#include <cstdlib>
#include <string>
#include <map>
#include <utility>
-#include <log/debug_levels.h>
-#include <log/logger.h>
+
+// log4cplus logger header file
+#include <log4cplus/logger.h>
+
+// BIND-10 logger files
+#include <log/logger_level_impl.h>
#include <log/message_types.h>
-#include <log/root_logger_name.h>
namespace isc {
namespace log {
@@ -35,46 +39,36 @@ namespace log {
///
/// The logger uses a "pimpl" idiom for implementation, where the base logger
/// class contains little more than a pointer to the implementation class, and
-/// all actions are carried out by the latter. This class is an implementation
-/// class that just outputs to stdout.
+/// all actions are carried out by the latter.
+///
+/// This particular implementation is based on log4cplus (from sourceforge:
+/// http://log4cplus.sourceforge.net). Particular items of note:
+///
+/// a) BIND 10 loggers have names of the form "program.sublogger". In other
+/// words, each of the loggers is a sub-logger of the main program logger.
+/// In log4cplus, there is a root logger (called "root" according to the
+/// documentation, but actually unnamed) and all loggers created are subloggers
+/// if it.
+///
+/// In this implementation, the log4cplus root logger is unused. Instead, the
+/// BIND 10 root logger is created as a child of the log4cplus root logger,
+/// and all other loggers used in the program are created as sub-loggers of
+/// that. In this way, the logging system can just include the name of the
+/// logger in each message without the need to specially consider if the
+/// message is the root logger or not.
+///
+/// b) The idea of debug levels is implemented. See logger_level.h and
+/// logger_level_impl.h for more details on this.
class LoggerImpl {
public:
- /// \brief Information About Logger
- ///
- /// Holds a information about a logger, namely its severity and its debug
- /// level. This could be a std::pair, except that it gets confusing when
- /// accessing the LoggerInfoMap: that returns a pair, so we to reference
- /// elements we would use constructs like ((i->first).second);
- struct LoggerInfo {
- isc::log::Severity severity;
- int dbglevel;
-
- LoggerInfo(isc::log::Severity sev = isc::log::INFO,
- int dbg = MIN_DEBUG_LEVEL) : severity(sev), dbglevel(dbg)
- {}
- };
-
-
- /// \brief Information About All Loggers
- ///
- /// Information about all loggers in the system - except the root logger -
- /// is held in a map, linking name of the logger (excluding the root
- /// name component) and its set severity and debug levels. The root
- /// logger information is held separately.
- typedef std::map<std::string, LoggerInfo> LoggerInfoMap;
-
-
/// \brief Constructor
///
/// Creates a logger of the specific name.
///
/// \param name Name of the logger.
- ///
- /// \param exit_delete This argument is present to get round a bug in
- /// the log4cxx implementation. It is unused here.
- LoggerImpl(const std::string& name, bool);
+ LoggerImpl(const std::string& name);
/// \brief Destructor
@@ -94,16 +88,16 @@ public:
///
/// \param severity Severity level to log
/// \param dbglevel If the severity is DEBUG, this is the debug level.
- /// This can be in the range 1 to 100 and controls the verbosity. A value
+ /// This can be in the range 0 to 99 and controls the verbosity. A value
/// outside these limits is silently coerced to the nearest boundary.
- virtual void setSeverity(isc::log::Severity severity, int dbglevel = 1);
+ virtual void setSeverity(Severity severity, int dbglevel = 1);
/// \brief Get Severity Level for Logger
///
/// \return The current logging level of this logger. In most cases though,
/// the effective logging level is what is required.
- virtual isc::log::Severity getSeverity();
+ virtual Severity getSeverity();
/// \brief Get Effective Severity Level for Logger
@@ -111,120 +105,67 @@ public:
/// \return The effective severity level of the logger. This is the same
/// as getSeverity() if the logger has a severity level set, but otherwise
/// is the severity of the parent.
- virtual isc::log::Severity getEffectiveSeverity();
+ virtual Severity getEffectiveSeverity();
- /// \brief Return DEBUG Level
+ /// \brief Return debug level
///
- /// \return Current setting of debug level. This is returned regardless of
- /// whether the
+ /// \return Current setting of debug level. This will be zero if the
+ /// the current severity level is not DEBUG.
virtual int getDebugLevel();
+ /// \brief Return effective debug level
+ ///
+ /// \return Current setting of effective debug level. This will be zero if
+ /// the current effective severity level is not DEBUG.
+ virtual int getEffectiveDebugLevel();
+
+
/// \brief Returns if Debug Message Should Be Output
///
/// \param dbglevel Level for which debugging is checked. Debugging is
/// enabled only if the logger has DEBUG enabled and if the dbglevel
/// checked is less than or equal to the debug level set for the logger.
- virtual bool
- isDebugEnabled(int dbglevel = MIN_DEBUG_LEVEL);
+ virtual bool isDebugEnabled(int dbglevel = MIN_DEBUG_LEVEL) {
+ Level level(DEBUG, dbglevel);
+ return logger_.isEnabledFor(LoggerLevelImpl::convertFromBindLevel(level));
+ }
/// \brief Is INFO Enabled?
virtual bool isInfoEnabled() {
- return (isEnabled(isc::log::INFO));
+ return (logger_.isEnabledFor(log4cplus::INFO_LOG_LEVEL));
}
/// \brief Is WARNING Enabled?
virtual bool isWarnEnabled() {
- return (isEnabled(isc::log::WARN));
+ return (logger_.isEnabledFor(log4cplus::WARN_LOG_LEVEL));
}
/// \brief Is ERROR Enabled?
virtual bool isErrorEnabled() {
- return (isEnabled(isc::log::ERROR));
+ return (logger_.isEnabledFor(log4cplus::ERROR_LOG_LEVEL));
}
/// \brief Is FATAL Enabled?
virtual bool isFatalEnabled() {
- return (isEnabled(isc::log::FATAL));
+ return (logger_.isEnabledFor(log4cplus::FATAL_LOG_LEVEL));
}
-
- /// \brief Common Severity check
- ///
- /// Implements the common severity check. As an optimisation, this checks
- /// to see if any logger-specific levels have been set (a quick check as it
- /// just involves seeing if the collection of logger information is empty).
- /// if not, it returns the information for the root level; if so, it has
- /// to take longer and look up the information in the map holding the
- /// logging details.
- virtual bool isEnabled(isc::log::Severity severity) {
- if (logger_info_.empty()) {
- return (root_logger_info_.severity <= severity);
- }
- else {
- return (getSeverity() <= severity);
- }
- }
-
-
- /// \brief Output General Message
+ /// \brief Raw output
///
- /// The message is formatted to include the date and time, the severity
- /// and the logger generating the message.
+ /// Writes the message with time into the log. Used by the Formatter
+ /// to produce output.
///
- /// \param sev_text Severity level as a text string
- /// \param ident Message identification
- /// \param ap Variable argument list holding message arguments
- void output(const char* sev_text, const MessageID& ident,
- va_list ap);
+ /// \param severity Severity of the message. (This controls the prefix
+ /// label output with the message text.)
+ /// \param message Text of the message.
+ void outputRaw(const Severity& severity, const std::string& message);
-
- /// \brief Output Debug Message
- ///
- /// \param ident Message identification.
- /// \param text Text to log
- /// \param ap Variable argument list holding message arguments
- void debug(const MessageID& ident, va_list ap) {
- output("DEBUG", ident, ap);
- }
-
-
- /// \brief Output Informational Message
- ///
- /// \param ident Message identification.
- /// \param text Text to log
- /// \param ap Variable argument list holding message arguments
- void info(const MessageID& ident, va_list ap) {
- output("INFO ", ident, ap);
- }
-
- /// \brief Output Warning Message
- ///
- /// \param ident Message identification.
- /// \param text Text to log
- /// \param ap Variable argument list holding message arguments
- void warn(const MessageID& ident, va_list ap) {
- output("WARN ", ident, ap);
- }
-
- /// \brief Output Error Message
+ /// \brief Look up message text in dictionary
///
- /// \param ident Message identification.
- /// \param text Text to log
- /// \param ap Variable argument list holding message arguments
- void error(const MessageID& ident, va_list ap) {
- output("ERROR", ident, ap);
- }
-
- /// \brief Output Fatal Message
- ///
- /// \param ident Message identification.
- /// \param text Text to log
- /// \param ap Variable argument list holding message arguments
- void fatal(const MessageID& ident, va_list ap) {
- output("FATAL", ident, ap);
- }
+ /// This gets you the unformatted text of message for given ID.
+ std::string* lookupMessage(const MessageID& id);
/// \brief Equality
///
@@ -236,28 +177,9 @@ public:
return (name_ == other.name_);
}
-
- /// \brief Reset Global Data
- ///
- /// Only used for testing, this clears all the logger information and
- /// resets it back to default values.
- static void reset() {
- root_logger_info_ = LoggerInfo(isc::log::INFO, MIN_DEBUG_LEVEL);
- logger_info_.clear();
- }
-
-
private:
- bool is_root_; ///< true if a root logger
- std::string name_; ///< Name of this logger
-
- // Split the status of the root logger from this logger. If - is will
- // probably be the usual case - no per-logger setting is enabled, a
- // quick check of logger_info_.empty() will return true and we can quickly
- // return the root logger status without a length lookup in the map.
-
- static LoggerInfo root_logger_info_; ///< Status of root logger
- static LoggerInfoMap logger_info_; ///< Store of debug levels etc.
+ std::string name_; ///< Full name of this logger
+ log4cplus::Logger logger_; ///< Underlying log4cplus logger
};
} // namespace log
diff --git a/src/lib/log/logger_impl_log4cxx.cc b/src/lib/log/logger_impl_log4cxx.cc
deleted file mode 100644
index 404fd03..0000000
--- a/src/lib/log/logger_impl_log4cxx.cc
+++ /dev/null
@@ -1,241 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE
-
-#include <iostream>
-
-#include <stdarg.h>
-#include <stdio.h>
-
-#include <log4cxx/appender.h>
-#include <log4cxx/basicconfigurator.h>
-#include <log4cxx/patternlayout.h>
-#include <log4cxx/consoleappender.h>
-
-#include <log/root_logger_name.h>
-#include <log/logger.h>
-#include <log/logger_impl.h>
-#include <log/message_dictionary.h>
-#include <log/message_types.h>
-#include <log/strutil.h>
-#include <log/xdebuglevel.h>
-
-using namespace std;
-
-namespace isc {
-namespace log {
-
-// Static initializations
-
-bool LoggerImpl::init_ = false;
-
-// Destructor. Delete log4cxx stuff if "don't delete" is clear.
-
-LoggerImpl::~LoggerImpl() {
- if (exit_delete_) {
- delete loggerptr_;
- }
-}
-
-// Initialize logger - create a logger as a child of the root logger. With
-// log4cxx this is assured by naming the logger <parent>.<child>.
-
-void
-LoggerImpl::initLogger() {
-
- // Initialize basic logging if not already done. This is a one-off for
- // all loggers.
- if (!init_) {
-
- // TEMPORARY
- // Add a suitable console logger to the log4cxx root logger. (This
- // is the logger at the root of the log4cxx tree, not the BIND-10 root
- // logger, which is one level down.) The chosen format is:
- //
- // YYYY-MM-DD hh:mm:ss.sss [logger] SEVERITY: text
- //
- // As noted, this is a temporary hack: it is done here to ensure that
- // a suitable output and output pattern is set. Future versions of the
- // software will set this based on configuration data.
-
- log4cxx::LayoutPtr layout(
- new log4cxx::PatternLayout(
- "%d{yyyy-MM-DD HH:mm:ss.SSS} %-5p [%c] %m\n"));
- log4cxx::AppenderPtr console(
- new log4cxx::ConsoleAppender(layout));
- log4cxx::LoggerPtr sys_root_logger = log4cxx::Logger::getRootLogger();
- sys_root_logger->addAppender(console);
-
- // Set the default logging to INFO
- sys_root_logger->setLevel(log4cxx::Level::getInfo());
-
- // All static stuff initialized
- init_ = true;
- }
-
- // Initialize this logger. Name this as to whether the BIND-10 root logger
- // name has been set. (If not, this mucks up the hierarchy :-( ).
- string root_name = RootLoggerName::getName();
- if (root_name.empty() || (name_ == root_name)) {
- loggerptr_ = new log4cxx::LoggerPtr(log4cxx::Logger::getLogger(name_));
- }
- else {
- loggerptr_ = new log4cxx::LoggerPtr(
- log4cxx::Logger::getLogger(root_name + "." + name_)
- );
- }
-}
-
-
-// Set the severity for logging. There is a 1:1 mapping between the logging
-// severity and the log4cxx logging levels, apart from DEBUG.
-//
-// In log4cxx, each of the logging levels (DEBUG, INFO, WARN etc.) has a numeric
-// value. The level is set to one of these and any numeric level equal to or
-// above it that is reported. For example INFO has a value of 20000 and ERROR
-// a value of 40000. So if a message of WARN severity (= 30000) is logged, it is
-// not logged when the logger's severity level is ERROR (as 30000 !>= 40000).
-// It is reported if the logger's severity level is set to WARN (as 30000 >=
-/// 30000) or INFO (30000 >= 20000).
-//
-// This gives a simple system for handling different debug levels. The debug
-// level is a number between 0 and 99, with 0 being least verbose and 99 the
-// most. To implement this seamlessly, when DEBUG is set, the numeric value
-// of the logging level is actually set to (DEBUG - debug-level). Similarly
-// messages of level "n" are logged at a logging level of (DEBUG - n). Thus if
-// the logging level is set to DEBUG and the debug level set to 25, the actual
-// level set is 10000 - 25 = 99975.
-//
-// Attempting to log a debug message of level 26 is an attempt to log a message
-// of level 10000 - 26 = 9974. As 9974 !>= 9975, it is not logged. A
-// message of level 25 is, because 9975 >= 9975.
-//
-// The extended set of logging levels is implemented by the XDebugLevel class.
-
-void
-LoggerImpl::setSeverity(isc::log::Severity severity, int dbglevel) {
- switch (severity) {
- case NONE:
- getLogger()->setLevel(log4cxx::Level::getOff());
- break;
-
- case FATAL:
- getLogger()->setLevel(log4cxx::Level::getFatal());
- break;
-
- case ERROR:
- getLogger()->setLevel(log4cxx::Level::getError());
- break;
-
- case WARN:
- getLogger()->setLevel(log4cxx::Level::getWarn());
- break;
-
- case INFO:
- getLogger()->setLevel(log4cxx::Level::getInfo());
- break;
-
- case DEBUG:
- getLogger()->setLevel(
- log4cxx::XDebugLevel::getExtendedDebug(dbglevel));
- break;
-
- // Will get here for DEFAULT or any other value. This disables the
- // logger's own severity and it defaults to the severity of the parent
- // logger.
- default:
- getLogger()->setLevel(0);
- }
-}
-
-// Convert between numeric log4cxx logging level and BIND-10 logging severity.
-
-isc::log::Severity
-LoggerImpl::convertLevel(int value) {
-
- // The order is optimised. This is only likely to be called when testing
- // for writing debug messages, so the check for DEBUG_INT is first.
- if (value <= log4cxx::Level::DEBUG_INT) {
- return (DEBUG);
- } else if (value <= log4cxx::Level::INFO_INT) {
- return (INFO);
- } else if (value <= log4cxx::Level::WARN_INT) {
- return (WARN);
- } else if (value <= log4cxx::Level::ERROR_INT) {
- return (ERROR);
- } else if (value <= log4cxx::Level::FATAL_INT) {
- return (FATAL);
- } else {
- return (NONE);
- }
-}
-
-
-// Return the logging severity associated with this logger.
-
-isc::log::Severity
-LoggerImpl::getSeverityCommon(const log4cxx::LoggerPtr& ptrlogger,
- bool check_parent) {
-
- log4cxx::LevelPtr level = ptrlogger->getLevel();
- if (level == log4cxx::LevelPtr()) {
-
- // Null level returned, logging should be that of the parent.
-
- if (check_parent) {
- log4cxx::LoggerPtr parent = ptrlogger->getParent();
- if (parent == log4cxx::LoggerPtr()) {
-
- // No parent, so reached the end of the chain. Return INFO
- // severity.
- return (INFO);
- }
- else {
- return (getSeverityCommon(parent, check_parent));
- }
- }
- else {
- return (DEFAULT);
- }
- } else {
- return (convertLevel(level->toInt()));
- }
-}
-
-
-// Get the debug level. This returns 0 unless the severity is DEBUG.
-
-int
-LoggerImpl::getDebugLevel() {
-
- log4cxx::LevelPtr level = getLogger()->getLevel();
- if (level == log4cxx::LevelPtr()) {
-
- // Null pointer returned, logging should be that of the parent.
- return (0);
-
- } else {
- int severity = level->toInt();
- if (severity <= log4cxx::Level::DEBUG_INT) {
- return (log4cxx::Level::DEBUG_INT - severity);
- }
- else {
- return (0);
- }
- }
-}
-
-
-
-} // namespace log
-} // namespace isc
diff --git a/src/lib/log/logger_impl_log4cxx.h b/src/lib/log/logger_impl_log4cxx.h
deleted file mode 100644
index 3101347..0000000
--- a/src/lib/log/logger_impl_log4cxx.h
+++ /dev/null
@@ -1,315 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __LOGGER_IMPL_LOG4CXX_H
-#define __LOGGER_IMPL_LOG4CXX_H
-
-#include <cstdlib>
-#include <string>
-#include <boost/lexical_cast.hpp>
-#include <log4cxx/logger.h>
-#include <log4cxx/logger.h>
-
-#include <log/debug_levels.h>
-#include <log/logger.h>
-#include <log/message_types.h>
-
-namespace isc {
-namespace log {
-
-/// \brief Log4cxx Logger Implementation
-///
-/// The logger uses a "pimpl" idiom for implementation, where the base logger
-/// class contains little more than a pointer to the implementation class, and
-/// all actions are carried out by the latter. This class is an implementation
-/// class interfacing to the log4cxx logging system.
-
-class LoggerImpl {
-public:
-
- /// \brief Constructor
- ///
- /// Creates/attaches to a logger of a specific name.
- ///
- /// \param name Name of the logger. If the name is that of the root name,
- /// this creates an instance of the root logger; otherwise it creates a
- /// child of the root logger.
- ///
- /// \param exit_delete This argument is present to get round a bug in
- /// log4cxx. If a log4cxx logger is declared outside an execution unit, it
- /// is not deleted until the program runs down. At that point all such
- /// objects - including internal log4cxx objects - are deleted. However,
- /// there seems to be a bug in log4cxx where the way that such objects are
- /// destroyed causes a MutexException to be thrown (this is described in
- /// https://issues.apache.org/jira/browse/LOGCXX-322). As this only occurs
- /// during program rundown, the issue is not serious - it just looks bad to
- /// have the program crash instead of shut down cleanly.\n
- /// \n
- /// The original implementation of the isc::log::Logger had as a member a
- /// log4cxx logger (actually a LoggerPtr). If the isc:: Logger was declared
- /// statically, when it was destroyed at the end of the program the internal
- /// LoggerPtr was destroyed, which triggered the problem. The problem did
- /// not occur if the isc::log::Logger was created on the stack. To get
- /// round this, the internal LoggerPtr is now created dynamically. The
- /// exit_delete argument controls its destruction: if true, it is destroyed
- /// in the ISC Logger destructor. If false, it is not.\n
- /// \n
- /// When creating an isc::log::Logger on the stack, the argument should be
- /// false (the default); when the Logger is destroyed, all the internal
- /// log4cxx objects are destroyed. As only the logger (and not the internal
- /// log4cxx data structures are being destroyed), all is well. However,
- /// when creating the logger statically, the argument should be false. This
- /// means that the log4cxx objects are not destroyed at program rundown;
- /// instead memory is reclaimed and files are closed when the process is
- /// destroyed, something that does not trigger the bug.
- LoggerImpl(const std::string& name, bool exit_delete = false) :
- loggerptr_(NULL), name_(name), exit_delete_(exit_delete)
- {}
-
-
- /// \brief Destructor
- virtual ~LoggerImpl();
-
-
- /// \brief Get the full name of the logger (including the root name)
- virtual std::string getName() {
- return (getLogger()->getName());
- }
-
-
- /// \brief Set Severity Level for Logger
- ///
- /// Sets the level at which this logger will log messages. If none is set,
- /// the level is inherited from the parent.
- ///
- /// \param severity Severity level to log
- /// \param dbglevel If the severity is DEBUG, this is the debug level.
- /// This can be in the range 1 to 100 and controls the verbosity. A value
- /// outside these limits is silently coerced to the nearest boundary.
- virtual void setSeverity(isc::log::Severity severity, int dbglevel = 1);
-
-
- /// \brief Get Severity Level for Logger
- ///
- /// \return The current logging level of this logger. In most cases though,
- /// the effective logging level is what is required.
- virtual isc::log::Severity getSeverity() {
- return (getSeverityCommon(getLogger(), false));
- }
-
-
- /// \brief Get Effective Severity Level for Logger
- ///
- /// \return The effective severity level of the logger. This is the same
- /// as getSeverity() if the logger has a severity level set, but otherwise
- /// is the severity of the parent.
- virtual isc::log::Severity getEffectiveSeverity() {
- return (getSeverityCommon(getLogger(), true));
- }
-
-
- /// \brief Return DEBUG Level
- ///
- /// \return Current setting of debug level. This is returned regardless of
- /// whether the
- virtual int getDebugLevel();
-
-
- /// \brief Returns if Debug Message Should Be Output
- ///
- /// \param dbglevel Level for which debugging is checked. Debugging is
- /// enabled only if the logger has DEBUG enabled and if the dbglevel
- /// checked is less than or equal to the debug level set for the logger.
- virtual bool
- isDebugEnabled(int dbglevel = MIN_DEBUG_LEVEL) {
- return (getLogger()->getEffectiveLevel()->toInt() <=
- (log4cxx::Level::DEBUG_INT - dbglevel));
- }
-
-
- /// \brief Is INFO Enabled?
- virtual bool isInfoEnabled() {
- return (getLogger()->isInfoEnabled());
- }
-
-
- /// \brief Is WARNING Enabled?
- virtual bool isWarnEnabled() {
- return (getLogger()->isWarnEnabled());
- }
-
-
- /// \brief Is ERROR Enabled?
- virtual bool isErrorEnabled() {
- return (getLogger()->isErrorEnabled());
- }
-
-
- /// \brief Is FATAL Enabled?
- virtual bool isFatalEnabled() {
- return (getLogger()->isFatalEnabled());
- }
-
-
- /// \brief Output Debug Message
- ///
- /// \param ident Message identification.
- /// \param text Text to log
- void debug(const MessageID& ident, const char* text) {
- LOG4CXX_DEBUG(getLogger(), ident << ", " << text);
- }
-
-
- /// \brief Output Informational Message
- ///
- /// \param ident Message identification.
- /// \param text Text to log
- void info(const MessageID& ident, const char* text) {
- LOG4CXX_INFO(getLogger(), ident << ", " << text);
- }
-
-
- /// \brief Output Warning Message
- ///
- /// \param ident Message identification.
- /// \param text Text to log
- void warn(const MessageID& ident, const char* text) {
- LOG4CXX_WARN(getLogger(), ident << ", " << text);
- }
-
-
- /// \brief Output Error Message
- ///
- /// \param ident Message identification.
- /// \param text Text to log
- void error(const MessageID& ident, const char* text) {
- LOG4CXX_ERROR(getLogger(), ident << ", " << text);
- }
-
-
- /// \brief Output Fatal Message
- ///
- /// \param ident Message identification.
- /// \param text Text to log
- void fatal(const MessageID& ident, const char* text) {
- LOG4CXX_FATAL(getLogger(), ident << ", " << text);
- }
-
- //@{
- /// \brief Testing Methods
- ///
- /// The next set of methods are used in testing. As they are accessed from
- /// the main logger class, they must be public.
-
- /// \brief Equality
- ///
- /// Check if two instances of this logger refer to the same stream.
- /// (This method is principally for testing.)
- ///
- /// \return true if the logger objects are instances of the same logger.
- bool operator==(LoggerImpl& other) {
- return (*loggerptr_ == *other.loggerptr_);
- }
-
-
- /// \brief Logger Initialized
- ///
- /// Check that the logger has been properly initialized. (This method
- /// is principally for testing.)
- ///
- /// \return true if this logger object has been initialized.
- bool isInitialized() {
- return (loggerptr_ != NULL);
- }
-
- /// \brief Reset Global Data
- ///
- /// Only used for testing, this clears all the logger information and
- /// resets it back to default values. This is a no-op for log4cxx.
- static void reset() {
- }
-
- //@}
-
-protected:
-
- /// \brief Convert Between BIND-10 and log4cxx Logging Levels
- ///
- /// This method is marked protected to allow for unit testing.
- ///
- /// \param value log4cxx numeric logging level
- ///
- /// \return BIND-10 logging severity
- isc::log::Severity convertLevel(int value);
-
-private:
-
- /// \brief Get Severity Level for Logger
- ///
- /// This is common code for getSeverity() and getEffectiveSeverity() -
- /// it returns the severity of the logger; if not set (and the check_parent)
- /// flag is set, it searches up the parent-child tree until a severity
- /// level is found and uses that.
- ///
- /// \param ptrlogger Pointer to the log4cxx logger to check.
- /// \param check_parent true to search up the tree, false to return the
- /// current level.
- ///
- /// \return The effective severity level of the logger. This is the same
- /// as getSeverity() if the logger has a severity level set, but otherwise
- /// is the severity of the parent.
- isc::log::Severity getSeverityCommon(const log4cxx::LoggerPtr& ptrlogger,
- bool check_parent);
-
-
-
- /// \brief Initialize log4cxx Logger
- ///
- /// Creates the log4cxx logger used internally. A function is provided for
- /// this so that the creation does not take place when this Logger object
- /// is created but when it is used. As the latter occurs in executable
- /// code but the former can occur during initialization, this order
- /// guarantees that anything that is statically initialized has completed
- /// its initialization by the time the logger is used.
- void initLogger();
-
-
- /// \brief Return underlying log4cxx logger, initializing it if necessary
- ///
- /// \return Loggerptr object
- log4cxx::LoggerPtr& getLogger() {
- if (loggerptr_ == NULL) {
- initLogger();
- }
- return (*loggerptr_);
- }
-
- // Members. Note that loggerptr_ is a pointer to a LoggerPtr, which is
- // itself a pointer to the underlying log4cxx logger. This is due to the
- // problems with memory deletion on program exit, explained in the comments
- // for the "exit_delete" parameter in this class's constructor.
-
- log4cxx::LoggerPtr* loggerptr_; ///< Pointer to the underlying logger
- std::string name_; ///< Name of this logger]
- bool exit_delete_; ///< Delete loggerptr_ on exit?
-
- // NOTE - THIS IS A PLACE HOLDER
- static bool init_; ///< Set true when initialized
-};
-
-} // namespace log
-} // namespace isc
-
-
-#endif // __LOGGER_IMPL_LOG4CXX_H
diff --git a/src/lib/log/logger_level.cc b/src/lib/log/logger_level.cc
new file mode 100644
index 0000000..abac5be
--- /dev/null
+++ b/src/lib/log/logger_level.cc
@@ -0,0 +1,48 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE
+
+#include <log/logger_level.h>
+#include <log/macros.h>
+#include <log/log_messages.h>
+
+#include <boost/algorithm/string.hpp>
+
+
+namespace isc {
+namespace log {
+
+isc::log::Severity
+getSeverity(const std::string& sev_str) {
+ if (boost::iequals(sev_str, "DEBUG")) {
+ return isc::log::DEBUG;
+ } else if (boost::iequals(sev_str, "INFO")) {
+ return isc::log::INFO;
+ } else if (boost::iequals(sev_str, "WARN")) {
+ return isc::log::WARN;
+ } else if (boost::iequals(sev_str, "ERROR")) {
+ return isc::log::ERROR;
+ } else if (boost::iequals(sev_str, "FATAL")) {
+ return isc::log::FATAL;
+ } else if (boost::iequals(sev_str, "NONE")) {
+ return isc::log::NONE;
+ } else {
+ Logger logger("log");
+ LOG_ERROR(logger, LOG_BAD_SEVERITY).arg(sev_str);
+ return isc::log::INFO;
+ }
+}
+
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_level.h b/src/lib/log/logger_level.h
new file mode 100644
index 0000000..ea60c3c
--- /dev/null
+++ b/src/lib/log/logger_level.h
@@ -0,0 +1,76 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOGGER_LEVEL_H
+#define __LOGGER_LEVEL_H
+
+#include <string>
+
+namespace isc {
+namespace log {
+
+/// \brief Severity Levels
+///
+/// Defines the severity levels for logging. This is shared between the logger
+/// and the implementations classes.
+///
+/// N.B. The order of the levels - DEBUG less than INFO less that WARN etc. is
+/// implicitly assumed in several implementations. They must not be changed.
+
+typedef enum {
+ DEFAULT = 0, // Default to logging level of the parent
+ DEBUG = 1,
+ INFO = 2,
+ WARN = 3,
+ ERROR = 4,
+ FATAL = 5,
+ NONE = 6 // Disable logging
+} Severity;
+
+/// Minimum/maximum debug levels.
+
+const int MIN_DEBUG_LEVEL = 0;
+const int MAX_DEBUG_LEVEL = 99;
+
+/// \brief Log level structure
+///
+/// A simple pair structure that provides suitable names for the members. It
+/// holds a combination of logging severity and debug level.
+struct Level {
+ Severity severity; ///< Logging severity
+ int dbglevel; ///< Debug level
+
+ Level(Severity sev = DEFAULT, int dbg = MIN_DEBUG_LEVEL) :
+ severity(sev), dbglevel(dbg)
+ {}
+
+ // Default assignment and copy constructor is appropriate
+};
+
+/// \brief Returns the isc::log::Severity value represented by the given string
+///
+/// This must be one of the strings "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or
+/// "NONE". (Case is not important, but the string most not contain leading or
+/// trailing spaces.)
+///
+/// \param sev_str The string representing severity value
+///
+/// \return The severity. If the string is not recognized, an error will be
+/// logged and the string will return isc::log::INFO.
+isc::log::Severity getSeverity(const std::string& sev_str);
+
+} // namespace log
+} // namespace isc
+
+#endif // __LOGGER_LEVEL_H
diff --git a/src/lib/log/logger_level_impl.cc b/src/lib/log/logger_level_impl.cc
new file mode 100644
index 0000000..397f6d4
--- /dev/null
+++ b/src/lib/log/logger_level_impl.cc
@@ -0,0 +1,217 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <algorithm>
+#include <string.h>
+#include <iostream>
+#include <boost/lexical_cast.hpp>
+
+#include <log4cplus/logger.h>
+
+#include <log/logger_level.h>
+#include <log/logger_level_impl.h>
+#include <log/logimpl_messages.h>
+#include <log/macros.h>
+
+using namespace log4cplus;
+using namespace std;
+
+namespace {
+isc::log::Logger logger("log");
+}
+
+namespace isc {
+namespace log {
+
+// Convert BIND 10 level to a log4cplus logging level.
+log4cplus::LogLevel
+LoggerLevelImpl::convertFromBindLevel(const Level& level) {
+
+ // BIND 10 logging levels are small integers so we can do a table lookup
+ static const log4cplus::LogLevel log4cplus_levels[] = {
+ log4cplus::NOT_SET_LOG_LEVEL,
+ log4cplus::DEBUG_LOG_LEVEL,
+ log4cplus::INFO_LOG_LEVEL,
+ log4cplus::WARN_LOG_LEVEL,
+ log4cplus::ERROR_LOG_LEVEL,
+ log4cplus::FATAL_LOG_LEVEL,
+ log4cplus::OFF_LOG_LEVEL
+ };
+
+ // ... with compile-time checks to ensure that table indexes are correct.
+ BOOST_STATIC_ASSERT(static_cast<int>(DEFAULT) == 0);
+ BOOST_STATIC_ASSERT(static_cast<int>(DEBUG) == 1);
+ BOOST_STATIC_ASSERT(static_cast<int>(INFO) == 2);
+ BOOST_STATIC_ASSERT(static_cast<int>(WARN) == 3);
+ BOOST_STATIC_ASSERT(static_cast<int>(ERROR) == 4);
+ BOOST_STATIC_ASSERT(static_cast<int>(FATAL) == 5);
+ BOOST_STATIC_ASSERT(static_cast<int>(NONE) == 6);
+
+ // Do the conversion
+ if (level.severity == DEBUG) {
+
+ // Debug severity, so the log4cplus level returned depends on the
+ // debug level. Silently limit the debug level to the range
+ // MIN_DEBUG_LEVEL to MAX_DEBUG_LEVEL (avoids the hassle of throwing
+ // and catching exceptions and besides, this is for debug information).
+ int limited = std::max(MIN_DEBUG_LEVEL,
+ std::min(level.dbglevel, MAX_DEBUG_LEVEL));
+ LogLevel newlevel = static_cast<int>(DEBUG_LOG_LEVEL -
+ (limited - MIN_DEBUG_LEVEL));
+ return (static_cast<log4cplus::LogLevel>(newlevel));
+
+ } else {
+
+ // Can do a table lookup to speed things up. There is no need to check
+ // that the index is out of range. That the variable is of type
+ // isc::log::Severity ensures that it must be one of the Severity enum
+ // members - conversion of a numeric value to an enum is not permitted.
+ return (log4cplus_levels[level.severity]);
+ }
+}
+
+// Convert log4cplus logging level to BIND 10 debug level. It is up to the
+// caller to validate that the debug level is valid.
+Level
+LoggerLevelImpl::convertToBindLevel(const log4cplus::LogLevel loglevel) {
+
+ // Not easy to do a table lookup as the numerical values of log4cplus levels
+ // are quite high.
+ if (loglevel <= log4cplus::NOT_SET_LOG_LEVEL) {
+ return (Level(DEFAULT));
+
+ } else if (loglevel <= log4cplus::DEBUG_LOG_LEVEL) {
+
+ // Debug severity, so extract the debug level from the numeric value.
+ // If outside the limits, change the severity to the level above or
+ // below.
+ int dbglevel = MIN_DEBUG_LEVEL +
+ static_cast<int>(log4cplus::DEBUG_LOG_LEVEL) -
+ static_cast<int>(loglevel);
+ if (dbglevel > MAX_DEBUG_LEVEL) {
+ return (Level(DEFAULT));
+ } else if (dbglevel < MIN_DEBUG_LEVEL) {
+ return (Level(INFO));
+ }
+ return (Level(DEBUG, dbglevel));
+
+ } else if (loglevel <= log4cplus::INFO_LOG_LEVEL) {
+ return (Level(INFO));
+
+ } else if (loglevel <= log4cplus::WARN_LOG_LEVEL) {
+ return (Level(WARN));
+
+ } else if (loglevel <= log4cplus::ERROR_LOG_LEVEL) {
+ return (Level(ERROR));
+
+ } else if (loglevel <= log4cplus::FATAL_LOG_LEVEL) {
+ return (Level(FATAL));
+
+ }
+
+ return (Level(NONE));
+}
+
+
+// Convert string to appropriate logging level
+log4cplus::LogLevel
+LoggerLevelImpl::logLevelFromString(const log4cplus::tstring& level) {
+
+ std::string name = level; // Get to known type
+ size_t length = name.size(); // Length of the string
+
+ if (length < 5) {
+
+ // String can't possibly start DEBUG so we don't know what it is.
+ // As per documentation, return NOT_SET level.
+ return (NOT_SET_LOG_LEVEL);
+ }
+ else {
+ if (strncasecmp(name.c_str(), "DEBUG", 5) == 0) {
+
+ // String starts "DEBUG" (or "debug" or any case mixture). The
+ // rest of the string - if any - should be a number.
+ if (length == 5) {
+
+ // It is plain "DEBUG". Take this as level 0.
+ return (DEBUG_LOG_LEVEL);
+ }
+ else {
+
+ // Try converting the remainder to an integer. The "5" is
+ // the length of the string "DEBUG". Note that if the number
+ // is outside the range of debug levels, it is coerced to the
+ // nearest limit. Thus a level of DEBUG509 will end up as
+ // if DEBUG99 has been specified.
+ try {
+ int dbglevel = boost::lexical_cast<int>(name.substr(5));
+ if (dbglevel < MIN_DEBUG_LEVEL) {
+ LOG_WARN(logger, LOGIMPL_BELOW_MIN_DEBUG).arg(dbglevel)
+ .arg(MIN_DEBUG_LEVEL);
+ dbglevel = MIN_DEBUG_LEVEL;
+
+ } else if (dbglevel > MAX_DEBUG_LEVEL) {
+ LOG_WARN(logger, LOGIMPL_ABOVE_MAX_DEBUG).arg(dbglevel)
+ .arg(MAX_DEBUG_LEVEL);
+ dbglevel = MAX_DEBUG_LEVEL;
+
+ }
+ return convertFromBindLevel(Level(DEBUG, dbglevel));
+ }
+ catch (boost::bad_lexical_cast&) {
+ LOG_ERROR(logger, LOGIMPL_BAD_DEBUG_STRING).arg(name);
+ return (NOT_SET_LOG_LEVEL);
+ }
+ }
+ } else {
+
+ // Unknown string - return default. Log4cplus will call any other
+ // registered conversion functions to interpret it.
+ return (NOT_SET_LOG_LEVEL);
+ }
+ }
+}
+
+// Convert logging level to string. If the level is a valid debug level,
+// return the string DEBUG, else return the empty string.
+log4cplus::tstring
+LoggerLevelImpl::logLevelToString(log4cplus::LogLevel level) {
+ Level bindlevel = convertToBindLevel(level);
+ Severity& severity = bindlevel.severity;
+ int& dbglevel = bindlevel.dbglevel;
+
+ if ((severity == DEBUG) &&
+ ((dbglevel >= MIN_DEBUG_LEVEL) && (dbglevel <= MAX_DEBUG_LEVEL))) {
+ return (tstring("DEBUG"));
+ }
+
+ // Unknown, so return empty string for log4cplus to try other conversion
+ // functions.
+ return (tstring());
+}
+
+// Initialization. Register the conversion functions with the LogLevelManager.
+void
+LoggerLevelImpl::init() {
+
+ // Get the singleton log-level manager.
+ LogLevelManager& manager = getLogLevelManager();
+
+ // Register the conversion functions
+ manager.pushFromStringMethod(LoggerLevelImpl::logLevelFromString);
+ manager.pushToStringMethod(LoggerLevelImpl::logLevelToString);
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_level_impl.h b/src/lib/log/logger_level_impl.h
new file mode 100644
index 0000000..9289a1d
--- /dev/null
+++ b/src/lib/log/logger_level_impl.h
@@ -0,0 +1,127 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOGGER_LEVEL_IMPL_H
+#define __LOGGER_LEVEL_IMPL_H
+
+#include <log4cplus/logger.h>
+#include <log/logger_level.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Implementation aspects of logging levels
+///
+/// This extends the log4cplus level set to allow 100 debug levels.
+///
+/// First some terminology, as the use of the term "level" gets confusing. The
+/// code and comments here use the term "level" in two contexts:
+///
+/// Logging level: The category of messages to log. By default log4cplus
+/// defines the following logging levels: OFF_LOG_LEVEL, FATAL_LOG_LEVEL,
+/// ERROR_LOG_LEVEL, WARN_LOG_LEVEL, INFO_LOG_LEVEL, DEBUG_LOG_LEVEL,
+/// TRACE_LOG_LEVEL, ALL_LOG_LEVEL (which here will be abbreviated OFF, FATAL
+/// etc.). Within the context of BIND-10, OFF, TRACE and ALL are not used
+/// and the idea of DEBUG has been extended, as will be seen below. In
+/// BIND 10 terms, this is known as "severity"; the "logging level" usage will
+/// usually be used when talking about log4cplus aspects of the idea (as
+/// log4cplus uses that teminology).
+///
+/// Debug level: This is a number that ranges from 0 to 99 and is used by the
+/// application to control the detail of debug output. A value of 0 gives the
+/// highest-level debug output; a value of 99 gives the most verbose and most
+/// detailed. Debug messages (or whatever debug level) are only ever output
+/// when the logging level is set to DEBUG. (Note that the numbers 0 and 99
+/// are not hard-coded - they are the constants MIN_DEBUG_LEVEL and
+/// MAX_DEBUG_LEVEL.)
+///
+/// With log4cplus, the various logging levels have a numeric value associated
+/// with them, such that FATAL > ERROR > WARNING etc. This suggests that the
+/// idea of debug levels can be incorporated into the existing logging level
+/// scheme by assigning them appropriate numeric values, i.e.
+///
+/// WARNING > INFO > DEBUG > DEBUG - 1 > DEBUG - 2 > ... > DEBUG - 99
+///
+/// Setting a numeric level of DEBUG enables the basic messages; setting higher
+/// debug levels (corresponding to lower numeric logging levels) will enable
+/// progressively more messages. The lowest debug level (0) is chosen such that
+/// it corresponds to the default level of DEBUG.
+///
+/// This class comprises nothing more than static methods to aid the conversion
+/// of logging levels between log4cplus and BIND 10, and to register those
+/// levels with log4cplus.
+
+class LoggerLevelImpl {
+public:
+
+ /// \brief Convert BIND 10 level to log4cplus logging level
+ ///
+ /// Converts the BIND 10 severity level into a log4cplus logging level.
+ /// If the severity is DEBUG, the current BIND 10 debug level is taken
+ /// into account when doing the conversion.
+ ///
+ /// \param level BIND 10 severity and debug level
+ ///
+ /// \return Equivalent log4cplus logging level.
+ static
+ log4cplus::LogLevel convertFromBindLevel(const isc::log::Level& level);
+
+ /// \brief Convert log4cplus logging level to BIND 10 logging level
+ ///
+ /// Converts the log4cplus log level into a BIND 10 severity level.
+ /// The log4cplus log level may be non-standard in which case it is
+ /// encoding a BIND 10 debug level as well.
+ ///
+ /// \param level log4cplus log level
+ ///
+ /// \return Equivalent BIND 10 severity and debug level
+ static
+ isc::log::Level convertToBindLevel(const log4cplus::LogLevel loglevel);
+
+ /// \brief Convert string to log4cplus logging level
+ ///
+ /// BIND 10 extends the set of logging levels in log4cplus with a group
+ /// of debug levels. These are given names DEBUG0 through DEBUG99 (with
+ /// DEBUG0 being equivalent to DEBUG, the standard log level. If the name
+ /// is DEBUGn but n lies outside the range of debug levels, debug level
+ /// specifies is coerced to the nearest valid value. If the string is just
+ /// not recognised, a NOT_SET_LOG_LEVEL is returned.
+ ///
+ /// \param level String representing the logging level.
+ ///
+ /// \return Corresponding log4cplus log level
+ static
+ log4cplus::LogLevel logLevelFromString(const log4cplus::tstring& level);
+
+ /// \brief Convert log level to string
+ ///
+ /// If the log level is one of the extended debug levels, the string DEBUG
+ /// is returned, otherwise the empty string.
+ ///
+ /// \param level Extended logging level
+ ///
+ /// \return Equivalent string.
+ static log4cplus::tstring logLevelToString(log4cplus::LogLevel level);
+
+ /// \brief Initialize extended logging levels
+ ///
+ /// This must be called once, after log4cplus has been initialized. It
+ /// registers the level/string converter functions.
+ static void init();
+};
+
+} // namespace log
+} // namespace isc
+
+#endif // __LOGGER_LEVEL_IMPL_H
diff --git a/src/lib/log/logger_levels.h b/src/lib/log/logger_levels.h
deleted file mode 100644
index 2f123e8..0000000
--- a/src/lib/log/logger_levels.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __LOGGER_LEVELS_H
-#define __LOGGER_LEVELS_H
-
-namespace isc {
-namespace log {
-
-/// \brief Severity Levels
-///
-/// Defines the severity levels for logging. This is shared between the logger
-/// and the implementations classes.
-///
-/// N.B. The order of the levels - DEBUG less than INFO less that WARN etc. is
-/// implicitly assumed in several implementations. They must not be changed.
-
-typedef enum {
- DEFAULT = 0, // Default to logging level of the parent
- DEBUG = 1,
- INFO = 2,
- WARN = 3,
- ERROR = 4,
- FATAL = 5,
- NONE = 6 // Disable logging
-} Severity;
-
-} // namespace log
-} // namespace isc
-
-#endif // __LOGGER_LEVELS_H
diff --git a/src/lib/log/logger_manager.cc b/src/lib/log/logger_manager.cc
new file mode 100644
index 0000000..70e0d6f
--- /dev/null
+++ b/src/lib/log/logger_manager.cc
@@ -0,0 +1,184 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <algorithm>
+#include <vector>
+
+#include <log/logger.h>
+#include <log/logger_manager.h>
+#include <log/logger_manager_impl.h>
+#include <log/logger_name.h>
+#include <log/logger_support.h>
+#include <log/log_messages.h>
+#include <log/macros.h>
+#include <log/message_dictionary.h>
+#include <log/message_exception.h>
+#include <log/message_initializer.h>
+#include <log/message_initializer.h>
+#include <log/message_reader.h>
+#include <log/message_types.h>
+
+using namespace std;
+
+namespace {
+
+// Logger used for logging messages within the logging code itself.
+isc::log::Logger logger("log");
+
+// Static stores for the initialization severity and debug level.
+// These are put in methods to avoid a "static initialization fiasco".
+
+isc::log::Severity& initSeverity() {
+ static isc::log::Severity severity = isc::log::INFO;
+ return (severity);
+}
+
+int& initDebugLevel() {
+ static int dbglevel = 0;
+ return (dbglevel);
+}
+
+std::string& initRootName() {
+ static std::string root("bind10");
+ return (root);
+}
+
+} // Anonymous namespace
+
+
+namespace isc {
+namespace log {
+
+// Constructor - create the implementation class.
+LoggerManager::LoggerManager() {
+ impl_ = new LoggerManagerImpl();
+}
+
+// Destructor - get rid of the implementation class
+LoggerManager::~LoggerManager() {
+ delete impl_;
+}
+
+// Initialize processing
+void
+LoggerManager::processInit() {
+ impl_->processInit();
+}
+
+// Process logging specification
+void
+LoggerManager::processSpecification(const LoggerSpecification& spec) {
+ impl_->processSpecification(spec);
+}
+
+// End Processing
+void
+LoggerManager::processEnd() {
+ impl_->processEnd();
+}
+
+
+/// Logging system initialization
+
+void
+LoggerManager::init(const std::string& root, isc::log::Severity severity,
+ int dbglevel, const char* file)
+{
+ // Save name, severity and debug level for later. No need to save the
+ // file name as once the local message file is read the messages will
+ // not be lost.
+ initRootName() = root;
+ initSeverity() = severity;
+ initDebugLevel() = dbglevel;
+
+ // Create the BIND 10 root logger and set the default severity and
+ // debug level. This is the logger that has the name of the application.
+ // All other loggers created in this application will be its children.
+ setRootLoggerName(root);
+
+ // Initialize the implementation logging. After this point, some basic
+ // logging has been set up and messages can be logged.
+ LoggerManagerImpl::init(severity, dbglevel);
+ setLoggingInitialized();
+
+ // Check if there were any duplicate message IDs in the default dictionary
+ // and if so, log them. Log using the logging facility logger.
+ vector<string>& duplicates = MessageInitializer::getDuplicates();
+ if (!duplicates.empty()) {
+
+ // There are duplicates present. This will be listed in alphabetic
+ // order of message ID, so they need to be sorted. This list itself may
+ // contain duplicates; if so, the message ID is listed as many times as
+ // there are duplicates.
+ sort(duplicates.begin(), duplicates.end());
+ for (vector<string>::iterator i = duplicates.begin();
+ i != duplicates.end(); ++i) {
+ LOG_WARN(logger, LOG_DUPLICATE_MESSAGE_ID).arg(*i);
+ }
+
+ }
+
+ // Replace any messages with local ones (if given)
+ if (file) {
+ readLocalMessageFile(file);
+ }
+}
+
+
+// Read local message file
+// TODO This should be done after the configuration has been read so that
+// the file can be placed in the local configuration
+void
+LoggerManager::readLocalMessageFile(const char* file) {
+
+ MessageDictionary& dictionary = MessageDictionary::globalDictionary();
+ MessageReader reader(&dictionary);
+ try {
+
+ logger.info(LOG_READING_LOCAL_FILE).arg(file);
+ reader.readFile(file, MessageReader::REPLACE);
+
+ // File successfully read. As each message in the file is supposed to
+ // replace one in the dictionary, any ID read that can't be located in
+ // the dictionary will not be used. To aid problem diagnosis, the
+ // unknown message IDs are listed.
+ MessageReader::MessageIDCollection unknown = reader.getNotAdded();
+ for (MessageReader::MessageIDCollection::const_iterator
+ i = unknown.begin(); i != unknown.end(); ++i) {
+ string message_id = boost::lexical_cast<string>(*i);
+ logger.warn(LOG_NO_SUCH_MESSAGE).arg(message_id);
+ }
+ }
+ catch (MessageException& e) {
+ MessageID ident = e.id();
+ vector<string> args = e.arguments();
+
+ // Log the variable number of arguments. The actual message will be
+ // logged when the error_message variable is destroyed.
+ Formatter<isc::log::Logger> error_message = logger.error(ident);
+ for (int i = 0; i < args.size(); ++i) {
+ error_message = error_message.arg(args[i]);
+ }
+ }
+}
+
+// Reset logging to settings passed to init()
+void
+LoggerManager::reset() {
+ setRootLoggerName(initRootName());
+ LoggerManagerImpl::reset(initSeverity(), initDebugLevel());
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_manager.h b/src/lib/log/logger_manager.h
new file mode 100644
index 0000000..dece0c9
--- /dev/null
+++ b/src/lib/log/logger_manager.h
@@ -0,0 +1,141 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOGGER_MANAGER_H
+#define __LOGGER_MANAGER_H
+
+#include "exceptions/exceptions.h"
+#include <log/logger_specification.h>
+
+// Generated if, when updating the logging specification, an unknown
+// destination is encountered.
+class UnknownLoggingDestination : public isc::Exception {
+public:
+ UnknownLoggingDestination(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what)
+ {}
+};
+
+namespace isc {
+namespace log {
+
+class LoggerManagerImpl;
+
+/// \brief Logger Manager
+///
+/// The logger manager class exists to process the set of logger specifications
+/// and use them to set up the logging in the program appropriately.
+///
+/// To isolate the underlying implementation from basic processing, the
+/// LoggerManager is implemented using the "pimpl" idiom.
+
+class LoggerManager {
+public:
+ /// \brief Constructor
+ LoggerManager();
+
+ /// \brief Destructor
+ ~LoggerManager();
+
+ /// \brief Process Specifications
+ ///
+ /// Replaces the current logging configuration by the one given.
+ ///
+ /// \param start Iterator pointing to the start of the collection of
+ /// logging specifications.
+ /// \param finish Iterator pointing to the end of the collection of
+ /// logging specification.
+ template <typename T>
+ void process(T start, T finish) {
+ processInit();
+ for (T i = start; i != finish; ++i) {
+ processSpecification(*i);
+ }
+ processEnd();
+ }
+
+ /// \brief Process a single specification
+ ///
+ /// A convenience function for a single specification.
+ ///
+ /// \param spec Specification to process
+ void process(const LoggerSpecification& spec) {
+ processInit();
+ processSpecification(spec);
+ processEnd();
+ }
+
+ /// \brief Run-Time Initialization
+ ///
+ /// Performs run-time initialization of the logger system, in particular
+ /// supplying the root logger name and name of a replacement message file.
+ ///
+ /// This must be the first logging function called in the program. If
+ /// an attempt is made to log a message before this is function is called,
+ /// the results will be dependent on the underlying logging package.
+ ///
+ /// \param root Name of the root logger. This should be set to the name of
+ /// the program.
+ /// \param severity Severity at which to log
+ /// \param dbglevel Debug severity (ignored if "severity" is not "DEBUG")
+ /// \param file Name of the local message file. This must be NULL if there
+ /// is no local message file.
+ static void init(const std::string& root,
+ isc::log::Severity severity = isc::log::INFO,
+ int dbglevel = 0, const char* file = NULL);
+
+ /// \brief Reset logging
+ ///
+ /// Resets logging to whatever was set in the call to init().
+ static void reset();
+
+ /// \brief Read local message file
+ ///
+ /// Reads the local message file into the global dictionary, overwriting
+ /// existing messages. If the file contained any message IDs not in the
+ /// dictionary, they are listed in a warning message.
+ ///
+ /// \param file Name of the local message file
+ static void readLocalMessageFile(const char* file);
+
+private:
+ /// \brief Initialize Processing
+ ///
+ /// Initializes the processing of a list of specifications by resetting all
+ /// loggers to their defaults, which is to pass the message to their
+ /// parent logger. (Except for the root logger, where the default action is
+ /// to output the message.)
+ void processInit();
+
+ /// \brief Process Logging Specification
+ ///
+ /// Processes the given specification. It is assumed at this point that
+ /// either the logger does not exist or has been made inactive.
+ void processSpecification(const LoggerSpecification& spec);
+
+ /// \brief End Processing
+ ///
+ /// Place holder for finish processing.
+ /// TODO: Check that the root logger has something enabled
+ void processEnd();
+
+ // Members
+ LoggerManagerImpl* impl_; ///< Pointer to implementation
+};
+
+} // namespace log
+} // namespace isc
+
+
+#endif // __LOGGER_MANAGER_H
diff --git a/src/lib/log/logger_manager_impl.cc b/src/lib/log/logger_manager_impl.cc
new file mode 100644
index 0000000..d69cec8
--- /dev/null
+++ b/src/lib/log/logger_manager_impl.cc
@@ -0,0 +1,228 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <algorithm>
+#include <iostream>
+
+#include <log4cplus/logger.h>
+#include <log4cplus/configurator.h>
+#include <log4cplus/consoleappender.h>
+#include <log4cplus/fileappender.h>
+#include <log4cplus/syslogappender.h>
+
+#include <log/logger.h>
+#include <log/logger_level_impl.h>
+#include <log/logger_manager.h>
+#include <log/logger_manager_impl.h>
+#include <log/log_messages.h>
+#include <log/logger_name.h>
+#include <log/logger_specification.h>
+
+using namespace std;
+
+namespace isc {
+namespace log {
+
+// Reset hierarchy of loggers back to default settings. This removes all
+// appenders from loggers, sets their severity to NOT_SET (so that events are
+// passed back to the parent) and resets the root logger to logging
+// informational messages. (This last is not a log4cplus default, so we have to
+// explicitly reset the logging severity.)
+
+void
+LoggerManagerImpl::processInit() {
+ log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
+ initRootLogger();
+}
+
+// Process logging specification. Set up the common states then dispatch to
+// add output specifications.
+
+void
+LoggerManagerImpl::processSpecification(const LoggerSpecification& spec) {
+
+ log4cplus::Logger logger = log4cplus::Logger::getInstance(
+ expandLoggerName(spec.getName()));
+
+ // Set severity level according to specification entry.
+ logger.setLogLevel(LoggerLevelImpl::convertFromBindLevel(
+ Level(spec.getSeverity(), spec.getDbglevel())));
+
+ // Set the additive flag.
+ logger.setAdditivity(spec.getAdditive());
+
+ // Output options given?
+ if (spec.optionCount() > 0) {
+
+ // Yes, so replace all appenders for this logger.
+ logger.removeAllAppenders();
+
+ // Now process output specifications.
+ for (LoggerSpecification::const_iterator i = spec.begin();
+ i != spec.end(); ++i) {
+ switch (i->destination) {
+ case OutputOption::DEST_CONSOLE:
+ createConsoleAppender(logger, *i);
+ break;
+
+ case OutputOption::DEST_FILE:
+ createFileAppender(logger, *i);
+ break;
+
+ case OutputOption::DEST_SYSLOG:
+ createSyslogAppender(logger, *i);
+ break;
+
+ default:
+ // Not a valid destination. As we are in the middle of updating
+ // logging destinations, we could be in the situation where
+ // there are no valid appenders. For this reason, throw an
+ // exception.
+ isc_throw(UnknownLoggingDestination,
+ "Unknown logging destination, code = " <<
+ i->destination);
+ }
+ }
+ }
+}
+
+// Console appender - log to either stdout or stderr.
+void
+LoggerManagerImpl::createConsoleAppender(log4cplus::Logger& logger,
+ const OutputOption& opt)
+{
+ log4cplus::SharedAppenderPtr console(
+ new log4cplus::ConsoleAppender(
+ (opt.stream == OutputOption::STR_STDERR), opt.flush));
+ setConsoleAppenderLayout(console);
+ logger.addAppender(console);
+}
+
+// File appender. Depending on whether a maximum size is given, either
+// a standard file appender or a rolling file appender will be created.
+void
+LoggerManagerImpl::createFileAppender(log4cplus::Logger& logger,
+ const OutputOption& opt)
+{
+ LOG4CPLUS_OPEN_MODE_TYPE mode =
+ LOG4CPLUS_FSTREAM_NAMESPACE::ios::app; // Append to existing file
+
+ log4cplus::SharedAppenderPtr fileapp;
+ if (opt.maxsize == 0) {
+ fileapp = log4cplus::SharedAppenderPtr(new log4cplus::FileAppender(
+ opt.filename, mode, opt.flush));
+ } else {
+ fileapp = log4cplus::SharedAppenderPtr(
+ new log4cplus::RollingFileAppender(opt.filename, opt.maxsize,
+ opt.maxver, opt.flush));
+ }
+
+ // use the same console layout for the files.
+ setConsoleAppenderLayout(fileapp);
+ logger.addAppender(fileapp);
+}
+
+// Syslog appender.
+void
+LoggerManagerImpl::createSyslogAppender(log4cplus::Logger& logger,
+ const OutputOption& opt)
+{
+ log4cplus::SharedAppenderPtr syslogapp(
+ new log4cplus::SysLogAppender(opt.facility));
+ setSyslogAppenderLayout(syslogapp);
+ logger.addAppender(syslogapp);
+}
+
+
+// One-time initialization of the log4cplus system
+
+void
+LoggerManagerImpl::init(isc::log::Severity severity, int dbglevel) {
+
+ // Set up basic configurator. This attaches a ConsoleAppender to the
+ // root logger with suitable output. This is used until we we have
+ // actually read the logging configuration, in which case the output
+ // may well be changed.
+ log4cplus::BasicConfigurator config;
+ config.configure();
+
+ // Add the additional debug levels
+ LoggerLevelImpl::init();
+
+ reset(severity, dbglevel);
+}
+
+// Reset logging to default configuration. This closes all appenders
+// and resets the root logger to output INFO messages to the console.
+// It is principally used in testing.
+void
+LoggerManagerImpl::reset(isc::log::Severity severity, int dbglevel) {
+
+ // Initialize the root logger
+ initRootLogger(severity, dbglevel);
+}
+
+// Initialize the root logger
+void LoggerManagerImpl::initRootLogger(isc::log::Severity severity,
+ int dbglevel)
+{
+ log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
+
+ // Set the log4cplus root to not output anything - effectively we are
+ // ignoring it.
+ log4cplus::Logger::getRoot().setLogLevel(log4cplus::OFF_LOG_LEVEL);
+
+ // Set the level for the BIND 10 root logger to the given severity and
+ // debug level.
+ log4cplus::Logger b10root = log4cplus::Logger::getInstance(
+ getRootLoggerName());
+ b10root.setLogLevel(LoggerLevelImpl::convertFromBindLevel(
+ Level(severity, dbglevel)));
+
+ // Set the BIND 10 root to use a console logger.
+ OutputOption opt;
+ createConsoleAppender(b10root, opt);
+}
+
+// Set the the "console" layout for the given appenders. This layout includes
+// a date/time and the name of the logger.
+
+void LoggerManagerImpl::setConsoleAppenderLayout(
+ log4cplus::SharedAppenderPtr& appender)
+{
+ // Create the pattern we want for the output - local time.
+ string pattern = "%D{%Y-%m-%d %H:%M:%S.%q} %-5p [%c] %m\n";
+
+ // Finally the text of the message
+ auto_ptr<log4cplus::Layout> layout(new log4cplus::PatternLayout(pattern));
+ appender->setLayout(layout);
+}
+
+// Set the the "syslog" layout for the given appenders. This is the same
+// as the console, but without the timestamp (which is expected to be
+// set by syslogd).
+
+void LoggerManagerImpl::setSyslogAppenderLayout(
+ log4cplus::SharedAppenderPtr& appender)
+{
+ // Create the pattern we want for the output - local time.
+ string pattern = "%-5p [%c] %m\n";
+
+ // Finally the text of the message
+ auto_ptr<log4cplus::Layout> layout(new log4cplus::PatternLayout(pattern));
+ appender->setLayout(layout);
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_manager_impl.h b/src/lib/log/logger_manager_impl.h
new file mode 100644
index 0000000..aa596a0
--- /dev/null
+++ b/src/lib/log/logger_manager_impl.h
@@ -0,0 +1,171 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOGGER_MANAGER_IMPL_H
+#define __LOGGER_MANAGER_IMPL_H
+
+#include <string>
+
+#include <log4cplus/appender.h>
+#include <log/logger_level.h>
+
+// Forward declaration to avoid need to include log4cplus header file here.
+namespace log4cplus {
+class Logger;
+class Appender;
+}
+
+namespace isc {
+namespace log {
+
+// Forward declarations
+class LoggerSpecification;
+class OutputOption;
+
+/// \brief Logger Manager Implementation
+///
+/// This is the implementation of the logger manager for the log4cplus
+/// underlying logger.
+///
+/// As noted in logger_manager.h, the logger manager class exists to set up the
+/// logging given a set of specifications. This class handles the processing
+/// of those specifications.
+///
+/// Note: the logging has been implemented using a "pimpl" idiom to conceal
+/// the underlying implementation (log4cplus) from the BIND 10 interface.
+/// This requires that there be an implementation class, even though in this
+/// case, all the implementation class methods can be declared static.
+
+class LoggerManagerImpl {
+public:
+
+ /// \brief Constructor
+ LoggerManagerImpl()
+ {}
+
+ /// \brief Initialize Processing
+ ///
+ /// This resets the hierachy of loggers back to their defaults. This means
+ /// that all non-root loggers (if they exist) are set to NOT_SET, and the
+ /// root logger reset to logging informational messages.
+ ///
+ /// \param root_name BIND 10 name of the root logger
+ static void processInit();
+
+ /// \brief Process Specification
+ ///
+ /// Processes the specification for a single logger.
+ ///
+ /// \param spec Logging specification for this logger
+ static void processSpecification(const LoggerSpecification& spec);
+
+ /// \brief End Processing
+ ///
+ /// Terminates the processing of the logging specifications.
+ static void processEnd()
+ {}
+
+ /// \brief Implementation-specific initialization
+ ///
+ /// Sets the basic configuration for logging (the root logger has INFO and
+ /// more severe messages routed to stdout). Unless this function (or
+ /// process() with a valid specification for all loggers that will log
+ /// messages) is called before a message is logged, log4cplus will output
+ /// a message to stderr noting that logging has not been initialized.
+ ///
+ /// It is assumed here that the name of the BIND 10 root logger can be
+ /// obtained from the global function getRootLoggerName().
+ ///
+ /// \param severity Severity to be associated with this logger
+ /// \param dbglevel Debug level associated with the root logger
+ static void init(isc::log::Severity severity = isc::log::INFO,
+ int dbglevel = 0);
+
+ /// \brief Reset logging
+ ///
+ /// Resets to default configuration (root logger logging to the console
+ /// with INFO severity).
+ ///
+ /// \param severity Severity to be associated with this logger
+ /// \param dbglevel Debug level associated with the root logger
+ static void reset(isc::log::Severity severity = isc::log::INFO,
+ int dbglevel = 0);
+
+private:
+ /// \brief Create console appender
+ ///
+ /// Creates an object that, when attached to a logger, will log to one
+ /// of the output streams (stdout or stderr).
+ ///
+ /// \param logger Log4cplus logger to which the appender must be attached.
+ /// \param opt Output options for this appender.
+ static void createConsoleAppender(log4cplus::Logger& logger,
+ const OutputOption& opt);
+
+ /// \brief Create file appender
+ ///
+ /// Creates an object that, when attached to a logger, will log to a
+ /// specified file. This also includes the ability to "roll" files when
+ /// they reach a specified size.
+ ///
+ /// \param logger Log4cplus logger to which the appender must be attached.
+ /// \param opt Output options for this appender.
+ static void createFileAppender(log4cplus::Logger& logger,
+ const OutputOption& opt);
+
+ /// \brief Create syslog appender
+ ///
+ /// Creates an object that, when attached to a logger, will log to the
+ /// syslog file.
+ ///
+ /// \param logger Log4cplus logger to which the appender must be attached.
+ /// \param opt Output options for this appender.
+ static void createSyslogAppender(log4cplus::Logger& logger,
+ const OutputOption& opt);
+
+ /// \brief Set default layout and severity for root logger
+ ///
+ /// Initializes the root logger to BIND 10 defaults - console output and
+ /// the passed severity/debug level.
+ ///
+ /// \param severity Severity of messages that the logger should output.
+ /// \param dbglevel Debug level if severity = DEBUG
+ static void initRootLogger(isc::log::Severity severity = isc::log::INFO,
+ int dbglevel = 0);
+
+ /// \brief Set layout for console appender
+ ///
+ /// Sets the layout of the specified appender to one suitable for file
+ /// or console output:
+ ///
+ /// YYYY-MM-DD HH:MM:SS.ssss SEVERITY [root.logger] message
+ ///
+ /// \param appender Appender for which this pattern is to be set.
+ static void setConsoleAppenderLayout(log4cplus::SharedAppenderPtr& appender);
+
+ /// \brief Set layout for syslog appender
+ ///
+ /// Sets the layout of the specified appender to one suitable for the
+ /// syslog file:
+ ///
+ /// SEVERITY [root.logger] message
+ ///
+ /// \param appender Appender for which this pattern is to be set.
+ static void setSyslogAppenderLayout(log4cplus::SharedAppenderPtr& appender);
+};
+
+} // namespace log
+} // namespace isc
+
+#endif // __LOGGER_MANAGER_IMPL_H
diff --git a/src/lib/log/logger_name.cc b/src/lib/log/logger_name.cc
new file mode 100644
index 0000000..abfcd5e
--- /dev/null
+++ b/src/lib/log/logger_name.cc
@@ -0,0 +1,59 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+#include "log/logger_name.h"
+
+namespace isc {
+namespace log {
+
+namespace {
+
+// Obtain the root logger name in a way that is safe for statically-initialized
+// objects.
+
+std::string&
+getRootLoggerNameInternal() {
+ static std::string root_name;
+ return (root_name);
+}
+
+} // Anonymous namespace
+
+void
+setRootLoggerName(const std::string& name) {
+ getRootLoggerNameInternal() = name;
+}
+
+const std::string& getRootLoggerName() {
+ return (getRootLoggerNameInternal());
+}
+
+std::string expandLoggerName(const std::string& name) {
+
+ // Are we the root logger, or does the logger name start with
+ // the string "<root_logger_name>.". If so, use a logger
+ // whose name is the one given.
+ if ((name == getRootLoggerName()) ||
+ (name.find(getRootLoggerName() + std::string(".")) == 0)) {
+ return (name);
+
+ }
+
+ // Anything else is assumed to be a sub-logger of the root logger.
+ return (getRootLoggerName() + "." + name);
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_name.h b/src/lib/log/logger_name.h
new file mode 100644
index 0000000..82ea2ad
--- /dev/null
+++ b/src/lib/log/logger_name.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOGGER_NAME_H
+#define __LOGGER_NAME_H
+
+#include <string>
+
+/// \brief Define Name of Root Logger
+///
+/// In BIND-10, the name root logger of a program is the name of the program
+/// itself (in contrast to packages such as log4cplus where the root logger name
+// is something like "root"). These trivial functions allow the setting and
+// getting of that name by the logger classes.
+
+namespace isc {
+namespace log {
+
+/// \brief Set root logger name
+///
+/// This function should be called by the program's initialization code before
+/// any logging functions are called.
+///
+/// \param name Name of the root logger. This should be the program name.
+void setRootLoggerName(const std::string& name);
+
+/// \brief Get root logger name
+///
+/// \return Name of the root logger.
+const std::string& getRootLoggerName();
+
+/// \brief Expand logger name
+///
+/// Given a logger name, returns the fully-expanded logger name. If the name
+/// starts with the root logger name, it is returned as-is. Otherwise it is
+/// prefixed with the root logger name.
+///
+/// \param name Name to expand.
+///
+/// \return Fully-expanded logger name.
+std::string expandLoggerName(const std::string& name);
+
+}
+}
+
+#endif // __LOGGER_NAME_H
diff --git a/src/lib/log/logger_specification.h b/src/lib/log/logger_specification.h
new file mode 100644
index 0000000..35c879c
--- /dev/null
+++ b/src/lib/log/logger_specification.h
@@ -0,0 +1,156 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOGGER_SPECIFICATION_H
+#define __LOGGER_SPECIFICATION_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <log/logger_level.h>
+#include <log/output_option.h>
+
+/// \brief Logger Specification
+///
+/// The logging configuration options are a list of logger specifications, each
+/// of which represents a logger and the options for its appenders.
+///
+/// Unlike OutputOption (which is a struct), this contains a bit more
+/// structure and is concealed in a class.
+
+#include <vector>
+
+namespace isc {
+namespace log {
+
+class LoggerSpecification {
+public:
+ typedef std::vector<OutputOption>::iterator iterator;
+ typedef std::vector<OutputOption>::const_iterator const_iterator;
+
+ /// \brief Constructor
+ ///
+ /// \param name Name of the logger.
+ /// \param severity Severity at which this logger logs
+ /// \param dbglevel Debug level
+ /// \param additive true to cause message logged with this logger to be
+ /// passed to the parent for logging.
+ LoggerSpecification(const std::string& name = "",
+ isc::log::Severity severity = isc::log::INFO,
+ int dbglevel = 0, bool additive = false) :
+ name_(name), severity_(severity), dbglevel_(dbglevel),
+ additive_(additive)
+ {}
+
+ /// \brief Set the name of the logger.
+ ///
+ /// \param name Name of the logger.
+ void setName(const std::string& name) {
+ name_ = name;
+ }
+
+ /// \return Return logger name.
+ std::string getName() const {
+ return name_;
+ }
+
+ /// \brief Set the severity.
+ ///
+ /// \param severity New severity of the logger.
+ void setSeverity(isc::log::Severity severity) {
+ severity_ = severity;
+ }
+
+ /// \return Return logger severity.
+ isc::log::Severity getSeverity() const {
+ return severity_;
+ }
+
+ /// \brief Set the debug level.
+ ///
+ /// \param dbglevel New debug level of the logger.
+ void setDbglevel(int dbglevel) {
+ dbglevel_ = dbglevel;
+ }
+
+ /// \return Return logger debug level
+ int getDbglevel() const {
+ return dbglevel_;
+ }
+
+ /// \brief Set the additive flag.
+ ///
+ /// \param additive New value of the additive flag.
+ void setAdditive(bool additive) {
+ additive_ = additive;
+ }
+
+ /// \return Return additive flag.
+ int getAdditive() const {
+ return additive_;
+ }
+
+ /// \brief Add output option.
+ ///
+ /// \param Option to add to the list.
+ void addOutputOption(const OutputOption& option) {
+ options_.push_back(option);
+ }
+
+ /// \return Iterator to start of output options.
+ iterator begin() {
+ return options_.begin();
+ }
+
+ /// \return Iterator to start of output options.
+ const_iterator begin() const {
+ return options_.begin();
+ }
+
+ /// \return Iterator to end of output options.
+ iterator end() {
+ return options_.end();
+ }
+
+ /// \return Iterator to end of output options.
+ const_iterator end() const {
+ return options_.end();
+ }
+
+ /// \return Number of output specification options.
+ size_t optionCount() const {
+ return options_.size();
+ }
+
+ /// \brief Reset back to defaults.
+ void reset() {
+ name_ = "";
+ severity_ = isc::log::INFO;
+ dbglevel_ = 0;
+ additive_ = false;
+ options_.clear();
+ }
+
+private:
+ std::string name_; ///< Logger name
+ isc::log::Severity severity_; ///< Severity for this logger
+ int dbglevel_; ///< Debug level
+ bool additive_; ///< Chaining output
+ std::vector<OutputOption> options_; ///< Logger options
+};
+
+} // namespace log
+} // namespace isc
+
+#endif // __LOGGER_SPEC_IFICATIONH
diff --git a/src/lib/log/logger_support.cc b/src/lib/log/logger_support.cc
index 1ac4481..2c5ab45 100644
--- a/src/lib/log/logger_support.cc
+++ b/src/lib/log/logger_support.cc
@@ -21,111 +21,181 @@
/// appropriate).
/// b) Reads in the local message file is one has been supplied.
///
-/// These functions will be replaced once the code has bneen written to obtain
+/// These functions will be replaced once the code has been written to obtain
/// the logging parameters from the configuration database.
+#include <iostream>
#include <algorithm>
+#include <iostream>
#include <string>
-#include <vector>
-#include <boost/lexical_cast.hpp>
-#include <log/logger.h>
+#include <log/logger_level.h>
+#include <log/logger_manager.h>
+#include <log/logger_specification.h>
#include <log/logger_support.h>
-#include <log/messagedef.h>
-#include <log/message_dictionary.h>
-#include <log/message_exception.h>
-#include <log/message_initializer.h>
-#include <log/message_reader.h>
-#include <log/message_types.h>
-#include <log/root_logger_name.h>
-
-namespace isc {
-namespace log {
+#include <log/output_option.h>
using namespace std;
-// Declare a logger for the logging subsystem. This is a sub-logger of the
-// root logger and is used in all functions in this file.
-Logger logger("log");
+namespace {
+// Flag to hold logging initialization state.
+bool logging_init_state = false;
-/// \brief Reads Local Message File
-///
-/// Reads the local message file into the global dictionary, overwriting
-/// existing messages. If the file contained any message IDs not in the
-/// dictionary, they are listed in a warning message.
-///
-/// \param file Name of the local message file
-static void
-readLocalMessageFile(const char* file) {
-
- MessageDictionary& dictionary = MessageDictionary::globalDictionary();
- MessageReader reader(&dictionary);
- try {
- logger.info(MSG_RDLOCMES, file);
- reader.readFile(file, MessageReader::REPLACE);
-
- // File successfully read, list the duplicates
- MessageReader::MessageIDCollection unknown = reader.getNotAdded();
- for (MessageReader::MessageIDCollection::const_iterator
- i = unknown.begin(); i != unknown.end(); ++i) {
- string message_id = boost::lexical_cast<string>(*i);
- logger.warn(MSG_IDNOTFND, message_id.c_str());
- }
- }
- catch (MessageException& e) {
- MessageID ident = e.id();
- vector<string> args = e.arguments();
- switch (args.size()) {
- case 0:
- logger.error(ident);
- break;
-
- case 1:
- logger.error(ident, args[0].c_str());
- break;
-
- default: // 2 or more (2 should be the maximum)
- logger.error(ident, args[0].c_str(), args[1].c_str());
+
+// Set logging destination according to the setting of B10_LOGGER_DESTINATION.
+// (See header for initLogger() for more details.) This is a no-op if the
+// environment variable is not defined.
+//
+// \param root Name of the root logger
+// \param severity Severity level to be assigned to the root logger
+// \param dbglevel Debug level
+
+void
+setDestination(const char* root, const isc::log::Severity severity,
+ const int dbglevel) {
+
+ using namespace isc::log;
+
+ const char* destination = getenv("B10_LOGGER_DESTINATION");
+ if (destination != NULL) {
+
+ // Constants: not declared static as this is function is expected to be
+ // called once only
+ const string STDOUT = "stdout";
+ const string STDERR = "stderr";
+ const string SYSLOG = "syslog";
+ const string SYSLOG_COLON = "syslog:";
+
+ // Prepare the objects to define the logging specification
+ LoggerSpecification spec(root, severity, dbglevel);
+ OutputOption option;
+
+ // Set up output option according to destination specification
+ const string dest = destination;
+ if (dest == STDOUT) {
+ option.destination = OutputOption::DEST_CONSOLE;
+ option.stream = OutputOption::STR_STDOUT;
+
+ } else if (dest == STDERR) {
+ option.destination = OutputOption::DEST_CONSOLE;
+ option.stream = OutputOption::STR_STDERR;
+
+ } else if (dest == SYSLOG) {
+ option.destination = OutputOption::DEST_SYSLOG;
+ // Use default specified in OutputOption constructor for the
+ // syslog destination
+
+ } else if (dest.find(SYSLOG_COLON) == 0) {
+ option.destination = OutputOption::DEST_SYSLOG;
+ // Must take account of the string actually being "syslog:"
+ if (dest == SYSLOG_COLON) {
+ cerr << "**ERROR** value for B10_LOGGER_DESTINATION of " <<
+ SYSLOG_COLON << " is invalid, " << SYSLOG <<
+ " will be used instead\n";
+ // Use default for logging facility
+
+ } else {
+ // Everything else in the string is the facility name
+ option.facility = dest.substr(SYSLOG_COLON.size());
+ }
+
+ } else {
+ // Not a recognised destination, assume a file
+ option.destination = OutputOption::DEST_FILE;
+ option.filename = dest;
}
+
+ // ... and set the destination
+ spec.addOutputOption(option);
+ LoggerManager manager;
+ manager.process(spec);
}
}
-/// Logger Run-Time Initialization
+} // Anonymous namespace
+
+namespace isc {
+namespace log {
+
+// Return initialization state.
+bool
+isLoggingInitialized() {
+ return (logging_init_state);
+}
+
+// Set initialization state. (Note: as logging can be initialized via a direct
+// call to LoggerManager::init(), this function is called from there, not from
+// the initialization functions in this file.
+void
+setLoggingInitialized(bool state) {
+ logging_init_state = state;
+}
+
+// Logger Run-Time Initialization.
void
initLogger(const string& root, isc::log::Severity severity, int dbglevel,
const char* file) {
+ LoggerManager::init(root, severity, dbglevel, file);
+}
- // Create the application root logger and set the default severity and
- // debug level. This is the logger that has the name of the application.
- // All other loggers created in this application will be its children.
- setRootLoggerName(root);
- Logger root_logger(isc::log::getRootLoggerName(), true);
-
- // Set the severity associated with it. If no other logger has a severity,
- // this will be the default.
- root_logger.setSeverity(severity, dbglevel);
-
- // Check if there were any duplicate message IDs in the default dictionary
- // and if so, log them. Log using the logging facility root logger.
- vector<string>& duplicates = MessageInitializer::getDuplicates();
- if (!duplicates.empty()) {
-
- // There are - sort and remove any duplicates.
- sort(duplicates.begin(), duplicates.end());
- vector<string>::iterator new_end =
- unique(duplicates.begin(), duplicates.end());
- for (vector<string>::iterator i = duplicates.begin(); i != new_end; ++i) {
- logger.warn(MSG_DUPMSGID, i->c_str());
- }
+// Logger Run-Time Initialization via Environment Variables
+void initLogger(isc::log::Severity severity, int dbglevel) {
+
+ // Root logger name is defined by the environment variable B10_LOGGER_ROOT.
+ // If not present, the name is "bind10".
+ const char* DEFAULT_ROOT = "bind10";
+ const char* root = getenv("B10_LOGGER_ROOT");
+ if (! root) {
+ root = DEFAULT_ROOT;
+ }
+ // Set the logging severity. The environment variable is
+ // B10_LOGGER_SEVERITY, and can be one of "DEBUG", "INFO", "WARN", "ERROR"
+ // of "FATAL". Note that the string must be in upper case with no leading
+ // of trailing blanks.
+ const char* sev_char = getenv("B10_LOGGER_SEVERITY");
+ if (sev_char) {
+ severity = isc::log::getSeverity(sev_char);
}
- // Replace any messages with local ones (if given)
- if (file) {
- readLocalMessageFile(file);
+ // If the severity is debug, get the debug level (environment variable
+ // B10_LOGGER_DBGLEVEL), which should be in the range 0 to 99.
+ if (severity == isc::log::DEBUG) {
+ const char* dbg_char = getenv("B10_LOGGER_DBGLEVEL");
+ if (dbg_char) {
+ int level = 0;
+ try {
+ level = boost::lexical_cast<int>(dbg_char);
+ if (level < MIN_DEBUG_LEVEL) {
+ cerr << "**ERROR** debug level of " << level
+ << " is invalid - a value of " << MIN_DEBUG_LEVEL
+ << " will be used\n";
+ level = MIN_DEBUG_LEVEL;
+ } else if (level > MAX_DEBUG_LEVEL) {
+ cerr << "**ERROR** debug level of " << level
+ << " is invalid - a value of " << MAX_DEBUG_LEVEL
+ << " will be used\n";
+ level = MAX_DEBUG_LEVEL;
+ }
+ } catch (...) {
+ // Error, but not fatal to the test
+ cerr << "**ERROR** Unable to translate "
+ "B10_LOGGER_DBGLEVEL - a value of 0 will be used\n";
+ }
+ dbglevel = level;
+ }
}
+
+ // Set the local message file
+ const char* localfile = getenv("B10_LOGGER_LOCALMSG");
+
+ // Initialize logging
+ initLogger(root, severity, dbglevel, localfile);
+
+ // Now set the destination for logging output
+ setDestination(root, severity, dbglevel);
}
} // namespace log
diff --git a/src/lib/log/logger_support.h b/src/lib/log/logger_support.h
index 57d8383..cf83abc 100644
--- a/src/lib/log/logger_support.h
+++ b/src/lib/log/logger_support.h
@@ -15,12 +15,34 @@
#ifndef __LOGGER_SUPPORT_H
#define __LOGGER_SUPPORT_H
+#include <unistd.h>
+
#include <string>
#include <log/logger.h>
namespace isc {
namespace log {
+/// \brief Is logging initialized?
+///
+/// As some underlying logging implementations can behave unpredictably if they
+/// have not been initialized when a logging function is called, their
+/// initialization state is tracked. The logger functions will check this flag
+/// and throw an exception if logging is not initialized at that point.
+///
+/// \return true if logging has been initialized, false if not
+bool isLoggingInitialized();
+
+/// \brief Set "logging initialized" flag
+///
+/// Sets the state of the "logging initialized" flag.
+///
+/// \param state State to set the flag to. (This is expected to be "true" - the
+/// default - for all code apart from specific unit tests.)
+void setLoggingInitialized(bool state = true);
+
+
+
/// \brief Run-Time Initialization
///
/// Performs run-time initialization of the logger in particular supplying:
@@ -34,10 +56,57 @@ namespace log {
///
/// \param root Name of the root logger
/// \param severity Severity at which to log
-/// \param dbglevel Debug severiy (ignored if "severity" is not "DEBUG")
+/// \param dbglevel Debug severity (ignored if "severity" is not "DEBUG")
/// \param file Name of the local message file.
-void initLogger(const std::string& root, isc::log::Severity severity,
- int dbglevel, const char* file);
+void initLogger(const std::string& root,
+ isc::log::Severity severity = isc::log::INFO,
+ int dbglevel = 0, const char* file = NULL);
+
+
+/// \brief Run-Time Initialization from Environment
+///
+/// Performs run-time initialization of the logger via the setting of
+/// environment variables. These are:
+///
+/// - B10_LOGGER_ROOT\n
+/// Name of the root logger. If not given, the string "bind10" will be used.
+///
+/// - B10_LOGGER_SEVERITY\n
+/// Severity of messages that will be logged. This must be one of the strings
+/// "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or "NONE". (Must be upper case
+/// and must not contain leading or trailing spaces.) If not specified (or if
+/// specified but incorrect), the default passed as argument to this function
+/// (currently INFO) will be used.
+///
+/// - B10_LOGGER_DBGLEVEL\n
+/// Ignored if the level is not DEBUG, this should be a number between 0 and
+/// 99 indicating the logging severity. The default is 0. If outside these
+/// limits or if not a number, The value passed to this function (default
+/// of 0) is used.
+///
+/// - B10_LOGGER_LOCALMSG\n
+/// If defined, the path specification of a file that contains message
+/// definitions replacing ones in the default dictionary.
+///
+/// - B10_LOGGER_DESTINATION\n
+/// If defined, the destination of the logging output. This can be one of:
+/// - \c stdout Send output to stdout.
+/// - \c stderr Send output to stderr
+/// - \c syslog Send output to syslog using the facility local0.
+/// - \c syslog:xxx Send output to syslog, using the facility xxx. ("xxx"
+/// should be one of the syslog facilities such as "local0".) There must
+/// be a colon between "syslog" and "xxx
+/// - \c other Anything else is interpreted as the name of a file to which
+/// output is appended. If the file does not exist, it is created.
+///
+/// Any errors in the settings cause messages to be output to stderr.
+///
+/// This function is aimed at test programs, allowing the default settings to
+/// be overridden by the tester. It is not intended for use in production
+/// code.
+
+void initLogger(isc::log::Severity severity = isc::log::INFO,
+ int dbglevel = 0);
} // namespace log
} // namespace isc
diff --git a/src/lib/log/logimpl_messages.cc b/src/lib/log/logimpl_messages.cc
new file mode 100644
index 0000000..ca8552e
--- /dev/null
+++ b/src/lib/log/logimpl_messages.cc
@@ -0,0 +1,29 @@
+// File created from logimpl_messages.mes on Wed Jun 22 10:57:02 2011
+
+#include <cstddef>
+#include <log/message_types.h>
+#include <log/message_initializer.h>
+
+namespace isc {
+namespace log {
+
+extern const isc::log::MessageID LOGIMPL_ABOVE_MAX_DEBUG = "LOGIMPL_ABOVE_MAX_DEBUG";
+extern const isc::log::MessageID LOGIMPL_BAD_DEBUG_STRING = "LOGIMPL_BAD_DEBUG_STRING";
+extern const isc::log::MessageID LOGIMPL_BELOW_MIN_DEBUG = "LOGIMPL_BELOW_MIN_DEBUG";
+
+} // namespace log
+} // namespace isc
+
+namespace {
+
+const char* values[] = {
+ "LOGIMPL_ABOVE_MAX_DEBUG", "debug level of %1 is too high and will be set to the maximum of %2",
+ "LOGIMPL_BAD_DEBUG_STRING", "debug string '%1' has invalid format",
+ "LOGIMPL_BELOW_MIN_DEBUG", "debug level of %1 is too low and will be set to the minimum of %2",
+ NULL
+};
+
+const isc::log::MessageInitializer initializer(values);
+
+} // Anonymous namespace
+
diff --git a/src/lib/log/logimpl_messages.h b/src/lib/log/logimpl_messages.h
new file mode 100644
index 0000000..1b94838
--- /dev/null
+++ b/src/lib/log/logimpl_messages.h
@@ -0,0 +1,18 @@
+// File created from logimpl_messages.mes on Wed Jun 22 10:57:02 2011
+
+#ifndef __LOGIMPL_MESSAGES_H
+#define __LOGIMPL_MESSAGES_H
+
+#include <log/message_types.h>
+
+namespace isc {
+namespace log {
+
+extern const isc::log::MessageID LOGIMPL_ABOVE_MAX_DEBUG;
+extern const isc::log::MessageID LOGIMPL_BAD_DEBUG_STRING;
+extern const isc::log::MessageID LOGIMPL_BELOW_MIN_DEBUG;
+
+} // namespace log
+} // namespace isc
+
+#endif // __LOGIMPL_MESSAGES_H
diff --git a/src/lib/log/logimpl_messages.mes b/src/lib/log/logimpl_messages.mes
new file mode 100644
index 0000000..c40f80c
--- /dev/null
+++ b/src/lib/log/logimpl_messages.mes
@@ -0,0 +1,43 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# \brief Logger Implementation Messages
+#
+# This holds messages generated by the underlying logger implementation. They
+# are likely to be specific to that implementation, and may well change if the
+# underlying implementation is changed. For that reason, they have been put
+# in a separate file.
+
+$NAMESPACE isc::log
+
+% LOGIMPL_ABOVE_MAX_DEBUG debug level of %1 is too high and will be set to the maximum of %2
+A message from the interface to the underlying logger implementation reporting
+that the debug level (as set by an internally-created string DEBUGn, where n
+is an integer, e.g. DEBUG22) is above the maximum allowed value and has
+been reduced to that value. The appearance of this message may indicate
+a programming error - please submit a bug report.
+
+% LOGIMPL_BAD_DEBUG_STRING debug string '%1' has invalid format
+A message from the interface to the underlying logger implementation
+reporting that an internally-created string used to set the debug level
+is not of the correct format (it should be of the form DEBUGn, where n
+is an integer, e.g. DEBUG22). The appearance of this message indicates
+a programming error - please submit a bug report.
+
+% LOGIMPL_BELOW_MIN_DEBUG debug level of %1 is too low and will be set to the minimum of %2
+A message from the interface to the underlying logger implementation reporting
+that the debug level (as set by an internally-created string DEBUGn, where n
+is an integer, e.g. DEBUG22) is below the minimum allowed value and has
+been increased to that value. The appearance of this message may indicate
+a programming error - please submit a bug report.
diff --git a/src/lib/log/macros.h b/src/lib/log/macros.h
new file mode 100644
index 0000000..3128131
--- /dev/null
+++ b/src/lib/log/macros.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOG_MACROS_H
+#define __LOG_MACROS_H
+
+#include <log/logger.h>
+
+/// \brief Macro to conveniently test debug output and log it
+#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE) \
+ if (!(LOGGER).isDebugEnabled((LEVEL))) { \
+ } else \
+ (LOGGER).debug((LEVEL), (MESSAGE))
+
+/// \brief Macro to conveniently test info output and log it
+#define LOG_INFO(LOGGER, MESSAGE) \
+ if (!(LOGGER).isInfoEnabled()) { \
+ } else \
+ (LOGGER).info((MESSAGE))
+
+/// \brief Macro to conveniently test warn output and log it
+#define LOG_WARN(LOGGER, MESSAGE) \
+ if (!(LOGGER).isWarnEnabled()) { \
+ } else \
+ (LOGGER).warn((MESSAGE))
+
+/// \brief Macro to conveniently test error output and log it
+#define LOG_ERROR(LOGGER, MESSAGE) \
+ if (!(LOGGER).isErrorEnabled()) { \
+ } else \
+ (LOGGER).error((MESSAGE))
+
+/// \brief Macro to conveniently test fatal output and log it
+#define LOG_FATAL(LOGGER, MESSAGE) \
+ if (!(LOGGER).isFatalEnabled()) { \
+ } else \
+ (LOGGER).fatal((MESSAGE))
+
+#endif
diff --git a/src/lib/log/message_dictionary.cc b/src/lib/log/message_dictionary.cc
index c091369..deb8232 100644
--- a/src/lib/log/message_dictionary.cc
+++ b/src/lib/log/message_dictionary.cc
@@ -109,5 +109,5 @@ MessageDictionary::globalDictionary() {
-} // namspace log
+} // namespace log
} // namespace isc
diff --git a/src/lib/log/message_exception.cc b/src/lib/log/message_exception.cc
deleted file mode 100644
index 1a69ca5..0000000
--- a/src/lib/log/message_exception.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-/// \brief Body of Virtual Destructor
-
-#include <log/message_exception.h>
-
-namespace isc {
-namespace log {
-
-MessageException::~MessageException() throw() {
-}
-
-} // namespace log
-} // namespace isc
diff --git a/src/lib/log/message_exception.h b/src/lib/log/message_exception.h
index 30c6618..eebee89 100644
--- a/src/lib/log/message_exception.h
+++ b/src/lib/log/message_exception.h
@@ -19,6 +19,7 @@
#include <string>
#include <vector>
+#include <boost/lexical_cast.hpp>
#include <log/message_types.h>
namespace isc {
@@ -35,33 +36,47 @@ public:
/// \brief Constructor
///
- /// \param id Message identification
- MessageException(MessageID id) : id_(id)
- {}
+ /// \param id Message identification.
+ /// \param lineno Line number on which error occurred (if > 0).
+ MessageException(MessageID id, int lineno = 0) : id_(id)
+ {
+ if (lineno > 0) {
+ args_.push_back(boost::lexical_cast<std::string>(lineno));
+ }
+ }
/// \brief Constructor
///
- /// \param id Message identification
- /// \param arg1 First message argument
- MessageException(MessageID id, const std::string& arg1) : id_(id)
+ /// \param id Message identification.
+ /// \param arg1 First message argument.
+ /// \param lineno Line number on which error occurred (if > 0).
+ MessageException(MessageID id, const std::string& arg1, int lineno = 0)
+ : id_(id)
{
+ if (lineno > 0) {
+ args_.push_back(boost::lexical_cast<std::string>(lineno));
+ }
args_.push_back(arg1);
}
/// \brief Constructor
///
- /// \param id Message identification
- /// \param arg1 First message argument
- /// \param arg2 Second message argument
+ /// \param id Message identification.
+ /// \param arg1 First message argument.
+ /// \param arg2 Second message argument.
+ /// \param lineno Line number on which error occurred (if > 0).
MessageException(MessageID id, const std::string& arg1,
- const std::string& arg2) : id_(id)
+ const std::string& arg2, int lineno = 0) : id_(id)
{
+ if (lineno > 0) {
+ args_.push_back(boost::lexical_cast<std::string>(lineno));
+ }
args_.push_back(arg1);
args_.push_back(arg2);
}
/// \brief Destructor
- virtual ~MessageException() throw();
+ ~MessageException() throw() {}
/// \brief Return Message ID
///
diff --git a/src/lib/log/message_reader.cc b/src/lib/log/message_reader.cc
index 7ae7ae0..2710ab8 100644
--- a/src/lib/log/message_reader.cc
+++ b/src/lib/log/message_reader.cc
@@ -12,77 +12,85 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <cassert>
#include <errno.h>
#include <string.h>
+#include <iostream>
#include <iostream>
#include <fstream>
+#include <log/log_messages.h>
#include <log/message_exception.h>
-#include <log/messagedef.h>
#include <log/message_reader.h>
-#include <log/strutil.h>
+#include <util/strutil.h>
using namespace std;
-namespace isc {
-namespace log {
-
-// Virtual destructor.
-MessageReader::~MessageReader() {
+namespace {
+const char DIRECTIVE_FLAG = '$'; // Starts each directive
+const char MESSAGE_FLAG = '%'; // Starts each message
}
+namespace isc {
+namespace log {
+
// Read the file.
void
MessageReader::readFile(const string& file, MessageReader::Mode mode) {
- // Ensure the non-added collection is empty: this object might be
- // being reused.
+ // Ensure the non-added collection is empty: we could be re-using this
+ // object.
not_added_.clear();
- // Open the file
+ // Open the file.
ifstream infile(file.c_str());
if (infile.fail()) {
- throw MessageException(MSG_OPNMSGIN, file, strerror(errno));
+ throw MessageException(LOG_INPUT_OPEN_FAIL, file, strerror(errno));
}
- // Loop round reading it.
+ // Loop round reading it. As we process the file one line at a time,
+ // keep a track of line number of aid diagnosis of problems.
string line;
getline(infile, line);
+ lineno_ = 0;
+
while (infile.good()) {
+ ++lineno_;
processLine(line, mode);
getline(infile, line);
}
// Why did the loop terminate?
if (!infile.eof()) {
- throw MessageException(MSG_MSGRDERR, file, strerror(errno));
+ throw MessageException(LOG_READ_ERROR, file, strerror(errno));
}
infile.close();
}
-// Parse a line of the file
+// Parse a line of the file.
void
MessageReader::processLine(const string& line, MessageReader::Mode mode) {
// Get rid of leading and trailing spaces
- string text = isc::strutil::trim(line);
+ string text = isc::util::str::trim(line);
if (text.empty()) {
; // Ignore blank lines
- } else if ((text[0] == '#') || (text[0] == '+')) {
- ; // Ignore comments or descriptions
-
- } else if (text[0] == '$') {
+ } else if (text[0] == DIRECTIVE_FLAG) {
parseDirective(text); // Process directives
- } else {
- parseMessage(text, mode); // Process other lines
+ } else if (text[0] == MESSAGE_FLAG) {
+ parseMessage(text, mode); // Process message definition line
+
+ } else {
+ ; // Other lines are extended message
+ // description so are ignored
}
}
@@ -93,136 +101,168 @@ MessageReader::parseDirective(const std::string& text) {
// Break into tokens
- vector<string> tokens = isc::strutil::tokens(text);
+ vector<string> tokens = isc::util::str::tokens(text);
// Uppercase directive and branch on valid ones
- isc::strutil::uppercase(tokens[0]);
+ isc::util::str::uppercase(tokens[0]);
if (tokens[0] == string("$PREFIX")) {
parsePrefix(tokens);
+
} else if (tokens[0] == string("$NAMESPACE")) {
parseNamespace(tokens);
+
} else {
- throw MessageException(MSG_UNRECDIR, tokens[0]);
+
+ // Unrecognised directive
+ throw MessageException(LOG_UNRECOGNISED_DIRECTIVE, tokens[0], lineno_);
}
}
// Process $PREFIX
-
void
MessageReader::parsePrefix(const vector<string>& tokens) {
- // Check argument count
-
- static string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
- if (tokens.size() < 2) {
- throw MessageException(MSG_PRFNOARG);
- } else if (tokens.size() > 2) {
- throw MessageException(MSG_PRFEXTRARG);
-
- }
+ // Should not get here unless there is something in the tokens array.
+ assert(tokens.size() > 0);
- // As a style, we are going to have the symbols in uppercase
- string prefix = tokens[1];
- isc::strutil::uppercase(prefix);
+ // Process $PREFIX. With no arguments, the prefix is set to the empty
+ // string. One argument sets the prefix to the to its value and more than
+ // one argument is invalid.
+ if (tokens.size() == 1) {
+ prefix_ = "";
- // Token is potentially valid providing it only contains alphabetic
- // and numeric characters (and underscores) and does not start with a
- // digit.
- if ((prefix.find_first_not_of(valid) != string::npos) ||
- (std::isdigit(prefix[0]))) {
+ } else if (tokens.size() == 2) {
+ prefix_ = tokens[1];
- // Invalid character in string or it starts with a digit.
- throw MessageException(MSG_PRFINVARG, tokens[1]);
- }
+ // Token is potentially valid providing it only contains alphabetic
+ // and numeric characters (and underscores) and does not start with a
+ // digit.
+ if (invalidSymbol(prefix_)) {
+ throw MessageException(LOG_PREFIX_INVALID_ARG, prefix_, lineno_);
+ }
- // All OK - unless the prefix has already been set.
+ } else {
- if (prefix_.size() != 0) {
- throw MessageException(MSG_DUPLPRFX);
+ // Too many arguments
+ throw MessageException(LOG_PREFIX_EXTRA_ARGS, lineno_);
}
+}
- // Prefix has not been set, so set it and return success.
-
- prefix_ = prefix;
+// Check if string is an invalid C++ symbol. It is valid if comprises only
+// alphanumeric characters and underscores, and does not start with a digit.
+// (Owing to the logic of the rest of the code, we check for its invalidity,
+// not its validity.)
+bool
+MessageReader::invalidSymbol(const string& symbol) {
+ static const string valid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789_";
+ return ( symbol.empty() ||
+ (symbol.find_first_not_of(valid_chars) != string::npos) ||
+ (std::isdigit(symbol[0])));
}
// Process $NAMESPACE. A lot of the processing is similar to that of $PREFIX,
// except that only limited checks will be done on the namespace (to avoid a
-// lot of parsing and separating out of the namespace components.)
+// lot of parsing and separating out of the namespace components.) Also, unlike
+// $PREFIX, there can only be one $NAMESPACE in a file.
void
MessageReader::parseNamespace(const vector<string>& tokens) {
// Check argument count
-
- static string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_:"
- "abcdefghijklmnopqrstuvwxyz";
-
if (tokens.size() < 2) {
- throw MessageException(MSG_NSNOARG);
+ throw MessageException(LOG_NAMESPACE_NO_ARGS, lineno_);
} else if (tokens.size() > 2) {
- throw MessageException(MSG_NSEXTRARG);
+ throw MessageException(LOG_NAMESPACE_EXTRA_ARGS, lineno_);
}
// Token is potentially valid providing it only contains alphabetic
- // and numeric characters (and underscores and colons).
- if (tokens[1].find_first_not_of(valid) != string::npos) {
-
- // Invalid character in string or it starts with a digit.
- throw MessageException(MSG_NSINVARG, tokens[1]);
+ // and numeric characters (and underscores and colons). As noted above,
+ // we won't be exhaustive - after all, and code containing the resultant
+ // namespace will have to be compiled, and the compiler will catch errors.
+ static const string valid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789_:";
+ if (tokens[1].find_first_not_of(valid_chars) != string::npos) {
+ throw MessageException(LOG_NAMESPACE_INVALID_ARG, tokens[1], lineno_);
}
// All OK - unless the namespace has already been set.
if (ns_.size() != 0) {
- throw MessageException(MSG_DUPLNS);
+ throw MessageException(LOG_DUPLICATE_NAMESPACE, lineno_);
}
// Prefix has not been set, so set it and return success.
-
ns_ = tokens[1];
}
// Process message. By the time this method is called, the line has been
-// stripped of leading and trailing spaces, and we believe that it is a line
-// defining a message. The first token on the line is convered to uppercase
-// and becomes the message ID; the rest of the line is the message text.
+// stripped of leading and trailing spaces. The first character of the string
+// is the message introducer, so we can get rid of that. The remainder is
+// a line defining a message.
+//
+// The first token on the line, when concatenated to the prefix and converted to
+// upper-case, is the message ID. The first of the line from the next token
+// on is the message text.
void
MessageReader::parseMessage(const std::string& text, MessageReader::Mode mode) {
static string delimiters("\t\n "); // Delimiters
+ // The line passed should be at least one character long and start with the
+ // message introducer (else we should not have got here).
+ assert((text.size() >= 1) && (text[0] == MESSAGE_FLAG));
+
+ // A line comprising just the message introducer is not valid.
+ if (text.size() == 1) {
+ throw MessageException(LOG_NO_MESSAGE_ID, text, lineno_);
+ }
+
+ // Strip off the introducer and any leading space after that.
+ string message_line = isc::util::str::trim(text.substr(1));
+
// Look for the first delimiter.
- size_t first_delim = text.find_first_of(delimiters);
+ size_t first_delim = message_line.find_first_of(delimiters);
if (first_delim == string::npos) {
// Just a single token in the line - this is not valid
- throw MessageException(MSG_NOMSGTXT, text);
+ throw MessageException(LOG_NO_MESSAGE_TEXT, message_line, lineno_);
}
- // Extract the first token into the message ID
- string ident = text.substr(0, first_delim);
+ // Extract the first token into the message ID, preceding it with the
+ // current prefix, then convert to upper-case. If the prefix is not set,
+ // perform the valid character check now - the string will become a C++
+ // symbol so we may as well identify problems early.
+ string ident = prefix_ + message_line.substr(0, first_delim);
+ if (prefix_.empty()) {
+ if (invalidSymbol(ident)) {
+ throw MessageException(LOG_INVALID_MESSAGE_ID, ident, lineno_);
+ }
+ }
+ isc::util::str::uppercase(ident);
// Locate the start of the message text
- size_t first_text = text.find_first_not_of(delimiters, first_delim);
+ size_t first_text = message_line.find_first_not_of(delimiters, first_delim);
if (first_text == string::npos) {
// ?? This happens if there are trailing delimiters, which should not
// occur as we have stripped trailing spaces off the line. Just treat
// this as a single-token error for simplicity's sake.
- throw MessageException(MSG_NOMSGTXT, text);
+ throw MessageException(LOG_NO_MESSAGE_TEXT, message_line, lineno_);
}
// Add the result to the dictionary and to the non-added list if the add to
// the dictionary fails.
bool added;
if (mode == ADD) {
- added = dictionary_->add(ident, text.substr(first_text));
+ added = dictionary_->add(ident, message_line.substr(first_text));
}
else {
- added = dictionary_->replace(ident, text.substr(first_text));
+ added = dictionary_->replace(ident, message_line.substr(first_text));
}
if (!added) {
not_added_.push_back(ident);
diff --git a/src/lib/log/message_reader.h b/src/lib/log/message_reader.h
index d07c7f2..eded9c6 100644
--- a/src/lib/log/message_reader.h
+++ b/src/lib/log/message_reader.h
@@ -64,10 +64,9 @@ public:
dictionary_(dictionary)
{}
-
/// \brief Virtual Destructor
- virtual ~MessageReader();
-
+ virtual ~MessageReader()
+ {}
/// \brief Get Dictionary
///
@@ -188,10 +187,24 @@ private:
/// \param tokens $NAMESPACE line split into tokens
void parseNamespace(const std::vector<std::string>& tokens);
+ /// \brief Check for invalid C++ symbol name
+ ///
+ /// The message ID (or concatenation of prefix and message ID) will be used
+ /// as the name of a symbol in C++ code. This function checks if the name
+ /// is invalid (contains anything other than alphanumeric characters or
+ /// underscores, or starts with a digit).
+ ///
+ /// \param symbol name to check to see if it is an invalid C++ symbol.
+ ///
+ /// \return true if the name is invalid, false if it is valid.
+ bool invalidSymbol(const std::string& symbol);
+
+
/// Attributes
MessageDictionary* dictionary_; ///< Dictionary to add messages to
MessageIDCollection not_added_; ///< List of IDs not added
+ int lineno_; ///< Number of last line read
std::string prefix_; ///< Argument of $PREFIX statement
std::string ns_; ///< Argument of $NAMESPACE statement
};
diff --git a/src/lib/log/messagedef.cc b/src/lib/log/messagedef.cc
deleted file mode 100644
index f680a74..0000000
--- a/src/lib/log/messagedef.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// File created from messagedef.mes on Mon Feb 14 11:07:45 2011
-
-#include <cstddef>
-#include <log/message_types.h>
-#include <log/message_initializer.h>
-
-namespace isc {
-namespace log {
-
-extern const isc::log::MessageID MSG_DUPLNS = "DUPLNS";
-extern const isc::log::MessageID MSG_DUPLPRFX = "DUPLPRFX";
-extern const isc::log::MessageID MSG_DUPMSGID = "DUPMSGID";
-extern const isc::log::MessageID MSG_IDNOTFND = "IDNOTFND";
-extern const isc::log::MessageID MSG_MSGRDERR = "MSGRDERR";
-extern const isc::log::MessageID MSG_MSGWRTERR = "MSGWRTERR";
-extern const isc::log::MessageID MSG_NOMSGTXT = "NOMSGTXT";
-extern const isc::log::MessageID MSG_NSEXTRARG = "NSEXTRARG";
-extern const isc::log::MessageID MSG_NSINVARG = "NSINVARG";
-extern const isc::log::MessageID MSG_NSNOARG = "NSNOARG";
-extern const isc::log::MessageID MSG_OPNMSGIN = "OPNMSGIN";
-extern const isc::log::MessageID MSG_OPNMSGOUT = "OPNMSGOUT";
-extern const isc::log::MessageID MSG_PRFEXTRARG = "PRFEXTRARG";
-extern const isc::log::MessageID MSG_PRFINVARG = "PRFINVARG";
-extern const isc::log::MessageID MSG_PRFNOARG = "PRFNOARG";
-extern const isc::log::MessageID MSG_RDLOCMES = "RDLOCMES";
-extern const isc::log::MessageID MSG_UNRECDIR = "UNRECDIR";
-
-} // namespace log
-} // namespace isc
-
-namespace {
-
-const char* values[] = {
- "DUPLNS", "duplicate $NAMESPACE directive found",
- "DUPLPRFX", "duplicate $PREFIX directive found",
- "DUPMSGID", "duplicate message ID (%s) in compiled code",
- "IDNOTFND", "could not replace message for '%s': no such message identification",
- "MSGRDERR", "error reading from message file %s: %s",
- "MSGWRTERR", "error writing to %s: %s",
- "NOMSGTXT", "a line containing a message ID ('%s') and nothing else was found",
- "NSEXTRARG", "$NAMESPACE directive has too many arguments",
- "NSINVARG", "$NAMESPACE directive has an invalid argument ('%s')",
- "NSNOARG", "no arguments were given to the $NAMESPACE directive",
- "OPNMSGIN", "unable to open message file %s for input: %s",
- "OPNMSGOUT", "unable to open %s for output: %s",
- "PRFEXTRARG", "$PREFIX directive has too many arguments",
- "PRFINVARG", "$PREFIX directive has an invalid argument ('%s')",
- "PRFNOARG", "no arguments were given to the $PREFIX directive",
- "RDLOCMES", "reading local message file %s",
- "UNRECDIR", "unrecognised directive '%s'",
- NULL
-};
-
-const isc::log::MessageInitializer initializer(values);
-
-} // Anonymous namespace
-
diff --git a/src/lib/log/messagedef.h b/src/lib/log/messagedef.h
deleted file mode 100644
index eb8f4ea..0000000
--- a/src/lib/log/messagedef.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// File created from messagedef.mes on Mon Feb 14 11:07:45 2011
-
-#ifndef __MESSAGEDEF_H
-#define __MESSAGEDEF_H
-
-#include <log/message_types.h>
-
-namespace isc {
-namespace log {
-
-extern const isc::log::MessageID MSG_DUPLNS;
-extern const isc::log::MessageID MSG_DUPLPRFX;
-extern const isc::log::MessageID MSG_DUPMSGID;
-extern const isc::log::MessageID MSG_IDNOTFND;
-extern const isc::log::MessageID MSG_MSGRDERR;
-extern const isc::log::MessageID MSG_MSGWRTERR;
-extern const isc::log::MessageID MSG_NOMSGTXT;
-extern const isc::log::MessageID MSG_NSEXTRARG;
-extern const isc::log::MessageID MSG_NSINVARG;
-extern const isc::log::MessageID MSG_NSNOARG;
-extern const isc::log::MessageID MSG_OPNMSGIN;
-extern const isc::log::MessageID MSG_OPNMSGOUT;
-extern const isc::log::MessageID MSG_PRFEXTRARG;
-extern const isc::log::MessageID MSG_PRFINVARG;
-extern const isc::log::MessageID MSG_PRFNOARG;
-extern const isc::log::MessageID MSG_RDLOCMES;
-extern const isc::log::MessageID MSG_UNRECDIR;
-
-} // namespace log
-} // namespace isc
-
-#endif // __MESSAGEDEF_H
diff --git a/src/lib/log/messagedef.mes b/src/lib/log/messagedef.mes
deleted file mode 100644
index 55b3e7c..0000000
--- a/src/lib/log/messagedef.mes
+++ /dev/null
@@ -1,119 +0,0 @@
-# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
-
-$PREFIX MSG_
-$NAMESPACE isc::log
-
-# \brief Message Utility Message File
-#
-# This is the source of the set of messages generated by the message and logging
-# components. The associated .h and .cc files are created by hand from this
-# file though and are not built during the build process; this is to avoid the
-# chicken-and-egg situation where we need the files to build the message
-# compiler, yet we need the compiler to build the files.
-
-DUPMSGID duplicate message ID (%s) in compiled code
-+ Indicative of a programming error, when it started up, BIND10 detected that
-+ the given message ID had been registered by one or more modules. (All message
-+ IDs should be unique throughout BIND10.) This has no impact on the operation
-+ of the server other that erroneous messages may be logged. (When BIND10 loads
-+ the message IDs (and their associated text), if a duplicate ID is found it is
-+ discarded. However, when the module that supplied the duplicate ID logs that
-+ particular message, the text supplied by the module that added the original
-+ ID will be output - something that may bear no relation to the condition being
-+ logged.
-
-DUPLNS duplicate $NAMESPACE directive found
-+ When reading a message file, more than one $NAMESPACE directive was found. In
-+ this version of the code, such a condition is regarded as an error and the
-+ read will be abandonded.
-
-DUPLPRFX duplicate $PREFIX directive found
-+ When reading a message file, more than one $PREFIX directive was found. In
-+ this version of the code, such a condition is regarded as an error and the
-+ read will be abandonded.
-
-IDNOTFND could not replace message for '%s': no such message identification
-+ During start-up a local message file was read. A line with the listed
-+ message identification was found in the file, but the identification is not
-+ one contained in the compiled-in message dictionary. Either the message
-+ identification has been mis-spelled in the file, or the local file was used
-+ for an earlier version of the software and the message with that
-+ identification has been removed.
-+
-+ This message may appear a number of times in the file, once for every such
-+ unknown mnessage identification.
-
-MSGRDERR error reading from message file %s: %s
-+ The specified error was encountered reading from the named message file.
-
-MSGWRTERR error writing to %s: %s
-+ The specified error was encountered by the message compiler when writing to
-+ the named output file.
-
-NSEXTRARG $NAMESPACE directive has too many arguments
-+ The $NAMESPACE directive takes a single argument, a namespace in which all the
-+ generated symbol names are placed. This error is generated when the
-+ compiler finds a $NAMESPACE directive with more than one argument.
-
-NSINVARG $NAMESPACE directive has an invalid argument ('%s')
-+ The $NAMESPACE argument should be a valid C++ namespace. The reader does a
-+ cursory check on its validity, checking that the characters in the namspace
-+ are correct. The error is generated when the reader finds an invalid
-+ character. (Valid are alphanumeric characters, underscroes and colons.)
-
-NOMSGTXT a line containing a message ID ('%s') and nothing else was found
-+ Message definitions comprise lines starting with a message identification (a
-+ symbolic name for the message) and followed by the text of the message. This
-+ error is generated when a line is found in the message file that contains just
-+ the message identification and no text.
-
-NSNOARG no arguments were given to the $NAMESPACE directive
-+ The $NAMESPACE directive takes a single argument, a namespace in which all the
-+ generated symbol names are placed. This error is generated when the
-+ compiler finds a $NAMESPACE directive with no arguments.
-
-OPNMSGIN unable to open message file %s for input: %s
-+ The program was not able to open the specified input message file for the
-+ reason given.
-
-OPNMSGOUT unable to open %s for output: %s
-+ The program was not able to open the specified output file for the reason
-+ given.
-
-PRFEXTRARG $PREFIX directive has too many arguments
-+ The $PREFIX directive takes a single argument, a prefix to be added to the
-+ symbol names when a C++ .h file is created. This error is generated when the
-+ compiler finds a $PREFIX directive with more than one argument.
-
-PRFINVARG $PREFIX directive has an invalid argument ('%s')
-+ The $PREFIX argument is used in a symbol name in a C++ header file. As such,
-+ it must adhere to restrictions on C++ symbol names (e.g. may only contain
-+ alphanumeric characters or underscores, and may nor start with a digit). A
-+ $PREFIX directive was found with an argument (given in the message) that
-+ violates those restictions.
-
-PRFNOARG no arguments were given to the $PREFIX directive
-+ The $PREFIX directive takes a single argument, a prefix to be added to the
-+ symbol names when a C++ .h file is created. This error is generated when the
-+ compiler finds a $PREFIX directive with no arguments.
-
-RDLOCMES reading local message file %s
-+ This is an informational message output by BIND10 when it starts to read a
-+ local message file. (A local message file may replace the text of one of more
-+ messages; the ID of the message will not be changed though.)
-
-UNRECDIR unrecognised directive '%s'
-+ 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.
diff --git a/src/lib/log/output_option.cc b/src/lib/log/output_option.cc
new file mode 100644
index 0000000..f56efb9
--- /dev/null
+++ b/src/lib/log/output_option.cc
@@ -0,0 +1,55 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <boost/algorithm/string.hpp>
+
+#include <log/log_messages.h>
+#include <log/macros.h>
+#include <log/output_option.h>
+
+namespace isc {
+namespace log {
+
+OutputOption::Destination
+getDestination(const std::string& dest_str) {
+ if (boost::iequals(dest_str, "console")) {
+ return OutputOption::DEST_CONSOLE;
+ } else if (boost::iequals(dest_str, "file")) {
+ return OutputOption::DEST_FILE;
+ } else if (boost::iequals(dest_str, "syslog")) {
+ return OutputOption::DEST_SYSLOG;
+ } else {
+ Logger logger("log");
+ LOG_ERROR(logger, LOG_BAD_DESTINATION).arg(dest_str);
+ return OutputOption::DEST_CONSOLE;
+ }
+}
+
+OutputOption::Stream
+getStream(const std::string& stream_str) {
+ if (boost::iequals(stream_str, "stderr")) {
+ return OutputOption::STR_STDERR;
+ } else if (boost::iequals(stream_str, "stdout")) {
+ return OutputOption::STR_STDOUT;
+ } else {
+ Logger logger("log");
+ LOG_ERROR(logger, LOG_BAD_STREAM).arg(stream_str);
+ return OutputOption::STR_STDOUT;
+ }
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/output_option.h b/src/lib/log/output_option.h
new file mode 100644
index 0000000..cbb7e95
--- /dev/null
+++ b/src/lib/log/output_option.h
@@ -0,0 +1,85 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __OUTPUT_OPTION_H
+#define __OUTPUT_OPTION_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string>
+
+/// \brief Logger Output Option
+///
+/// The logging configuration options are a list of logger specifications, each
+/// with one or more output options. This class represents an output option;
+/// one or more of these are attached to a LoggerSpecification object which is
+/// then passed to the LoggerManager to configure the logger.
+///
+/// Although there are three distinct output types (console, file, syslog) and
+/// the options for each do not really overlap. Although it is tempting to
+/// define a base OutputOption class and derive a class for each type
+/// (ConsoleOutputOptions etc.), it would be messy to use in practice. At
+/// some point the exact class would have to be known to get the class-specific
+/// options and the (pointer to) the base class cast to the appropriate type.
+/// Instead, this "struct" contains the union of all output options; it is up
+/// to the caller to cherry-pick the members it needs.
+///
+/// One final note: this object holds data and does no computation. For this
+/// reason, it is a "struct" and members are accessed directly instead of
+/// through methods.
+
+namespace isc {
+namespace log {
+
+struct OutputOption {
+
+ /// Destinations. Prefixed "DEST_" to avoid problems with the C stdio.h
+ /// FILE type.
+ typedef enum {
+ DEST_CONSOLE = 0,
+ DEST_FILE = 1,
+ DEST_SYSLOG = 2
+ } Destination;
+
+ /// If console, stream on which messages are output
+ typedef enum {
+ STR_STDOUT = 1,
+ STR_STDERR = 2
+ } Stream;
+
+ /// \brief Constructor
+ OutputOption() : destination(DEST_CONSOLE), stream(STR_STDERR),
+ flush(false), facility("LOCAL0"), filename(""),
+ maxsize(0), maxver(0)
+ {}
+
+ /// Members.
+
+ Destination destination; ///< Where the output should go
+ Stream stream; ///< stdout/stderr if console output
+ bool flush; ///< true to flush after each message
+ std::string facility; ///< syslog facility
+ std::string filename; ///< Filename if file output
+ size_t maxsize; ///< 0 if no maximum size
+ unsigned int maxver; ///< Maximum versions (none if <= 0)
+};
+
+OutputOption::Destination getDestination(const std::string& dest_str);
+OutputOption::Stream getStream(const std::string& stream_str);
+
+
+} // namespace log
+} // namespace isc
+
+#endif // __OUTPUT_OPTION_H
diff --git a/src/lib/log/root_logger_name.cc b/src/lib/log/root_logger_name.cc
deleted file mode 100644
index 58d9407..0000000
--- a/src/lib/log/root_logger_name.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <string>
-#include <root_logger_name.h>
-
-namespace isc {
-namespace log {
-
-namespace {
-
-// Obtain the root logger name in a way that is safe for statically-initialized
-// objects.
-
-std::string&
-getRootLoggerNameInternal() {
- static std::string root_name;
- return (root_name);
-}
-
-} // Anonymous namespace
-
-void
-setRootLoggerName(const std::string& name) {
- getRootLoggerNameInternal() = name;
-}
-
-const std::string& getRootLoggerName() {
- return (getRootLoggerNameInternal());
-}
-
-} // namespace log
-} // namespace isc
diff --git a/src/lib/log/root_logger_name.h b/src/lib/log/root_logger_name.h
deleted file mode 100644
index 9d50332..0000000
--- a/src/lib/log/root_logger_name.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __ROOT_LOGGER_NAME_H
-#define __ROOT_LOGGER_NAME_H
-
-#include <string>
-
-/// \brief Define Name of Root Logger
-///
-/// In BIND-10, the name root logger of a program is the name of the program
-/// itself (in contrast to packages such as log4cxx where the root logger name
-// is something like "."). These trivial functions allow the setting and
-// getting of that name by the logger classes.
-
-namespace isc {
-namespace log {
-
-/// \brief Set Root Logger Name
-///
-/// This function should be called by the program's initialization code before
-/// any logging functions are called.
-///
-/// \param name Name of the root logger. This should be the program name.
-void setRootLoggerName(const std::string& name);
-
-/// \brief Get Root Logger Name
-///
-/// \return Name of the root logger.
-const std::string& getRootLoggerName();
-
-}
-}
-
-#endif // __ROOT_LOGGER_NAME_H
diff --git a/src/lib/log/strutil.cc b/src/lib/log/strutil.cc
deleted file mode 100644
index 65fb0cd..0000000
--- a/src/lib/log/strutil.cc
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <numeric>
-
-#include <string.h>
-#include <strutil.h>
-
-using namespace std;
-
-namespace isc {
-namespace strutil {
-
-// Normalize slashes
-
-void
-normalizeSlash(std::string& name) {
- if (!name.empty()) {
- size_t pos = 0;
- while ((pos = name.find('\\', pos)) != std::string::npos) {
- name[pos] = '/';
- }
- }
-}
-
-// Trim String
-
-string
-trim(const string& instring) {
- static const char* blanks = " \t\n";
-
- string retstring = "";
- if (!instring.empty()) {
-
- // Search for first non-blank character in the string
- size_t first = instring.find_first_not_of(blanks);
- if (first != string::npos) {
-
- // String not all blanks, so look for last character
- size_t last = instring.find_last_not_of(blanks);
-
- // Extract the trimmed substring
- retstring = instring.substr(first, (last - first + 1));
- }
- }
-
- return (retstring);
-}
-
-// Tokenise string. As noted in the header, this is locally written to avoid
-// another dependency on a Boost library.
-
-vector<string>
-tokens(const std::string& text, const std::string& delim) {
- vector<string> result;
-
- // Search for the first non-delimiter character
- size_t start = text.find_first_not_of(delim);
- while (start != string::npos) {
-
- // Non-delimiter found, look for next delimiter
- size_t end = text.find_first_of(delim, start);
- if (end != string::npos) {
-
- // Delimiter found, so extract string & search for start of next
- // non-delimiter segment.
- result.push_back(text.substr(start, (end - start)));
- start = text.find_first_not_of(delim, end);
-
- } else {
-
- // End of string found, extract rest of string and flag to exit
- result.push_back(text.substr(start));
- start = string::npos;
- }
- }
-
- return (result);
-}
-
-// Local function to pass to accumulate() for summing up string lengths.
-
-namespace {
-
-size_t
-lengthSum(string::size_type curlen, const string& cur_string) {
- return (curlen + cur_string.size());
-}
-
-}
-
-// Provide printf-style formatting.
-
-std::string
-format(const std::string& format, const std::vector<std::string>& args) {
-
- static const string flag = "%s";
-
- // Initialize return string. To speed things up, we'll reserve an
- // appropriate amount of space - current string size, plus length of all
- // the argument strings, less two characters for each argument (the %s in
- // the format string is being replaced).
- string result;
- size_t length = accumulate(args.begin(), args.end(), format.size(),
- lengthSum) - (args.size() * flag.size());
- result.reserve(length);
-
- // Iterate through replacing all tokens
- result = format;
- size_t tokenpos = 0; // Position of last token replaced
- int i = 0; // Index into argument array
-
- while ((i < args.size()) && (tokenpos != string::npos)) {
- tokenpos = result.find(flag, tokenpos);
- if (tokenpos != string::npos) {
- result.replace(tokenpos, flag.size(), args[i++]);
- }
- }
-
- return (result);
-}
-
-} // namespace log
-} // namespace isc
diff --git a/src/lib/log/strutil.h b/src/lib/log/strutil.h
deleted file mode 100644
index f44b0d0..0000000
--- a/src/lib/log/strutil.h
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __STRUTIL_H
-#define __STRUTIL_H
-
-#include <algorithm>
-#include <cctype>
-#include <string>
-#include <vector>
-
-namespace isc {
-namespace strutil {
-
-/// \brief A Set of C++ Utilities for Manipulating Strings
-
-/// \brief Normalize Backslash
-///
-/// Only relevant to Windows, this replaces all "\" in a string with "/" and
-/// returns the result. On other systems it is a no-op. Note that Windows does
-/// recognise file names with the "\" replaced by "/" (at least in system calls,
-/// if not the command line).
-///
-/// \param name Name to be substituted
-void normalizeSlash(std::string& name);
-
-
-/// \brief Trim Leading and Trailing Spaces
-///
-/// Returns a copy of the input string but with any leading or trailing spaces
-/// or tabs removed.
-///
-/// \param instring Input string to modify
-///
-/// \return String with leading and trailing spaces removed
-std::string trim(const std::string& instring);
-
-
-/// \brief Split String into Tokens
-///
-/// Splits a string into tokens (the tokens being delimited by one or more of
-/// the delimiter characters) and returns the tokens in a vector array. Note
-/// that adjacent delimiters are considered to be a single delimiter.
-///
-/// Special cases are:
-/// -# The empty string is considered to be zero tokens.
-/// -# A string comprising nothing but delimiters is considered to be zero
-/// tokens.
-///
-/// The reasoning behind this is that the string can be thought of as having
-/// invisible leading and trailing delimiter characters. Therefore both cases
-/// reduce to a set of contiguous delimiters, which are considered a single
-/// delimiter (so getting rid of the string).
-///
-/// We could use Boost for this, but this (simple) function eliminates one
-/// dependency in the code.
-///
-/// \param text String to be split. Passed by value as the internal copy is
-/// altered during the processing.
-/// \param delim Delimiter characters
-///
-/// \return Vector of tokens.
-std::vector<std::string> tokens(const std::string& text,
- const std::string& delim = std::string(" \t\n"));
-
-
-/// \brief Uppercase Character
-///
-/// Used in uppercase() to pass as an argument to std::transform(). The
-/// function std::toupper() can't be used as it takes an "int" as its argument;
-/// this confuses the template expansion mechanism because defererencing a
-/// string::iterator returns a char.
-///
-/// \param chr Character to be upper-cased.
-///
-/// \return Uppercase version of the argument
-inline char toUpper(char chr) {
- return (static_cast<char>(std::toupper(static_cast<int>(chr))));
-}
-
-
-/// \brief Uppercase String
-///
-/// A convenience function to uppercase a string.
-///
-/// \param text String to be upper-cased.
-inline void uppercase(std::string& text) {
- std::transform(text.begin(), text.end(), text.begin(),
- isc::strutil::toUpper);
-}
-
-/// \brief Lowercase Character
-///
-/// Used in lowercase() to pass as an argument to std::transform(). The
-/// function std::tolower() can't be used as it takes an "int" as its argument;
-/// this confuses the template expansion mechanism because defererencing a
-/// string::iterator returns a char.
-///
-/// \param chr Character to be lower-cased.
-///
-/// \return Lowercase version of the argument
-inline char toLower(char chr) {
- return (static_cast<char>(std::tolower(static_cast<int>(chr))));
-}
-
-/// \brief Lowercase String
-///
-/// A convenience function to lowercase a string
-///
-/// \param text String to be lower-cased.
-inline void lowercase(std::string& text) {
- std::transform(text.begin(), text.end(), text.begin(),
- isc::strutil::toLower);
-}
-
-
-/// \brief Apply Formatting
-///
-/// Given a printf-style format string containing only "%s" place holders
-/// (others are ignored) and a vector of strings, this produces a single string
-/// with the placeholders replaced.
-///
-/// \param format Format string
-/// \param args Vector of argument strings
-///
-/// \return Resultant string
-std::string format(const std::string& format,
- const std::vector<std::string>& args);
-
-
-} // namespace strutil
-} // namespace isc
-
-#endif // __STRUTIL_H
diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am
index 1845706..069a7b4 100644
--- a/src/lib/log/tests/Makefile.am
+++ b/src/lib/log/tests/Makefile.am
@@ -2,7 +2,6 @@ SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
@@ -14,32 +13,64 @@ CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
TESTS += run_unittests
-run_unittests_SOURCES = root_logger_name_unittest.cc
-run_unittests_SOURCES += filename_unittest.cc
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += log_formatter_unittest.cc
+run_unittests_SOURCES += logger_level_impl_unittest.cc
+run_unittests_SOURCES += logger_level_unittest.cc
+run_unittests_SOURCES += logger_manager_unittest.cc
+run_unittests_SOURCES += logger_name_unittest.cc
+run_unittests_SOURCES += logger_support_unittest.cc
run_unittests_SOURCES += logger_unittest.cc
+run_unittests_SOURCES += logger_specification_unittest.cc
run_unittests_SOURCES += message_dictionary_unittest.cc
-run_unittests_SOURCES += message_reader_unittest.cc
-run_unittests_SOURCES += message_initializer_unittest.cc
run_unittests_SOURCES += message_initializer_unittest_2.cc
-run_unittests_SOURCES += strutil_unittest.cc
-run_unittests_SOURCES += run_unittests.cc
+run_unittests_SOURCES += message_initializer_unittest.cc
+run_unittests_SOURCES += message_reader_unittest.cc
+run_unittests_SOURCES += output_option_unittest.cc
-run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_CLANGPP
+# This is to workaround unused variables tcout and tcerr in
+# log4cplus's streams.h.
+run_unittests_CXXFLAGS += -Wno-unused-variable
+endif
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
-TESTS += logger_support_test
-logger_support_test_SOURCES = logger_support_test.cc
-logger_support_test_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-logger_support_test_LDFLAGS = $(AM_LDFLAGS)
-logger_support_test_LDADD = $(top_builddir)/src/lib/log/liblog.la
+check_PROGRAMS = logger_example
+logger_example_SOURCES = logger_example.cc
+logger_example_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+logger_example_LDFLAGS = $(AM_LDFLAGS) $(LOG4CPLUS_LDFLAGS)
+logger_example_LDADD = $(top_builddir)/src/lib/log/liblog.la
+logger_example_LDADD += $(top_builddir)/src/lib/util/libutil.la
+logger_example_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+
+check_PROGRAMS += init_logger_test
+init_logger_test_SOURCES = init_logger_test.cc
+init_logger_test_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+init_logger_test_LDFLAGS = $(AM_LDFLAGS) $(LOG4CPLUS_LDFLAGS)
+init_logger_test_LDADD = $(top_builddir)/src/lib/log/liblog.la
+init_logger_test_LDADD += $(top_builddir)/src/lib/util/libutil.la
+init_logger_test_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
noinst_PROGRAMS = $(TESTS)
-# Additional test using the shell
-PYTESTS = run_time_init_test.sh
+# Additional test using the shell. These are principally tests
+# where the global logging environment is affected, and where the
+# output needs to be compared with stored output (where "cut" and
+# "diff" are useful utilities).
+
check-local:
- $(SHELL) $(abs_builddir)/run_time_init_test.sh
+ $(SHELL) $(abs_builddir)/console_test.sh
+ $(SHELL) $(abs_builddir)/destination_test.sh
+ $(SHELL) $(abs_builddir)/init_logger_test.sh
+ $(SHELL) $(abs_builddir)/local_file_test.sh
+ $(SHELL) $(abs_builddir)/severity_test.sh
diff --git a/src/lib/log/tests/console_test.sh.in b/src/lib/log/tests/console_test.sh.in
new file mode 100755
index 0000000..a16dc23
--- /dev/null
+++ b/src/lib/log/tests/console_test.sh.in
@@ -0,0 +1,67 @@
+#!/bin/sh
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# The logger supports the idea of a "console" logger than logs to either stdout
+# or stderr. This test checks that both these options work.
+
+testname="Console output test"
+echo $testname
+
+failcount=0
+tempfile=@abs_builddir@/console_test_tempfile_$$
+
+# Look at tempfile and check that the count equals the expected count
+passfail() {
+ count=`wc -l $tempfile | awk '{print $1}'`
+ if [ $count -eq $1 ]; then
+ echo " pass"
+ else
+ echo " FAIL"
+ failcount=`expr $failcount + $1`
+ fi
+}
+
+echo -n "1. Checking that console output to stdout goes to stdout:"
+rm -f $tempfile
+./logger_example -c stdout -s error 1> $tempfile 2> /dev/null
+passfail 4
+
+echo -n "2. Checking that console output to stdout does not go to stderr:"
+rm -f $tempfile
+./logger_example -c stdout -s error 1> /dev/null 2> $tempfile
+passfail 0
+
+echo -n "3. Checking that console output to stderr goes to stderr:"
+rm -f $tempfile
+./logger_example -c stderr -s error 1> /dev/null 2> $tempfile
+passfail 4
+
+echo -n "4. Checking that console output to stderr does not go to stdout:"
+rm -f $tempfile
+./logger_example -c stderr -s error 1> $tempfile 2> /dev/null
+passfail 0
+
+if [ $failcount -eq 0 ]; then
+ echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+ echo "FAIL: $testname - 1 test failed"
+else
+ echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up
+rm -f $tempfile
+
+exit $failcount
diff --git a/src/lib/log/tests/destination_test.sh.in b/src/lib/log/tests/destination_test.sh.in
new file mode 100755
index 0000000..1cfb9fb
--- /dev/null
+++ b/src/lib/log/tests/destination_test.sh.in
@@ -0,0 +1,91 @@
+#!/bin/sh
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# Checks that the logger will route messages to the chosen destination.
+
+testname="Destination test"
+echo $testname
+
+failcount=0
+tempfile=@abs_builddir@/destination_test_tempfile_$$
+destfile1=@abs_builddir@/destination_test_destfile_1_$$
+destfile2=@abs_builddir@/destination_test_destfile_2_$$
+
+passfail() {
+ if [ $1 -eq 0 ]; then
+ echo " pass"
+ else
+ echo " FAIL"
+ failcount=`expr $failcount + $1`
+ fi
+}
+
+echo "1. One logger, multiple destinations:"
+cat > $tempfile << .
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+.
+rm -f $destfile1 $destfile2
+./logger_example -s error -f $destfile1 -f $destfile2
+
+echo -n " - destination 1:"
+cut -d' ' -f3- $destfile1 | diff $tempfile -
+passfail $?
+
+echo -n " - destination 2:"
+cut -d' ' -f3- $destfile2 | diff $tempfile -
+passfail $?
+
+echo "2. Two loggers, different destinations and severities"
+rm -f $destfile1 $destfile2
+./logger_example -l example -s info -f $destfile1 -l alpha -s warn -f $destfile2
+
+# All output for example and example.beta should have gone to destfile1.
+# Output for example.alpha should have done to destfile2.
+
+cat > $tempfile << .
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+WARN [example] LOG_BAD_STREAM bad log console output stream: example
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+WARN [example.beta] LOG_BAD_STREAM bad log console output stream: beta_warn
+INFO [example.beta] LOG_READ_ERROR error reading from message file beta: info
+.
+echo -n " - destination 1:"
+cut -d' ' -f3- $destfile1 | diff $tempfile -
+passfail $?
+
+echo -n " - destination 2:"
+cat > $tempfile << .
+WARN [example.alpha] LOG_READ_ERROR error reading from message file a.txt: dummy reason
+.
+cut -d' ' -f3- $destfile2 | diff $tempfile -
+passfail $?
+
+if [ $failcount -eq 0 ]; then
+ echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+ echo "FAIL: $testname - 1 test failed"
+else
+ echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up.
+rm -f $tempfile $destfile1 $destfile2
+
+exit $failcount
diff --git a/src/lib/log/tests/filename_unittest.cc b/src/lib/log/tests/filename_unittest.cc
deleted file mode 100644
index f3032eb..0000000
--- a/src/lib/log/tests/filename_unittest.cc
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <string>
-
-#include <gtest/gtest.h>
-
-#include <log/filename.h>
-
-using namespace isc;
-using namespace isc::log;
-using namespace std;
-
-class FilenameTest : public ::testing::Test {
-protected:
- FilenameTest()
- {
- }
-};
-
-
-// Check that the name can be changed
-
-TEST_F(FilenameTest, SetName) {
- Filename fname("/a/b/c.d");
- EXPECT_EQ("/a/b/c.d", fname.fullName());
-
- fname.setName("test.txt");
- EXPECT_EQ("test.txt", fname.fullName());
-}
-
-
-// Check that the components are split correctly. This is a check of the
-// private member split() method.
-
-TEST_F(FilenameTest, Components) {
-
- // Complete name
- Filename fname("/alpha/beta/gamma.delta");
- EXPECT_EQ("/alpha/beta/", fname.directory());
- EXPECT_EQ("gamma", fname.name());
- EXPECT_EQ(".delta", fname.extension());
-
- // Directory only
- fname.setName("/gamma/delta/");
- EXPECT_EQ("/gamma/delta/", fname.directory());
- EXPECT_EQ("", fname.name());
- EXPECT_EQ("", fname.extension());
-
- // Filename only
- fname.setName("epsilon");
- EXPECT_EQ("", fname.directory());
- EXPECT_EQ("epsilon", fname.name());
- EXPECT_EQ("", fname.extension());
-
- // Extension only
- fname.setName(".zeta");
- EXPECT_EQ("", fname.directory());
- EXPECT_EQ("", fname.name());
- EXPECT_EQ(".zeta", fname.extension());
-
- // Missing directory
- fname.setName("eta.theta");
- EXPECT_EQ("", fname.directory());
- EXPECT_EQ("eta", fname.name());
- EXPECT_EQ(".theta", fname.extension());
-
- // Missing filename
- fname.setName("/iota/.kappa");
- EXPECT_EQ("/iota/", fname.directory());
- EXPECT_EQ("", fname.name());
- EXPECT_EQ(".kappa", fname.extension());
-
- // Missing extension
- fname.setName("lambda/mu/nu");
- EXPECT_EQ("lambda/mu/", fname.directory());
- EXPECT_EQ("nu", fname.name());
- EXPECT_EQ("", fname.extension());
-
- // Check that the decomposition can occur in the presence of leading and
- // trailing spaces
- fname.setName(" lambda/mu/nu\t ");
- EXPECT_EQ("lambda/mu/", fname.directory());
- EXPECT_EQ("nu", fname.name());
- EXPECT_EQ("", fname.extension());
-
- // Empty string
- fname.setName("");
- EXPECT_EQ("", fname.directory());
- EXPECT_EQ("", fname.name());
- EXPECT_EQ("", fname.extension());
-
- // ... and just spaces
- fname.setName(" ");
- EXPECT_EQ("", fname.directory());
- EXPECT_EQ("", fname.name());
- EXPECT_EQ("", fname.extension());
-
- // Check corner cases - where separators are present, but strings are
- // absent.
- fname.setName("/");
- EXPECT_EQ("/", fname.directory());
- EXPECT_EQ("", fname.name());
- EXPECT_EQ("", fname.extension());
-
- fname.setName(".");
- EXPECT_EQ("", fname.directory());
- EXPECT_EQ("", fname.name());
- EXPECT_EQ(".", fname.extension());
-
- fname.setName("/.");
- EXPECT_EQ("/", fname.directory());
- EXPECT_EQ("", fname.name());
- EXPECT_EQ(".", fname.extension());
-
- // Note that the space is a valid filename here; only leading and trailing
- // spaces should be trimmed.
- fname.setName("/ .");
- EXPECT_EQ("/", fname.directory());
- EXPECT_EQ(" ", fname.name());
- EXPECT_EQ(".", fname.extension());
-
- fname.setName(" / . ");
- EXPECT_EQ("/", fname.directory());
- EXPECT_EQ(" ", fname.name());
- EXPECT_EQ(".", fname.extension());
-}
-
-// Check that the expansion with a default works.
-
-TEST_F(FilenameTest, ExpandWithDefault) {
- Filename fname("a.b");
-
- // These tests also check that the trimming of the default component is
- // done properly.
- EXPECT_EQ("/c/d/a.b", fname.expandWithDefault(" /c/d/ "));
- EXPECT_EQ("/c/d/a.b", fname.expandWithDefault("/c/d/e.f"));
- EXPECT_EQ("a.b", fname.expandWithDefault("e.f"));
-
- fname.setName("/a/b/c");
- EXPECT_EQ("/a/b/c.d", fname.expandWithDefault(".d"));
- EXPECT_EQ("/a/b/c.d", fname.expandWithDefault("x.d"));
- EXPECT_EQ("/a/b/c.d", fname.expandWithDefault("/s/t/u.d"));
- EXPECT_EQ("/a/b/c", fname.expandWithDefault("/s/t/u"));
-
- fname.setName(".h");
- EXPECT_EQ("/a/b/c.h", fname.expandWithDefault("/a/b/c.msg"));
-}
-
-// Check that we can use this as a default in expanding a filename
-
-TEST_F(FilenameTest, UseAsDefault) {
-
- Filename fname("a.b");
-
- // These tests also check that the trimming of the default component is
- // done properly.
- EXPECT_EQ("/c/d/a.b", fname.useAsDefault(" /c/d/ "));
- EXPECT_EQ("/c/d/e.f", fname.useAsDefault("/c/d/e.f"));
- EXPECT_EQ("e.f", fname.useAsDefault("e.f"));
-
- fname.setName("/a/b/c");
- EXPECT_EQ("/a/b/c.d", fname.useAsDefault(".d"));
- EXPECT_EQ("/a/b/x.d", fname.useAsDefault("x.d"));
- EXPECT_EQ("/s/t/u.d", fname.useAsDefault("/s/t/u.d"));
- EXPECT_EQ("/s/t/u", fname.useAsDefault("/s/t/u"));
- EXPECT_EQ("/a/b/c", fname.useAsDefault(""));
-}
diff --git a/src/lib/log/tests/init_logger_test.cc b/src/lib/log/tests/init_logger_test.cc
new file mode 100644
index 0000000..104c078
--- /dev/null
+++ b/src/lib/log/tests/init_logger_test.cc
@@ -0,0 +1,42 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <log/macros.h>
+#include <log/logger_support.h>
+#include <log/log_messages.h>
+
+using namespace isc::log;
+
+/// \brief Test InitLogger
+///
+/// A program used in testing the logger that initializes logging using
+/// initLogger(), then outputs several messages at different severities and
+/// debug levels. An external script sets the environment variables and checks
+/// that they have the desired effect.
+
+int
+main(int, char**) {
+ initLogger();
+ Logger logger("log");
+
+ LOG_DEBUG(logger, 0, LOG_BAD_DESTINATION).arg("debug-0");
+ LOG_DEBUG(logger, 50, LOG_BAD_DESTINATION).arg("debug-50");
+ LOG_DEBUG(logger, 99, LOG_BAD_DESTINATION).arg("debug-99");
+ LOG_INFO(logger, LOG_BAD_SEVERITY).arg("info");
+ LOG_WARN(logger, LOG_BAD_STREAM).arg("warn");
+ LOG_ERROR(logger, LOG_DUPLICATE_MESSAGE_ID).arg("error");
+ LOG_FATAL(logger, LOG_NO_MESSAGE_ID).arg("fatal");
+
+ return (0);
+}
diff --git a/src/lib/log/tests/init_logger_test.sh.in b/src/lib/log/tests/init_logger_test.sh.in
new file mode 100755
index 0000000..d26ca5d
--- /dev/null
+++ b/src/lib/log/tests/init_logger_test.sh.in
@@ -0,0 +1,110 @@
+#!/bin/sh
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# Checks that the initLogger() call uses for unit tests respects the setting of
+# the environment variables.
+
+testname="initLogger test"
+echo $testname
+
+failcount=0
+tempfile=@abs_builddir@/init_logger_test_tempfile_$$
+destfile=@abs_builddir@/init_logger_test_destfile_$$
+
+passfail() {
+ if [ $1 -eq 0 ]; then
+ echo " pass"
+ else
+ echo " FAIL"
+ failcount=`expr $failcount + $1`
+ fi
+}
+
+echo "1. Checking that B10_LOGGER_SEVERITY/B10_LOGGER_DBGLEVEL work"
+
+echo -n " - severity=DEBUG, dbglevel=99: "
+cat > $tempfile << .
+DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-0
+DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-50
+DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-99
+INFO [bind10.log] LOG_BAD_SEVERITY unrecognized log severity: info
+WARN [bind10.log] LOG_BAD_STREAM bad log console output stream: warn
+ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code
+FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
+.
+B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=99 ./init_logger_test 2>&1 | \
+ cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n " - severity=DEBUG, dbglevel=50: "
+cat > $tempfile << .
+DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-0
+DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-50
+INFO [bind10.log] LOG_BAD_SEVERITY unrecognized log severity: info
+WARN [bind10.log] LOG_BAD_STREAM bad log console output stream: warn
+ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code
+FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
+.
+B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=50 ./init_logger_test 2>&1 | \
+ cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n " - severity=WARN: "
+cat > $tempfile << .
+WARN [bind10.log] LOG_BAD_STREAM bad log console output stream: warn
+ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code
+FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
+.
+B10_LOGGER_SEVERITY=WARN ./init_logger_test 2>&1 | \
+ cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo "2. Checking that B10_LOGGER_DESTINATION works"
+
+echo -n " - stdout: "
+cat > $tempfile << .
+FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
+.
+rm -f $destfile
+B10_LOGGER_SEVERITY=FATAL B10_LOGGER_DESTINATION=stdout ./init_logger_test 1> $destfile
+cut -d' ' -f3- $destfile | diff $tempfile -
+passfail $?
+
+echo -n " - stderr: "
+rm -f $destfile
+B10_LOGGER_SEVERITY=FATAL B10_LOGGER_DESTINATION=stderr ./init_logger_test 2> $destfile
+cut -d' ' -f3- $destfile | diff $tempfile -
+passfail $?
+
+echo -n " - file: "
+rm -f $destfile
+B10_LOGGER_SEVERITY=FATAL B10_LOGGER_DESTINATION=$destfile ./init_logger_test
+cut -d' ' -f3- $destfile | diff $tempfile -
+passfail $?
+
+# Note: can't automatically test syslog output.
+
+if [ $failcount -eq 0 ]; then
+ echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+ echo "FAIL: $testname - 1 test failed"
+else
+ echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up.
+rm -f $tempfile $destfile
+
+exit $failcount
diff --git a/src/lib/log/tests/local_file_test.sh.in b/src/lib/log/tests/local_file_test.sh.in
new file mode 100755
index 0000000..9b898e6
--- /dev/null
+++ b/src/lib/log/tests/local_file_test.sh.in
@@ -0,0 +1,83 @@
+#!/bin/sh
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# Checks that a local message file can override the definitions in the message
+# dictionary.
+
+testname="Local message file test"
+echo $testname
+
+failcount=0
+localmes=@abs_builddir@/localdef_mes_$$
+tempfile=@abs_builddir@/run_time_init_test_tempfile_$$
+
+passfail() {
+ if [ $1 -eq 0 ]; then
+ echo " pass"
+ else
+ echo " FAIL"
+ failcount=`expr $failcount + $1`
+ fi
+}
+
+# Create the local message file for testing
+
+cat > $localmes << .
+% LOG_NOTHERE this message is not in the global dictionary
+% LOG_READ_ERROR replacement read error, parameters: '%1' and '%2'
+% LOG_READING_LOCAL_FILE replacement read local message file, parameter is '%1'
+.
+
+echo -n "1. Local message replacement:"
+cat > $tempfile << .
+WARN [example.log] LOG_NO_SUCH_MESSAGE could not replace message text for 'LOG_NOTHERE': no such message
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE replacement read local message file, parameter is 'dummy/file'
+WARN [example] LOG_BAD_STREAM bad log console output stream: example
+WARN [example.alpha] LOG_READ_ERROR replacement read error, parameters: 'a.txt' and 'dummy reason'
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+WARN [example.beta] LOG_BAD_STREAM bad log console output stream: beta_warn
+.
+./logger_example -c stdout -s warn $localmes | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "2. Report error if unable to read local message file:"
+cat > $tempfile << .
+ERROR [example.log] LOG_INPUT_OPEN_FAIL unable to open message file $localmes for input: No such file or directory
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+WARN [example] LOG_BAD_STREAM bad log console output stream: example
+WARN [example.alpha] LOG_READ_ERROR error reading from message file a.txt: dummy reason
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+WARN [example.beta] LOG_BAD_STREAM bad log console output stream: beta_warn
+.
+rm -f $localmes
+./logger_example -c stdout -s warn $localmes | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+if [ $failcount -eq 0 ]; then
+ echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+ echo "FAIL: $testname - 1 test failed"
+else
+ echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up.
+rm -f $tempfile
+
+exit $failcount
diff --git a/src/lib/log/tests/log_formatter_unittest.cc b/src/lib/log/tests/log_formatter_unittest.cc
new file mode 100644
index 0000000..b91665d
--- /dev/null
+++ b/src/lib/log/tests/log_formatter_unittest.cc
@@ -0,0 +1,126 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+#include <log/log_formatter.h>
+#include <log/logger_level.h>
+
+#include <vector>
+#include <string>
+
+using namespace std;
+
+namespace {
+
+class FormatterTest : public ::testing::Test {
+protected:
+ typedef pair<isc::log::Severity, string> Output;
+ typedef isc::log::Formatter<FormatterTest> Formatter;
+ vector<Output> outputs;
+public:
+ void output(const isc::log::Severity& prefix, const string& message) {
+ outputs.push_back(Output(prefix, message));
+ }
+ // Just shortcut for new string
+ string* s(const char* text) {
+ return (new string(text));
+ }
+};
+
+// Create an inactive formatter and check it doesn't produce any output
+TEST_F(FormatterTest, inactive) {
+ Formatter();
+ EXPECT_EQ(0, outputs.size());
+}
+
+// Create an active formatter and check it produces output. Does no arg
+// substitution yet
+TEST_F(FormatterTest, active) {
+ Formatter(isc::log::INFO, s("Text of message"), this);
+ ASSERT_EQ(1, outputs.size());
+ EXPECT_EQ(isc::log::INFO, outputs[0].first);
+ EXPECT_EQ("Text of message", outputs[0].second);
+}
+
+// No output even when we have an arg on the inactive formatter
+TEST_F(FormatterTest, inactiveArg) {
+ Formatter().arg("Hello");
+ EXPECT_EQ(0, outputs.size());
+}
+
+// Create an active formatter and replace a placeholder with string
+TEST_F(FormatterTest, stringArg) {
+ {
+ SCOPED_TRACE("C++ string");
+ Formatter(isc::log::INFO, s("Hello %1"), this).arg(string("World"));
+ ASSERT_EQ(1, outputs.size());
+ EXPECT_EQ(isc::log::INFO, outputs[0].first);
+ EXPECT_EQ("Hello World", outputs[0].second);
+ }
+ {
+ SCOPED_TRACE("C++ string");
+ Formatter(isc::log::INFO, s("Hello %1"), this).arg(string("Internet"));
+ ASSERT_EQ(2, outputs.size());
+ EXPECT_EQ(isc::log::INFO, outputs[1].first);
+ EXPECT_EQ("Hello Internet", outputs[1].second);
+ }
+}
+
+// Can convert to string
+TEST_F(FormatterTest, intArg) {
+ Formatter(isc::log::INFO, s("The answer is %1"), this).arg(42);
+ ASSERT_EQ(1, outputs.size());
+ EXPECT_EQ(isc::log::INFO, outputs[0].first);
+ EXPECT_EQ("The answer is 42", outputs[0].second);
+}
+
+// Can use multiple arguments at different places
+TEST_F(FormatterTest, multiArg) {
+ Formatter(isc::log::INFO, s("The %2 are %1"), this).arg("switched").
+ arg("arguments");
+ ASSERT_EQ(1, outputs.size());
+ EXPECT_EQ(isc::log::INFO, outputs[0].first);
+ EXPECT_EQ("The arguments are switched", outputs[0].second);
+}
+
+// Can survive and complains if placeholder is missing
+TEST_F(FormatterTest, missingPlace) {
+ EXPECT_NO_THROW(Formatter(isc::log::INFO, s("Missing the first %2"), this).
+ arg("missing").arg("argument"));
+ ASSERT_EQ(1, outputs.size());
+ EXPECT_EQ(isc::log::INFO, outputs[0].first);
+ EXPECT_EQ("Missing the first argument "
+ "@@Missing placeholder %1 for 'missing'@@", outputs[0].second);
+}
+
+// Can replace multiple placeholders
+TEST_F(FormatterTest, multiPlaceholder) {
+ Formatter(isc::log::INFO, s("The %1 is the %1"), this).
+ arg("first rule of tautology club");
+ ASSERT_EQ(1, outputs.size());
+ EXPECT_EQ(isc::log::INFO, outputs[0].first);
+ EXPECT_EQ("The first rule of tautology club is "
+ "the first rule of tautology club", outputs[0].second);
+}
+
+// Test we can cope with replacement containing the placeholder
+TEST_F(FormatterTest, noRecurse) {
+ // If we recurse, this will probably eat all the memory and crash
+ Formatter(isc::log::INFO, s("%1"), this).arg("%1 %1");
+ ASSERT_EQ(1, outputs.size());
+ EXPECT_EQ(isc::log::INFO, outputs[0].first);
+ EXPECT_EQ("%1 %1", outputs[0].second);
+}
+
+}
diff --git a/src/lib/log/tests/logger_example.cc b/src/lib/log/tests/logger_example.cc
new file mode 100644
index 0000000..2170066
--- /dev/null
+++ b/src/lib/log/tests/logger_example.cc
@@ -0,0 +1,305 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// \brief Example Program
+///
+/// Simple example program showing how to use the logger. The various
+/// command-line options let most aspects of the logger be exercised, so
+/// making this a useful tool for testing.
+///
+/// See the usage() method for details of use.
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <util/strutil.h>
+
+#include <log/logger.h>
+#include <log/logger_level.h>
+#include <log/logger_manager.h>
+#include <log/logger_name.h>
+#include <log/logger_specification.h>
+#include <log/macros.h>
+
+// Include a set of message definitions.
+#include <log/log_messages.h>
+
+using namespace isc::log;
+using namespace std;
+
+
+// Print usage information
+
+void usage() {
+ cout <<
+"logger_support_test [-h | [logger_spec] [[logger_spec]...]]\n"
+"\n"
+" -h Print this message and exit\n"
+"\n"
+"The rest of the command line comprises the set of logger specifications.\n"
+"Each specification is of the form:\n"
+"\n"
+" -l logger [-s severity] [-d dbglevel] output_spec] [[output_spec] ...\n"
+"\n"
+"where:\n"
+"\n"
+" -l logger Give the name of the logger to which the following\n"
+" output specifications will apply.\n"
+"\n"
+"Each logger is followed by the indication of the serverity it is logging\n"
+"and, if applicable, its debug level:\n"
+"\n"
+" -d dbglevel Debug level. Only interpreted if the severity is 'debug'\n"
+" this is a number between 0 and 99.\n"
+" -s severity Set the severity of messages output. 'severity' is one\n"
+" of 'debug', 'info', 'warn', 'error', 'fatal', the default\n"
+" being 'info'.\n"
+"\n"
+"The output specifications - there may be more than one per logger - detail\n"
+"the output streams attached to the logger. These are of the form:\n"
+"\n"
+" -c stream | -f file [-m maxver] [-z maxsize] | -y facility\n"
+"\n"
+"These are:\n"
+"\n"
+" -c stream Send output to the console. 'stream' is one of 'stdout'\n"
+" of 'stderr'.\n"
+" -f file Send output to specified file, appending to existing file\n"
+" if one exists.\n"
+" -y facility Send output to the syslog file with the given facility\n"
+" name (e.g. local1, cron etc.)\n"
+"\n"
+"The following can be specified for the file logger:\n"
+"\n"
+" -m maxver If file rolling is selected (by the maximum file size being\n"
+" non-zero), the maximum number of versions to keep (defaults\n"
+" to 0)\n"
+" -z maxsize Maximum size of the file before the file is closed and a\n"
+" new one opened. The default of 0 means no maximum size.\n"
+"\n"
+"If none of -c, -f or -y is given, by default, output is sent to stdout. If no\n"
+"logger is specified, the default is the program's root logger ('example').\n";
+
+}
+
+
+// The program sets the attributes on the root logger and logs a set of
+// messages. Looking at the output determines whether the program worked.
+
+int main(int argc, char** argv) {
+ const string ROOT_NAME = "example";
+
+ bool sw_found = false; // Set true if switch found
+ bool c_found = false; // Set true if "-c" found
+ bool f_found = false; // Set true if "-f" found
+ bool y_found = false; // Set true if "-y" found
+ int option; // For getopt() processing
+ OutputOption def_opt; // Default output option - used
+ // for initialization
+ LoggerSpecification cur_spec(ROOT_NAME);// Current specification
+ OutputOption cur_opt; // Current output option
+ vector<LoggerSpecification> loggers; // Set of logger specifications
+ vector<OutputOption> options; // Output options for logger
+ std::string severity; // Severity set for logger
+
+ // Initialize logging system - set the root logger name.
+ LoggerManager manager;
+ manager.init(ROOT_NAME);
+
+ // In the parsing loop that follows, the construction of the logging
+ // specification is always "one behind". In other words, the parsing of
+ // command-line options updates thge current logging specification/output
+ // options. When the flag indicating a new logger or output specification
+ // is encountered, the previous one is added to the list.
+ //
+ // One complication is that there is deemed to be a default active when
+ // the parsing starts (console output for the BIND 10 root logger). This
+ // is included in the logging specifications UNLESS the first switch on
+ // the command line is a "-l" flag starting a new logger. To track this,
+ // the "sw_found" flag is set when a switch is completey processed. The
+ // processing of "-l" will only add information for a previous logger to
+ // the list if this flag is set.
+ while ((option = getopt(argc, argv, "hc:d:f:l:m:s:y:z:")) != -1) {
+ switch (option) {
+ case 'c': // Console output
+ // New output spec. If one was currently active, add it to the
+ // list and reset the current output option to the defaults.
+ if (c_found || f_found || y_found) {
+ cur_spec.addOutputOption(cur_opt);
+ cur_opt = def_opt;
+ c_found = f_found = y_found = false;
+ }
+
+ // Set the output option for this switch.
+ c_found = true;
+ cur_opt.destination = OutputOption::DEST_CONSOLE;
+ if (strcmp(optarg, "stdout") == 0) {
+ cur_opt.stream = OutputOption::STR_STDOUT;
+
+ } else if (strcmp(optarg, "stderr") == 0) {
+ cur_opt.stream = OutputOption::STR_STDERR;
+
+ } else {
+ cerr << "Unrecognised console option: " << optarg << "\n";
+ return (1);
+ }
+ break;
+
+ case 'd': // Debug level
+ cur_spec.setDbglevel(boost::lexical_cast<int>(optarg));
+ break;
+
+ case 'f': // File output specification
+ // New output spec. If one was currently active, add it to the
+ // list and reset the current output option to the defaults.
+ if (c_found || f_found || y_found) {
+ cur_spec.addOutputOption(cur_opt);
+ cur_opt = def_opt;
+ c_found = f_found = y_found = false;
+ }
+
+ // Set the output option for this switch.
+ f_found = true;
+ cur_opt.destination = OutputOption::DEST_FILE;
+ cur_opt.filename = optarg;
+ break;
+
+ case 'h': // Help
+ usage();
+ return (0);
+
+ case 'l': // Logger
+ // If a current specification is active, add the last output option
+ // to it, add it to the list and reset. A specification is active
+ // if at least one switch has been previously found.
+ if (sw_found) {
+ cur_spec.addOutputOption(cur_opt);
+ loggers.push_back(cur_spec);
+ cur_spec.reset();
+ }
+
+ // Set the logger name
+ cur_spec.setName(std::string(optarg));
+
+ // Reset the output option to the default.
+ cur_opt = def_opt;
+
+ // Indicate nothing is found to prevent the console option (the
+ // default output option) being added to the output list if an
+ // output option is found.
+ c_found = f_found = y_found = false;
+ break;
+
+ case 'm': // Maximum file version
+ if (!f_found) {
+ std::cerr << "Attempt to set maximum version (-m) "
+ "outside of file output specification\n";
+ return (1);
+ }
+ try {
+ cur_opt.maxsize = boost::lexical_cast<unsigned int>(optarg);
+ } catch (boost::bad_lexical_cast&) {
+ std::cerr << "Maximum version (-m) argument must be a positive "
+ "integer\n";
+ return (1);
+ }
+ break;
+
+ case 's': // Severity
+ severity = optarg;
+ isc::util::str::uppercase(severity);
+ cur_spec.setSeverity(getSeverity(severity));
+ break;
+
+ case 'y': // Syslog output
+ // New output spec. If one was currently active, add it to the
+ // list and reset the current output option to the defaults.
+ if (c_found || f_found || y_found) {
+ cur_spec.addOutputOption(cur_opt);
+ cur_opt = def_opt;
+ c_found = f_found = y_found = false;
+ }
+ y_found = true;
+ cur_opt.destination = OutputOption::DEST_SYSLOG;
+ cur_opt.facility = optarg;
+ break;
+
+ case 'z': // Maximum size
+ if (! f_found) {
+ std::cerr << "Attempt to set file size (-z) "
+ "outside of file output specification\n";
+ return (1);
+ }
+ try {
+ cur_opt.maxsize = boost::lexical_cast<size_t>(optarg);
+ } catch (boost::bad_lexical_cast&) {
+ std::cerr << "File size (-z) argument must be a positive "
+ "integer\n";
+ return (1);
+ }
+ break;
+
+
+ default:
+ std::cerr << "Unrecognised option: " <<
+ static_cast<char>(option) << "\n";
+ return (1);
+ }
+
+ // Have found at least one command-line switch, so note the fact.
+ sw_found = true;
+ }
+
+ // Add the current (unfinished specification) to the list.
+ cur_spec.addOutputOption(cur_opt);
+ loggers.push_back(cur_spec);
+
+ // Set the logging options.
+ manager.process(loggers.begin(), loggers.end());
+
+ // Set the local file
+ if (optind < argc) {
+ LoggerManager::readLocalMessageFile(argv[optind]);
+ }
+
+ // Log a few messages to different loggers.
+ isc::log::Logger logger_ex(ROOT_NAME);
+ isc::log::Logger logger_alpha("alpha");
+ isc::log::Logger logger_beta("beta");
+
+ LOG_FATAL(logger_ex, LOG_WRITE_ERROR).arg("test1").arg("42");
+ LOG_ERROR(logger_ex, LOG_READING_LOCAL_FILE).arg("dummy/file");
+ LOG_WARN(logger_ex, LOG_BAD_STREAM).arg("example");
+ LOG_WARN(logger_alpha, LOG_READ_ERROR).arg("a.txt").arg("dummy reason");
+ LOG_INFO(logger_alpha, LOG_INPUT_OPEN_FAIL).arg("example.msg").arg("dummy reason");
+ LOG_DEBUG(logger_ex, 0, LOG_READING_LOCAL_FILE).arg("example/0");
+ LOG_DEBUG(logger_ex, 24, LOG_READING_LOCAL_FILE).arg("example/24");
+ LOG_DEBUG(logger_ex, 25, LOG_READING_LOCAL_FILE).arg("example/25");
+ LOG_DEBUG(logger_ex, 26, LOG_READING_LOCAL_FILE).arg("example/26");
+ LOG_FATAL(logger_beta, LOG_BAD_SEVERITY).arg("beta_fatal");
+ LOG_ERROR(logger_beta, LOG_BAD_DESTINATION).arg("beta_error");
+ LOG_WARN(logger_beta, LOG_BAD_STREAM).arg("beta_warn");
+ LOG_INFO(logger_beta, LOG_READ_ERROR).arg("beta").arg("info");
+ LOG_DEBUG(logger_beta, 25, LOG_BAD_SEVERITY).arg("beta/25");
+ LOG_DEBUG(logger_beta, 26, LOG_BAD_SEVERITY).arg("beta/26");
+
+ return (0);
+}
diff --git a/src/lib/log/tests/logger_impl_log4cxx_unittest.cc b/src/lib/log/tests/logger_impl_log4cxx_unittest.cc
deleted file mode 100644
index cab2678..0000000
--- a/src/lib/log/tests/logger_impl_log4cxx_unittest.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <iostream>
-#include <string>
-
-#include <gtest/gtest.h>
-
-#include <log/root_logger_name.h>
-#include <log/logger.h>
-#include <log/logger_impl.h>
-#include <log/messagedef.h>
-
-using namespace isc;
-using namespace isc::log;
-using namespace std;
-
-/// \brief Log4cxx Implementation Tests
-///
-/// Some tests of methods that are not directly tested by the logger unit tests
-/// (when the logger is configured to use log4cxx)
-
-namespace isc {
-namespace log {
-
-/// \brief Test Logger
-///
-/// This logger is a subclass of the logger implementation class under test, but
-/// makes protected methods public (for testing)
-
-class TestLoggerImpl : public LoggerImpl {
-public:
- /// \brief constructor
- TestLoggerImpl(const string& name) : LoggerImpl(name, true)
- {}
-
-
- /// \brief Conversion Between log4cxx Number and BIND-10 Severity
- Severity convertLevel(int value) {
- return (LoggerImpl::convertLevel(value));
- }
-};
-
-} // namespace log
-} // namespace isc
-
-
-class LoggerImplTest : public ::testing::Test {
-protected:
- LoggerImplTest()
- {
- }
-};
-
-// Test the number to severity conversion function
-
-TEST_F(LoggerImplTest, ConvertLevel) {
-
- // Create a logger
- RootLoggerName::setName("test3");
- TestLoggerImpl logger("alpha");
-
- // Basic 1:1
- EXPECT_EQ(isc::log::DEBUG, logger.convertLevel(log4cxx::Level::DEBUG_INT));
- EXPECT_EQ(isc::log::INFO, logger.convertLevel(log4cxx::Level::INFO_INT));
- EXPECT_EQ(isc::log::WARN, logger.convertLevel(log4cxx::Level::WARN_INT));
- EXPECT_EQ(isc::log::WARN, logger.convertLevel(log4cxx::Level::WARN_INT));
- EXPECT_EQ(isc::log::ERROR, logger.convertLevel(log4cxx::Level::ERROR_INT));
- EXPECT_EQ(isc::log::FATAL, logger.convertLevel(log4cxx::Level::FATAL_INT));
- EXPECT_EQ(isc::log::FATAL, logger.convertLevel(log4cxx::Level::FATAL_INT));
- EXPECT_EQ(isc::log::NONE, logger.convertLevel(log4cxx::Level::OFF_INT));
-
- // Now some debug levels
- EXPECT_EQ(isc::log::DEBUG,
- logger.convertLevel(log4cxx::Level::DEBUG_INT - 1));
- EXPECT_EQ(isc::log::DEBUG,
- logger.convertLevel(log4cxx::Level::DEBUG_INT - MAX_DEBUG_LEVEL));
- EXPECT_EQ(isc::log::DEBUG,
- logger.convertLevel(log4cxx::Level::DEBUG_INT - 2 * MAX_DEBUG_LEVEL));
-}
diff --git a/src/lib/log/tests/logger_level_impl_unittest.cc b/src/lib/log/tests/logger_level_impl_unittest.cc
new file mode 100644
index 0000000..0ded7f9
--- /dev/null
+++ b/src/lib/log/tests/logger_level_impl_unittest.cc
@@ -0,0 +1,171 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <string>
+
+#include <gtest/gtest.h>
+#include <boost/static_assert.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <log/logger_level_impl.h>
+#include <log4cplus/logger.h>
+
+using namespace isc::log;
+using namespace std;
+
+class LoggerLevelImplTest : public ::testing::Test {
+protected:
+ LoggerLevelImplTest()
+ {}
+
+ ~LoggerLevelImplTest()
+ {}
+};
+
+
+// Checks that the log4cplus and BIND 10 levels convert correctly
+TEST_F(LoggerLevelImplTest, DefaultConversionFromBind) {
+ log4cplus::LogLevel fatal =
+ LoggerLevelImpl::convertFromBindLevel(Level(FATAL));
+ EXPECT_EQ(log4cplus::FATAL_LOG_LEVEL, fatal);
+
+ log4cplus::LogLevel error =
+ LoggerLevelImpl::convertFromBindLevel(Level(ERROR));
+ EXPECT_EQ(log4cplus::ERROR_LOG_LEVEL, error);
+
+ log4cplus::LogLevel warn =
+ LoggerLevelImpl::convertFromBindLevel(Level(WARN));
+ EXPECT_EQ(log4cplus::WARN_LOG_LEVEL, warn);
+
+ log4cplus::LogLevel info =
+ LoggerLevelImpl::convertFromBindLevel(Level(INFO));
+ EXPECT_EQ(log4cplus::INFO_LOG_LEVEL, info);
+
+ log4cplus::LogLevel debug =
+ LoggerLevelImpl::convertFromBindLevel(Level(DEBUG));
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL, debug);
+}
+
+// Checks that the debug severity and level converts correctly
+TEST_F(LoggerLevelImplTest, DebugConversionFromBind) {
+ log4cplus::LogLevel debug0 =
+ LoggerLevelImpl::convertFromBindLevel(Level(DEBUG, 0));
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - 0, debug0);
+
+ log4cplus::LogLevel debug1 =
+ LoggerLevelImpl::convertFromBindLevel(Level(DEBUG, 1));
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - 1, debug1);
+
+ log4cplus::LogLevel debug99 =
+ LoggerLevelImpl::convertFromBindLevel(Level(DEBUG, 99));
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - 99, debug99);
+
+ // Out of range should be coerced to the nearest boundary
+ log4cplus::LogLevel debug_1 =
+ LoggerLevelImpl::convertFromBindLevel(Level(DEBUG, MIN_DEBUG_LEVEL - 1));
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL, debug_1);
+
+ log4cplus::LogLevel debug100 =
+ LoggerLevelImpl::convertFromBindLevel(Level(DEBUG, MAX_DEBUG_LEVEL + 1));
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - MAX_DEBUG_LEVEL, debug100);
+}
+
+// Do the checks the other way
+static void
+test_convert_to(const char* trace, isc::log::Severity severity, int dbglevel,
+ log4cplus::LogLevel level)
+{
+ SCOPED_TRACE(trace);
+ Level test = LoggerLevelImpl::convertToBindLevel(level);
+ EXPECT_EQ(severity, test.severity);
+ EXPECT_EQ(dbglevel, test.dbglevel);
+}
+
+TEST_F(LoggerLevelImplTest, ConversionToBind) {
+ test_convert_to("FATAL", FATAL, MIN_DEBUG_LEVEL, log4cplus::FATAL_LOG_LEVEL);
+ test_convert_to("ERROR", ERROR, MIN_DEBUG_LEVEL, log4cplus::ERROR_LOG_LEVEL);
+ test_convert_to("WARN", WARN , MIN_DEBUG_LEVEL, log4cplus::WARN_LOG_LEVEL);
+ test_convert_to("INFO", INFO , MIN_DEBUG_LEVEL, log4cplus::INFO_LOG_LEVEL);
+ test_convert_to("DEBUG", DEBUG, MIN_DEBUG_LEVEL, log4cplus::DEBUG_LOG_LEVEL);
+
+ test_convert_to("DEBUG0", DEBUG, MIN_DEBUG_LEVEL + 0,
+ (log4cplus::DEBUG_LOG_LEVEL));
+ test_convert_to("DEBUG1", DEBUG, MIN_DEBUG_LEVEL + 1,
+ (log4cplus::DEBUG_LOG_LEVEL - 1));
+ test_convert_to("DEBUG2", DEBUG, MIN_DEBUG_LEVEL + 2,
+ (log4cplus::DEBUG_LOG_LEVEL - 2));
+ test_convert_to("DEBUG99", DEBUG, MIN_DEBUG_LEVEL + 99,
+ (log4cplus::DEBUG_LOG_LEVEL - 99));
+
+ // ... and some invalid valid values
+ test_convert_to("DEBUG-1", INFO, MIN_DEBUG_LEVEL,
+ (log4cplus::DEBUG_LOG_LEVEL + 1));
+ BOOST_STATIC_ASSERT(MAX_DEBUG_LEVEL == 99);
+ test_convert_to("DEBUG+100", DEFAULT, 0,
+ (log4cplus::DEBUG_LOG_LEVEL - MAX_DEBUG_LEVEL - 1));
+}
+
+// Check that we can convert from a string to the new log4cplus levels
+TEST_F(LoggerLevelImplTest, FromString) {
+
+ // Test all valid values
+ for (int i = MIN_DEBUG_LEVEL; i <= MAX_DEBUG_LEVEL; ++i) {
+ std::string token = string("DEBUG") + boost::lexical_cast<std::string>(i);
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - i,
+ LoggerLevelImpl::logLevelFromString(token));
+ }
+
+ // ... in lowercase too
+ for (int i = MIN_DEBUG_LEVEL; i <= MAX_DEBUG_LEVEL; ++i) {
+ std::string token = string("debug") + boost::lexical_cast<std::string>(i);
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - i,
+ LoggerLevelImpl::logLevelFromString(token));
+ }
+
+ // A few below the minimum
+ for (int i = MIN_DEBUG_LEVEL - 5; i < MIN_DEBUG_LEVEL; ++i) {
+ std::string token = string("DEBUG") + boost::lexical_cast<std::string>(i);
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL, LoggerLevelImpl::logLevelFromString(token));
+ }
+
+ // ... and above the maximum
+ for (int i = MAX_DEBUG_LEVEL + 1; i < MAX_DEBUG_LEVEL + 5; ++i) {
+ std::string token = string("DEBUG") + boost::lexical_cast<std::string>(i);
+ EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - MAX_DEBUG_LEVEL,
+ LoggerLevelImpl::logLevelFromString(token));
+ }
+
+ // Invalid strings.
+ EXPECT_EQ(log4cplus::NOT_SET_LOG_LEVEL,
+ LoggerLevelImpl::logLevelFromString("DEBU"));
+ EXPECT_EQ(log4cplus::NOT_SET_LOG_LEVEL,
+ LoggerLevelImpl::logLevelFromString("unrecognised"));
+}
+
+// ... and check the conversion back again. All levels should convert to "DEBUG".
+TEST_F(LoggerLevelImplTest, ToString) {
+
+ for (int i = MIN_DEBUG_LEVEL; i <= MAX_DEBUG_LEVEL; ++i) {
+ EXPECT_EQ(std::string("DEBUG"),
+ LoggerLevelImpl::logLevelToString(log4cplus::DEBUG_LOG_LEVEL - i));
+ }
+
+ // ... and that out of range stuff returns an empty string.
+ EXPECT_EQ(std::string(),
+ LoggerLevelImpl::logLevelToString(log4cplus::DEBUG_LOG_LEVEL + 1));
+ EXPECT_EQ(std::string(),
+ LoggerLevelImpl::logLevelToString(
+ log4cplus::DEBUG_LOG_LEVEL - MAX_DEBUG_LEVEL - 100));
+}
diff --git a/src/lib/log/tests/logger_level_unittest.cc b/src/lib/log/tests/logger_level_unittest.cc
new file mode 100644
index 0000000..8c98091
--- /dev/null
+++ b/src/lib/log/tests/logger_level_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/logger.h>
+#include <log/logger_manager.h>
+#include <log/log_messages.h>
+#include <log/logger_name.h>
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+class LoggerLevelTest : public ::testing::Test {
+protected:
+ LoggerLevelTest() {
+ // Logger initialization is done in main()
+ }
+ ~LoggerLevelTest() {
+ LoggerManager::reset();
+ }
+};
+
+
+// Checks that the logger is named correctly.
+
+TEST_F(LoggerLevelTest, Creation) {
+
+ // Default
+ isc::log::Level level1;
+ EXPECT_EQ(isc::log::DEFAULT, level1.severity);
+ EXPECT_EQ(isc::log::MIN_DEBUG_LEVEL, level1.dbglevel);
+
+ // Single argument constructor.
+ isc::log::Level level2(isc::log::FATAL);
+ EXPECT_EQ(isc::log::FATAL, level2.severity);
+ EXPECT_EQ(isc::log::MIN_DEBUG_LEVEL, level2.dbglevel);
+
+ // Two-argument constructor
+ isc::log::Level level3(isc::log::DEBUG, 42);
+ EXPECT_EQ(isc::log::DEBUG, level3.severity);
+ EXPECT_EQ(42, level3.dbglevel);
+}
+
+TEST(LoggerLevel, getSeverity) {
+ EXPECT_EQ(DEBUG, getSeverity("DEBUG"));
+ EXPECT_EQ(DEBUG, getSeverity("debug"));
+ EXPECT_EQ(DEBUG, getSeverity("DeBuG"));
+ EXPECT_EQ(INFO, getSeverity("INFO"));
+ EXPECT_EQ(INFO, getSeverity("info"));
+ EXPECT_EQ(INFO, getSeverity("iNfO"));
+ EXPECT_EQ(WARN, getSeverity("WARN"));
+ EXPECT_EQ(WARN, getSeverity("warn"));
+ EXPECT_EQ(WARN, getSeverity("wARn"));
+ EXPECT_EQ(ERROR, getSeverity("ERROR"));
+ EXPECT_EQ(ERROR, getSeverity("error"));
+ EXPECT_EQ(ERROR, getSeverity("ERRoR"));
+ EXPECT_EQ(FATAL, getSeverity("FATAL"));
+ EXPECT_EQ(FATAL, getSeverity("fatal"));
+ EXPECT_EQ(FATAL, getSeverity("FAtaL"));
+
+ // bad values should default to stdout
+ EXPECT_EQ(INFO, getSeverity("some bad value"));
+ EXPECT_EQ(INFO, getSeverity(""));
+
+ LoggerManager::reset();
+}
diff --git a/src/lib/log/tests/logger_manager_unittest.cc b/src/lib/log/tests/logger_manager_unittest.cc
new file mode 100644
index 0000000..0bdfc74
--- /dev/null
+++ b/src/lib/log/tests/logger_manager_unittest.cc
@@ -0,0 +1,321 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <boost/scoped_array.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <log/macros.h>
+#include <log/log_messages.h>
+#include <log/logger.h>
+#include <log/logger_level.h>
+#include <log/logger_manager.h>
+#include <log/logger_specification.h>
+#include <log/output_option.h>
+
+#include "tempdir.h"
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+/// \brief LoggerManager Test
+class LoggerManagerTest : public ::testing::Test {
+public:
+ LoggerManagerTest() {
+ // Initialization of logging is done in main()
+ }
+
+ ~LoggerManagerTest() {
+ LoggerManager::reset();
+ }
+};
+
+
+
+// Convenience class to create the specification for the logger "filelogger",
+// which, as the name suggests, logs to a file. It remembers the file name and
+// deletes the file when instance of the class is destroyed.
+class SpecificationForFileLogger {
+public:
+
+ // Constructor - allocate file and create the specification object
+ SpecificationForFileLogger() : spec_(), name_(""), logname_("filelogger") {
+
+ // Set the output to a temporary file.
+ OutputOption option;
+ option.destination = OutputOption::DEST_FILE;
+ option.filename = name_ = createTempFilename();
+
+ // Set target output to the file logger. The defauls indicate
+ // INFO severity.
+ spec_.setName(logname_);
+ spec_.addOutputOption(option);
+ }
+
+ // Destructor, remove the file. This is only a test, so ignore failures
+ ~SpecificationForFileLogger() {
+ if (! name_.empty()) {
+ (void) unlink(name_.c_str());
+ }
+ }
+
+ // Return reference to the logging specification for this loggger
+ LoggerSpecification& getSpecification() {
+ return spec_;
+ }
+
+ // Return name of the logger
+ string getLoggerName() const {
+ return logname_;
+ }
+
+ // Return name of the file
+ string getFileName() const {
+ return name_;
+ }
+
+ // Create temporary filename
+ //
+ // The compiler warns against tmpnam() and suggests mkstemp instead.
+ // Unfortunately, this creates the filename and opens it. So we need to
+ // close and delete the file before returning the name. Also, the name
+ // is based on the template supplied and the name of the temporary
+ // directory may vary between systems. So translate TMPDIR and if that
+ // does not exist, use /tmp.
+ //
+ // \return Temporary file name
+ std::string createTempFilename() {
+ string filename = TEMP_DIR + "/bind10_logger_manager_test_XXXXXX";
+
+ // Copy into writeable storage for the call to mkstemp
+ boost::scoped_array<char> tname(new char[filename.size() + 1]);
+ strcpy(tname.get(), filename.c_str());
+
+ // Create file, close and delete it, and store the name for later.
+ // There is still a race condition here, albeit a small one.
+ int filenum = mkstemp(tname.get());
+ if (filenum == -1) {
+ isc_throw(Exception, "Unable to obtain unique filename");
+ }
+ close(filenum);
+
+ return (string(tname.get()));
+ }
+
+
+private:
+ LoggerSpecification spec_; // Specification for this file logger
+ string name_; // Name of the output file
+ string logname_; // Name of this logger
+};
+
+
+// Convenience function to read an output log file and check that each line
+// contains the expected message ID
+//
+// \param filename Name of the file to check
+// \param start Iterator pointing to first expected message ID
+// \param finish Iterator pointing to last expected message ID
+template <typename T>
+void checkFileContents(const std::string& filename, T start, T finish) {
+
+ // Access the file for input
+ ifstream infile(filename.c_str());
+ if (! infile.good()) {
+ FAIL() << "Unable to open the logging file " << filename;
+ }
+
+ // Iterate round the expected message IDs and check that they appear in
+ // the string.
+ string line; // Line read from the file
+
+ T i = start; // Iterator
+ getline(infile, line);
+ int lineno = 1;
+
+ while ((i != finish) && (infile.good())) {
+
+ // Check that the message ID appears in the line.
+ EXPECT_TRUE(line.find(string(*i)) != string::npos)
+ << "Expected to find " << string(*i) << " on line " << lineno
+ << " of logging file " << filename;
+
+ // Go for the next line
+ ++i;
+ getline(infile, line);
+ ++lineno;
+ }
+
+ // Why did the loop end?
+ EXPECT_TRUE(i == finish) << "Did not reach the end of the message ID list";
+ EXPECT_TRUE(infile.eof()) << "Did not reach the end of the logging file";
+
+ // File will close when the instream is deleted at the end of this
+ // function.
+}
+
+// Check that the logger correctly creates something logging to a file.
+TEST_F(LoggerManagerTest, FileLogger) {
+
+ // Create a specification for the file logger and use the manager to
+ // connect the "filelogger" logger to it.
+ SpecificationForFileLogger file_spec;
+
+ // For the first test, we want to check that the file is created
+ // if it does not already exist. So delete the temporary file before
+ // logging the first message.
+ unlink(file_spec.getFileName().c_str());
+
+ // Set up the file appenders.
+ LoggerManager manager;
+ manager.process(file_spec.getSpecification());
+
+ // Try logging to the file. Local scope is set to ensure that the logger
+ // is destroyed before we reset the global logging. We record what we
+ // put in the file for a later comparison.
+ vector<MessageID> ids;
+ {
+
+ // Scope-limit the logger to ensure it is destroyed after the brief
+ // check. This adds weight to the idea that the logger will not
+ // keep the file open.
+ Logger logger(file_spec.getLoggerName());
+
+ LOG_FATAL(logger, LOG_DUPLICATE_MESSAGE_ID).arg("test");
+ ids.push_back(LOG_DUPLICATE_MESSAGE_ID);
+
+ LOG_FATAL(logger, LOG_DUPLICATE_NAMESPACE).arg("test");
+ ids.push_back(LOG_DUPLICATE_NAMESPACE);
+ }
+ LoggerManager::reset();
+
+ // At this point, the output file should contain two lines with messages
+ // LOG_DUPLICATE_MESSAGE_ID and LOG_DUPLICATE_NAMESPACE messages - test this.
+ checkFileContents(file_spec.getFileName(), ids.begin(), ids.end());
+
+ // Re-open the file (we have to assume that it was closed when we
+ // reset the logger - there is no easy way to check) and check that
+ // new messages are appended to it. We use the alternative
+ // invocation of process() here to check it works.
+ vector<LoggerSpecification> spec(1, file_spec.getSpecification());
+ manager.process(spec.begin(), spec.end());
+
+ // Create a new instance of the logger and log three more messages.
+ Logger logger(file_spec.getLoggerName());
+
+ LOG_FATAL(logger, LOG_NO_SUCH_MESSAGE).arg("test");
+ ids.push_back(LOG_NO_SUCH_MESSAGE);
+
+ LOG_FATAL(logger, LOG_INVALID_MESSAGE_ID).arg("test").arg("test2");
+ ids.push_back(LOG_INVALID_MESSAGE_ID);
+
+ LOG_FATAL(logger, LOG_NO_MESSAGE_ID).arg("42");
+ ids.push_back(LOG_NO_MESSAGE_ID);
+
+ // Close the file and check again
+ LoggerManager::reset();
+ checkFileContents(file_spec.getFileName(), ids.begin(), ids.end());
+}
+
+// Check if the file rolls over when it gets above a certain size.
+TEST_F(LoggerManagerTest, FileSizeRollover) {
+ // Set to a suitable minimum that log4cplus can copy with
+ static const size_t SIZE_LIMIT = 204800;
+
+ // Set up the name of the file.
+ SpecificationForFileLogger file_spec;
+ LoggerSpecification& spec = file_spec.getSpecification();
+
+ // Expand the option to ensure that a maximum version size is set.
+ LoggerSpecification::iterator opt = spec.begin();
+ EXPECT_TRUE(opt != spec.end());
+ opt->maxsize = SIZE_LIMIT; // Bytes
+ opt->maxver = 2;
+
+ // The current current output file does not exist (the creation of file_spec
+ // ensures that. Check that previous versions don't either.
+ vector<string> prev_name;
+ for (int i = 0; i < 3; ++i) {
+ prev_name.push_back(file_spec.getFileName() + "." +
+ boost::lexical_cast<string>(i + 1));
+ (void) unlink(prev_name[i].c_str());
+ }
+
+ // Generate an argument for a message that ensures that the message when
+ // logged will be over that size.
+ string big_arg(SIZE_LIMIT, 'x');
+
+ // Set up the file logger
+ LoggerManager manager;
+ manager.process(spec);
+
+ // Log the message twice using different message IDs. This should generate
+ // three files as for the log4cplus implementation, the files appear to
+ // be rolled after the message is logged.
+ {
+ Logger logger(file_spec.getLoggerName());
+ LOG_FATAL(logger, LOG_NO_SUCH_MESSAGE).arg(big_arg);
+ LOG_FATAL(logger, LOG_DUPLICATE_NAMESPACE).arg(big_arg);
+ }
+
+ // Check them.
+ LoggerManager::reset(); // Ensure files are closed
+
+ vector<MessageID> ids;
+ ids.push_back(LOG_NO_SUCH_MESSAGE);
+ checkFileContents(prev_name[1], ids.begin(), ids.end());
+
+ ids.clear();
+ ids.push_back(LOG_DUPLICATE_NAMESPACE);
+ checkFileContents(prev_name[0], ids.begin(), ids.end());
+
+ // Log another message and check that the files have rotated and that
+ // a .3 version does not exist.
+ manager.process(spec);
+ {
+ Logger logger(file_spec.getLoggerName());
+ LOG_FATAL(logger, LOG_NO_MESSAGE_TEXT).arg(big_arg);
+ }
+
+ LoggerManager::reset(); // Ensure files are closed
+
+ // Check that the files have moved.
+ ids.clear();
+ ids.push_back(LOG_DUPLICATE_NAMESPACE);
+ checkFileContents(prev_name[1], ids.begin(), ids.end());
+
+ ids.clear();
+ ids.push_back(LOG_NO_MESSAGE_TEXT);
+ checkFileContents(prev_name[0], ids.begin(), ids.end());
+
+ // ... and check that the .3 version does not exist.
+ ifstream file3(prev_name[2].c_str(), ifstream::in);
+ EXPECT_FALSE(file3.good());
+
+ // Tidy up
+ for (int i = 0; i < prev_name.size(); ++i) {
+ (void) unlink(prev_name[i].c_str());
+ }
+}
diff --git a/src/lib/log/tests/logger_name_unittest.cc b/src/lib/log/tests/logger_name_unittest.cc
new file mode 100644
index 0000000..51fead5
--- /dev/null
+++ b/src/lib/log/tests/logger_name_unittest.cc
@@ -0,0 +1,77 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/logger_name.h>
+
+using namespace isc;
+using namespace isc::log;
+
+// Test class. To avoid disturbing the root logger configuration in other
+// tests in the suite, the root logger name is saved in the constructor and
+// restored in the destructor. However, this is a bit chicken and egg, as the
+// functions used to do the save and restore are those being tested...
+//
+// Note that the root name is originally set by the initialization of the
+// logging configuration done in main().
+
+class LoggerNameTest : public ::testing::Test {
+public:
+ LoggerNameTest() {
+ name_ = getRootLoggerName();
+ }
+ ~LoggerNameTest() {
+ setRootLoggerName(name_);
+ }
+
+private:
+ std::string name_; ///< Saved name
+};
+
+// Check setting and getting of root name
+
+TEST_F(LoggerNameTest, RootNameSetGet) {
+ const std::string name1 = "test1";
+ const std::string name2 = "test2";
+
+ // Check that Set/Get works
+ setRootLoggerName(name1);
+ EXPECT_EQ(name1, getRootLoggerName());
+
+ // We could not test that the root logger name is initialised
+ // correctly (as there is one instance of it and we don't know
+ // when this test will be run) so to check that setName() actually
+ // does change the name, run the test again with a different name.
+ //
+ // (There was always the outside chance that the root logger name
+ // was initialised with name1 and that setName() has no effect.)
+ setRootLoggerName(name2);
+ EXPECT_EQ(name2, getRootLoggerName());
+}
+
+// Check expansion of name
+
+TEST_F(LoggerNameTest, ExpandLoggerName) {
+ const std::string ROOT = "example";
+ const std::string NAME = "something";
+ const std::string FULL_NAME = ROOT + "." + NAME;
+
+ setRootLoggerName(ROOT);
+ EXPECT_EQ(ROOT, expandLoggerName(ROOT));
+ EXPECT_EQ(FULL_NAME, expandLoggerName(NAME));
+ EXPECT_EQ(FULL_NAME, expandLoggerName(FULL_NAME));
+}
diff --git a/src/lib/log/tests/logger_specification_unittest.cc b/src/lib/log/tests/logger_specification_unittest.cc
new file mode 100644
index 0000000..e416c32
--- /dev/null
+++ b/src/lib/log/tests/logger_specification_unittest.cc
@@ -0,0 +1,96 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/logger_specification.h>
+#include <log/output_option.h>
+
+using namespace isc::log;
+using namespace std;
+
+// Check default initialization.
+TEST(LoggerSpecificationTest, DefaultInitialization) {
+ LoggerSpecification spec;
+
+ EXPECT_EQ(string(""), spec.getName());
+ EXPECT_EQ(isc::log::INFO, spec.getSeverity());
+ EXPECT_EQ(0, spec.getDbglevel());
+ EXPECT_FALSE(spec.getAdditive());
+ EXPECT_EQ(0, spec.optionCount());
+}
+
+// Non-default initialization
+TEST(LoggerSpecificationTest, Initialization) {
+ LoggerSpecification spec("alpha", isc::log::ERROR, 42, true);
+
+ EXPECT_EQ(string("alpha"), spec.getName());
+ EXPECT_EQ(isc::log::ERROR, spec.getSeverity());
+ EXPECT_EQ(42, spec.getDbglevel());
+ EXPECT_TRUE(spec.getAdditive());
+ EXPECT_EQ(0, spec.optionCount());
+}
+
+// Get/Set tests
+TEST(LoggerSpecificationTest, SetGet) {
+ LoggerSpecification spec;
+
+ spec.setName("gamma");
+ EXPECT_EQ(string("gamma"), spec.getName());
+
+ spec.setSeverity(isc::log::FATAL);
+ EXPECT_EQ(isc::log::FATAL, spec.getSeverity());
+
+ spec.setDbglevel(7);
+ EXPECT_EQ(7, spec.getDbglevel());
+
+ spec.setAdditive(true);
+ EXPECT_TRUE(spec.getAdditive());
+
+ // Should not affect option count
+ EXPECT_EQ(0, spec.optionCount());
+}
+
+// Check option setting
+TEST(LoggerSpecificationTest, AddOption) {
+ OutputOption option1;
+ option1.destination = OutputOption::DEST_FILE;
+ option1.filename = "/tmp/example.log";
+ option1.maxsize = 123456;
+
+ OutputOption option2;
+ option2.destination = OutputOption::DEST_SYSLOG;
+ option2.facility = "LOCAL7";
+
+ LoggerSpecification spec;
+ spec.addOutputOption(option1);
+ spec.addOutputOption(option2);
+ EXPECT_EQ(2, spec.optionCount());
+
+ // Iterate through them
+ LoggerSpecification::const_iterator i = spec.begin();
+
+ EXPECT_EQ(OutputOption::DEST_FILE, i->destination);
+ EXPECT_EQ(string("/tmp/example.log"), i->filename);
+ EXPECT_EQ(123456, i->maxsize);
+
+ ++i;
+ EXPECT_EQ(OutputOption::DEST_SYSLOG, i->destination);
+ EXPECT_EQ(string("LOCAL7"), i->facility);
+
+ ++i;
+ EXPECT_TRUE(i == spec.end());
+}
diff --git a/src/lib/log/tests/logger_support_test.cc b/src/lib/log/tests/logger_support_test.cc
deleted file mode 100644
index 4d8863e..0000000
--- a/src/lib/log/tests/logger_support_test.cc
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-/// \brief Example Program
-///
-/// Simple example program showing how to use the logger.
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-#include <iostream>
-
-#include <log/logger.h>
-#include <log/logger_support.h>
-#include <log/root_logger_name.h>
-
-// Include a set of message definitions.
-#include <log/messagedef.h>
-
-using namespace isc::log;
-
-// Declare logger to use an example.
-Logger logger_ex("example");
-
-// The program is invoked:
-//
-// logger_support_test [-s severity] [-d level ] [local_file]
-//
-// "severity" is one of "debug", "info", "warn", "error", "fatal"
-// "level" is the debug level, a number between 0 and 99
-// "local_file" is the name of a local file.
-//
-// The program sets the attributes on the root logger and logs a set of
-// messages. Looking at the output determines whether the program worked.
-
-int main(int argc, char** argv) {
-
- isc::log::Severity severity = isc::log::INFO; // Default logger severity
- int dbglevel = -1; // Logger debug level
- const char* localfile = NULL; // Local message file
- int option; // For getopt() processing
- Logger logger_dlm("dlm", true); // Another example logger
-
- // Parse options
- while ((option = getopt(argc, argv, "s:d:")) != -1) {
- switch (option) {
- case 's':
- if (strcmp(optarg, "debug") == 0) {
- severity = isc::log::DEBUG;
- } else if (strcmp(optarg, "info") == 0) {
- severity = isc::log::INFO;
- } else if (strcmp(optarg, "warn") == 0) {
- severity = isc::log::WARN;
- } else if (strcmp(optarg, "error") == 0) {
- severity = isc::log::ERROR;
- } else if (strcmp(optarg, "fatal") == 0) {
- severity = isc::log::FATAL;
- } else {
- std::cout << "Unrecognised severity option: " <<
- optarg << "\n";
- exit(1);
- }
- break;
-
- case 'd':
- dbglevel = atoi(optarg);
- break;
-
- default:
- std::cout << "Unrecognised option: " <<
- static_cast<char>(option) << "\n";
- }
- }
-
- if (optind < argc) {
- localfile = argv[optind];
- }
-
- // Update the logging parameters
- initLogger("alpha", severity, dbglevel, localfile);
-
- // Log a few messages
- logger_ex.fatal(MSG_MSGWRTERR, "test1", "42");
- logger_ex.error(MSG_UNRECDIR, "false");
- logger_dlm.warn(MSG_MSGRDERR, "a.txt", "dummy test");
- logger_dlm.info(MSG_OPNMSGIN, "example.msg", "dummy test");
- logger_ex.debug(0, MSG_UNRECDIR, "[abc]");
- logger_ex.debug(24, MSG_UNRECDIR, "[24]");
- logger_ex.debug(25, MSG_UNRECDIR, "[25]");
- logger_ex.debug(26, MSG_UNRECDIR, "[26]");
- return (0);
-}
diff --git a/src/lib/log/tests/logger_support_unittest.cc b/src/lib/log/tests/logger_support_unittest.cc
new file mode 100644
index 0000000..6a93652
--- /dev/null
+++ b/src/lib/log/tests/logger_support_unittest.cc
@@ -0,0 +1,72 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+#include <log/logger_support.h>
+#include <log/log_messages.h>
+
+using namespace isc::log;
+
+// Check that the initialized flag can be manipulated. This is a bit chicken-
+// -and-egg: we want to reset to the flag to the original value at the end
+// of the test, so use the functions to do that. But we are trying to check
+// that these functions in fact work.
+
+TEST(LoggerSupportTest, InitializedFlag) {
+ bool current_flag = isLoggingInitialized();
+
+ // check we can flip the flag.
+ setLoggingInitialized(!current_flag);
+ EXPECT_NE(current_flag, isLoggingInitialized());
+ setLoggingInitialized(!isLoggingInitialized());
+ EXPECT_EQ(current_flag, isLoggingInitialized());
+
+ // Check we can set it to explicit values (tests that a call to the "set"
+ // function does not just flip the flag).
+ setLoggingInitialized(false);
+ EXPECT_FALSE(isLoggingInitialized());
+ setLoggingInitialized(false);
+ EXPECT_FALSE(isLoggingInitialized());
+
+ setLoggingInitialized(true);
+ EXPECT_TRUE(isLoggingInitialized());
+ setLoggingInitialized(true);
+ EXPECT_TRUE(isLoggingInitialized());
+
+ // Reset to original value
+ setLoggingInitialized(current_flag);
+}
+
+// Check that a logger will throw an exception if logging has not been
+// initialized.
+
+TEST(LoggerSupportTest, LoggingInitializationCheck) {
+
+ // Assert that logging has been initialized (it should be in main()).
+ bool current_flag = isLoggingInitialized();
+ EXPECT_TRUE(current_flag);
+
+ // Flag that it has not been initialized and declare a logger. Any logging
+ // operation should then throw.
+ setLoggingInitialized(false);
+ isc::log::Logger test_logger("test");
+
+ EXPECT_THROW(test_logger.isDebugEnabled(), LoggingNotInitialized);
+ EXPECT_THROW(test_logger.info(LOG_INPUT_OPEN_FAIL), LoggingNotInitialized);
+
+ // ... and check that they work when logging is initialized.
+ setLoggingInitialized(true);
+ EXPECT_NO_THROW(test_logger.isDebugEnabled());
+ EXPECT_NO_THROW(test_logger.info(LOG_INPUT_OPEN_FAIL));
+}
diff --git a/src/lib/log/tests/logger_unittest.cc b/src/lib/log/tests/logger_unittest.cc
index 4eff622..edca9ce 100644
--- a/src/lib/log/tests/logger_unittest.cc
+++ b/src/lib/log/tests/logger_unittest.cc
@@ -17,45 +17,27 @@
#include <gtest/gtest.h>
-#include <log/root_logger_name.h>
#include <log/logger.h>
-#include <log/messagedef.h>
+#include <log/logger_manager.h>
+#include <log/logger_name.h>
+#include <log/log_messages.h>
using namespace isc;
using namespace isc::log;
using namespace std;
-namespace isc {
-namespace log {
-
-/// \brief Test Logger
+/// \brief Logger Test
///
-/// This logger is a subclass of the logger class under test, but makes
-/// protected methods public (for testing)
-
-class TestLogger : public Logger {
-public:
- /// \brief constructor
- TestLogger(const string& name) : Logger(name, true)
- {}
-
- static void reset() {
- Logger::reset();
- }
-};
-
-} // namespace log
-} // namespace isc
-
+/// As the logger is only a shell around the implementation, this tests also
+/// checks the logger implementation class as well.
class LoggerTest : public ::testing::Test {
-protected:
- LoggerTest()
- {
+public:
+ LoggerTest() {
+ // Initialization of logging is done in main()
}
-
~LoggerTest() {
- TestLogger::reset();
+ LoggerManager::reset();
}
};
@@ -65,11 +47,10 @@ protected:
TEST_F(LoggerTest, Name) {
// Create a logger
- setRootLoggerName("test1");
Logger logger("alpha");
// ... and check the name
- EXPECT_EQ(string("test1.alpha"), logger.getName());
+ EXPECT_EQ(getRootLoggerName() + string(".alpha"), logger.getName());
}
// This test attempts to get two instances of a logger with the same name
@@ -77,22 +58,18 @@ TEST_F(LoggerTest, Name) {
TEST_F(LoggerTest, GetLogger) {
- // Set the root logger name (not strictly needed, but this will be the
- // case in the program(.
- setRootLoggerName("test2");
-
const string name1 = "alpha";
const string name2 = "beta";
// Instantiate two loggers that should be the same
- TestLogger logger1(name1);
- TestLogger logger2(name1);
+ Logger logger1(name1);
+ Logger logger2(name1);
// And check they equal
EXPECT_TRUE(logger1 == logger2);
// Instantiate another logger with another name and check that it
// is different to the previously instantiated ones.
- TestLogger logger3(name2);
+ Logger logger3(name2);
EXPECT_FALSE(logger1 == logger3);
}
@@ -101,8 +78,7 @@ TEST_F(LoggerTest, GetLogger) {
TEST_F(LoggerTest, Severity) {
// Create a logger
- setRootLoggerName("test3");
- TestLogger logger("alpha");
+ Logger logger("alpha");
// Now check the levels
logger.setSeverity(isc::log::NONE);
@@ -132,8 +108,7 @@ TEST_F(LoggerTest, Severity) {
TEST_F(LoggerTest, DebugLevels) {
// Create a logger
- setRootLoggerName("test4");
- TestLogger logger("alpha");
+ Logger logger("alpha");
// Debug level should be 0 if not at debug severity
logger.setSeverity(isc::log::NONE, 20);
@@ -174,13 +149,47 @@ TEST_F(LoggerTest, DebugLevels) {
TEST_F(LoggerTest, SeverityInheritance) {
- // Create to loggers. We cheat here as we know that the underlying
- // implementation (in this case log4cxx) will set a parent-child
- // relationship if the loggers are named <parent> and <parent>.<child>.
+ // Create two loggers. We cheat here as we know that the underlying
+ // implementation will set a parent-child relationship if the loggers
+ // are named <parent> and <parent>.<child>.
+ Logger parent("alpha");
+ Logger child("alpha.beta");
- setRootLoggerName("test5");
- TestLogger parent("alpha");
- TestLogger child("alpha.beta");
+ // By default, newly created loggers should have a level of DEFAULT
+ // (i.e. default to parent)
+ EXPECT_EQ(isc::log::DEFAULT, parent.getSeverity());
+ EXPECT_EQ(isc::log::DEFAULT, child.getSeverity());
+
+ // Set the severity of the parent to debug and check what is
+ // reported by the child.
+ parent.setSeverity(isc::log::DEBUG, 42);
+ EXPECT_EQ(42, parent.getDebugLevel());
+ EXPECT_EQ(0, child.getDebugLevel());
+ EXPECT_EQ(42, child.getEffectiveDebugLevel());
+
+ // Setting the child to DEBUG severity should set its own
+ // debug level.
+ child.setSeverity(isc::log::DEBUG, 53);
+ EXPECT_EQ(53, child.getDebugLevel());
+ EXPECT_EQ(53, child.getEffectiveDebugLevel());
+
+ // If the child severity is set to something other than DEBUG,
+ // the debug level should be reported as 0.
+ child.setSeverity(isc::log::ERROR);
+ EXPECT_EQ(0, child.getDebugLevel());
+ EXPECT_EQ(0, child.getEffectiveDebugLevel());
+}
+
+// Check that changing the parent and child debug level does not affect
+// the other.
+
+TEST_F(LoggerTest, DebugLevelInheritance) {
+
+ // Create two loggers. We cheat here as we know that the underlying
+ // implementation will set a parent-child relationship if the loggers
+ // are named <parent> and <parent>.<child>.
+ Logger parent("alpha");
+ Logger child("alpha.beta");
// By default, newly created loggers should have a level of DEFAULT
// (i.e. default to parent)
@@ -206,11 +215,9 @@ TEST_F(LoggerTest, SeverityInheritance) {
TEST_F(LoggerTest, EffectiveSeverityInheritance) {
- // Create to loggers. We cheat here as we know that the underlying
- // implementation (in this case log4cxx) will set a parent-child
- // relationship if the loggers are named <parent> and <parent>.<child>.
-
- setRootLoggerName("test6");
+ // Create two loggers. We cheat here as we know that the underlying
+ // implementation will set a parent-child relationship if the loggers
+ // are named <parent> and <parent>.<child>.
Logger parent("test6");
Logger child("test6.beta");
@@ -245,7 +252,6 @@ TEST_F(LoggerTest, EffectiveSeverityInheritance) {
TEST_F(LoggerTest, IsXxxEnabled) {
- setRootLoggerName("test7");
Logger logger("test7");
logger.setSeverity(isc::log::INFO);
@@ -316,7 +322,6 @@ TEST_F(LoggerTest, IsXxxEnabled) {
TEST_F(LoggerTest, IsDebugEnabledLevel) {
- setRootLoggerName("test8");
Logger logger("test8");
int MID_LEVEL = (MIN_DEBUG_LEVEL + MAX_DEBUG_LEVEL) / 2;
diff --git a/src/lib/log/tests/message_dictionary_unittest.cc b/src/lib/log/tests/message_dictionary_unittest.cc
index a92585c..394fea0 100644
--- a/src/lib/log/tests/message_dictionary_unittest.cc
+++ b/src/lib/log/tests/message_dictionary_unittest.cc
@@ -29,7 +29,7 @@ using namespace std;
// and the latter should be present.
static const char* values[] = {
- "DUPLNS", "duplicate $NAMESPACE directive found",
+ "LOG_DUPLICATE_NAMESPACE", "duplicate $NAMESPACE directive found",
"NEWSYM", "new symbol added",
NULL
};
@@ -190,7 +190,7 @@ TEST_F(MessageDictionaryTest, GlobalTest) {
TEST_F(MessageDictionaryTest, GlobalLoadTest) {
vector<string>& duplicates = MessageInitializer::getDuplicates();
ASSERT_EQ(1, duplicates.size());
- EXPECT_EQ(string("DUPLNS"), duplicates[0]);
+ EXPECT_EQ(string("LOG_DUPLICATE_NAMESPACE"), duplicates[0]);
string text = MessageDictionary::globalDictionary().getText("NEWSYM");
EXPECT_EQ(string("new symbol added"), text);
diff --git a/src/lib/log/tests/message_reader_unittest.cc b/src/lib/log/tests/message_reader_unittest.cc
index 36288f2..d0214a4 100644
--- a/src/lib/log/tests/message_reader_unittest.cc
+++ b/src/lib/log/tests/message_reader_unittest.cc
@@ -16,7 +16,7 @@
#include <string>
#include <gtest/gtest.h>
-#include <log/messagedef.h>
+#include <log/log_messages.h>
#include <log/message_dictionary.h>
#include <log/message_exception.h>
#include <log/message_reader.h>
@@ -68,8 +68,8 @@ TEST_F(MessageReaderTest, BlanksAndComments) {
EXPECT_NO_THROW(reader_.processLine(" \n "));
EXPECT_NO_THROW(reader_.processLine("# This is a comment"));
EXPECT_NO_THROW(reader_.processLine("\t\t # Another comment"));
- EXPECT_NO_THROW(reader_.processLine(" + A description line"));
- EXPECT_NO_THROW(reader_.processLine("#+ A comment"));
+ EXPECT_NO_THROW(reader_.processLine(" A description line"));
+ EXPECT_NO_THROW(reader_.processLine("# A comment"));
EXPECT_NO_THROW(reader_.processLine(" +# A description line"));
// ... and (b) nothing gets added to either the map or the not-added section.
@@ -97,6 +97,15 @@ processLineException(MessageReader& reader, const char* what,
}
}
+// Check that it recognises invalid directives
+
+TEST_F(MessageReaderTest, InvalidDirectives) {
+
+ // Check that a "$" with nothing else generates an error
+ processLineException(reader_, "$", LOG_UNRECOGNISED_DIRECTIVE);
+ processLineException(reader_, "$xyz", LOG_UNRECOGNISED_DIRECTIVE);
+}
+
// Check that it can parse a prefix
TEST_F(MessageReaderTest, Prefix) {
@@ -104,31 +113,33 @@ TEST_F(MessageReaderTest, Prefix) {
// Check that no $PREFIX is present
EXPECT_EQ(string(""), reader_.getPrefix());
- // Check that a $PREFIX directive with no argument generates an error.
- processLineException(reader_, "$PREFIX", MSG_PRFNOARG);
+ // Check that a $PREFIX directive with no argument is OK
+ EXPECT_NO_THROW(reader_.processLine("$PREFIX"));
// Check a $PREFIX with multiple arguments is invalid
- processLineException(reader_, "$prefix A B", MSG_PRFEXTRARG);
+ processLineException(reader_, "$prefix A B", LOG_PREFIX_EXTRA_ARGS);
// Prefixes should be alphanumeric (with underscores) and not start
// with a number.
- processLineException(reader_, "$prefix ab[cd", MSG_PRFINVARG);
- processLineException(reader_, "$prefix 123", MSG_PRFINVARG);
- processLineException(reader_, "$prefix 1ABC", MSG_PRFINVARG);
+ processLineException(reader_, "$prefix ab[cd", LOG_PREFIX_INVALID_ARG);
+ processLineException(reader_, "$prefix 123", LOG_PREFIX_INVALID_ARG);
+ processLineException(reader_, "$prefix 1ABC", LOG_PREFIX_INVALID_ARG);
// A valid prefix should be accepted
EXPECT_NO_THROW(reader_.processLine("$PREFIX dlm__"));
- EXPECT_EQ(string("DLM__"), reader_.getPrefix());
+ EXPECT_EQ(string("dlm__"), reader_.getPrefix());
// And check that the parser fails on invalid prefixes...
- processLineException(reader_, "$prefix 1ABC", MSG_PRFINVARG);
-
- // ... and rejects another valid one
- processLineException(reader_, "$PREFIX ABC", MSG_DUPLPRFX);
+ processLineException(reader_, "$prefix 1ABC", LOG_PREFIX_INVALID_ARG);
// Check that we can clear the prefix as well
reader_.clearPrefix();
EXPECT_EQ(string(""), reader_.getPrefix());
+
+ EXPECT_NO_THROW(reader_.processLine("$PREFIX dlm__"));
+ EXPECT_EQ(string("dlm__"), reader_.getPrefix());
+ EXPECT_NO_THROW(reader_.processLine("$PREFIX"));
+ EXPECT_EQ(string(""), reader_.getPrefix());
}
// Check that it can parse a namespace
@@ -139,13 +150,13 @@ TEST_F(MessageReaderTest, Namespace) {
EXPECT_EQ(string(""), reader_.getNamespace());
// Check that a $NAMESPACE directive with no argument generates an error.
- processLineException(reader_, "$NAMESPACE", MSG_NSNOARG);
+ processLineException(reader_, "$NAMESPACE", LOG_NAMESPACE_NO_ARGS);
// Check a $NAMESPACE with multiple arguments is invalid
- processLineException(reader_, "$namespace A B", MSG_NSEXTRARG);
+ processLineException(reader_, "$namespace A B", LOG_NAMESPACE_EXTRA_ARGS);
// Namespaces should be alphanumeric (with underscores and colons)
- processLineException(reader_, "$namespace ab[cd", MSG_NSINVARG);
+ processLineException(reader_, "$namespace ab[cd", LOG_NAMESPACE_INVALID_ARG);
// A valid $NAMESPACE should be accepted
EXPECT_NO_THROW(reader_.processLine("$NAMESPACE isc"));
@@ -165,7 +176,7 @@ TEST_F(MessageReaderTest, Namespace) {
EXPECT_EQ(string("::"), reader_.getNamespace());
// ... and that another $NAMESPACE is rejected
- processLineException(reader_, "$NAMESPACE ABC", MSG_DUPLNS);
+ processLineException(reader_, "$NAMESPACE ABC", LOG_DUPLICATE_NAMESPACE);
}
// Check that it can parse a line
@@ -173,8 +184,8 @@ TEST_F(MessageReaderTest, Namespace) {
TEST_F(MessageReaderTest, ValidMessageAddDefault) {
// Add a couple of valid messages
- reader_.processLine("GLOBAL1\t\tthis is message global one\n");
- reader_.processLine("GLOBAL2 this is message global two");
+ reader_.processLine("% GLOBAL1\t\tthis is message global one\n");
+ reader_.processLine("%GLOBAL2 this is message global two");
// ... and check them
EXPECT_EQ(string("this is message global one"),
@@ -191,9 +202,9 @@ TEST_F(MessageReaderTest, ValidMessageAddDefault) {
TEST_F(MessageReaderTest, ValidMessageAdd) {
// Add a couple of valid messages
- reader_.processLine("GLOBAL1\t\tthis is message global one\n",
+ reader_.processLine("%GLOBAL1\t\tthis is message global one\n",
MessageReader::ADD);
- reader_.processLine("GLOBAL2 this is message global two",
+ reader_.processLine("% GLOBAL2 this is message global two",
MessageReader::ADD);
// ... and check them
@@ -214,9 +225,9 @@ TEST_F(MessageReaderTest, ValidMessageReplace) {
dictionary_->add("GLOBAL2", "original global2 message");
// Replace a couple of valid messages
- reader_.processLine("GLOBAL1\t\tthis is message global one\n",
+ reader_.processLine("% GLOBAL1\t\tthis is message global one\n",
MessageReader::REPLACE);
- reader_.processLine("GLOBAL2 this is message global two",
+ reader_.processLine("% GLOBAL2 this is message global two",
MessageReader::REPLACE);
// ... and check them
@@ -237,14 +248,14 @@ TEST_F(MessageReaderTest, ValidMessageReplace) {
TEST_F(MessageReaderTest, Overflows) {
// Add a couple of valid messages
- reader_.processLine("GLOBAL1\t\tthis is message global one\n");
- reader_.processLine("GLOBAL2 this is message global two");
+ reader_.processLine("% GLOBAL1\t\tthis is message global one\n");
+ reader_.processLine("% GLOBAL2 this is message global two");
// Add a duplicate in ADD mode.
- reader_.processLine("GLOBAL1\t\tthis is a replacement for global one");
+ reader_.processLine("% GLOBAL1\t\tthis is a replacement for global one");
// Replace a non-existent one in REPLACE mode
- reader_.processLine("LOCAL\t\tthis is a new message",
+ reader_.processLine("% LOCAL\t\tthis is a new message",
MessageReader::REPLACE);
// Check what is in the dictionary.
diff --git a/src/lib/log/tests/output_option_unittest.cc b/src/lib/log/tests/output_option_unittest.cc
new file mode 100644
index 0000000..8f0e0de
--- /dev/null
+++ b/src/lib/log/tests/output_option_unittest.cc
@@ -0,0 +1,66 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/output_option.h>
+
+using namespace isc::log;
+using namespace std;
+
+// As OutputOption is a struct, the only meaningful test is to check that it
+// initializes correctly.
+
+TEST(OutputOptionTest, Initialization) {
+ OutputOption option;
+
+ EXPECT_EQ(OutputOption::DEST_CONSOLE, option.destination);
+ EXPECT_EQ(OutputOption::STR_STDERR, option.stream);
+ EXPECT_FALSE(option.flush);
+ EXPECT_EQ(string("LOCAL0"), option.facility);
+ EXPECT_EQ(string(""), option.filename);
+ EXPECT_EQ(0, option.maxsize);
+ EXPECT_EQ(0, option.maxver);
+}
+
+TEST(OutputOption, getDestination) {
+ EXPECT_EQ(OutputOption::DEST_CONSOLE, getDestination("console"));
+ EXPECT_EQ(OutputOption::DEST_CONSOLE, getDestination("CONSOLE"));
+ EXPECT_EQ(OutputOption::DEST_CONSOLE, getDestination("CoNSoLE"));
+ EXPECT_EQ(OutputOption::DEST_FILE, getDestination("file"));
+ EXPECT_EQ(OutputOption::DEST_FILE, getDestination("FILE"));
+ EXPECT_EQ(OutputOption::DEST_FILE, getDestination("fIlE"));
+ EXPECT_EQ(OutputOption::DEST_SYSLOG, getDestination("syslog"));
+ EXPECT_EQ(OutputOption::DEST_SYSLOG, getDestination("SYSLOG"));
+ EXPECT_EQ(OutputOption::DEST_SYSLOG, getDestination("SYSlog"));
+
+ // bad values should default to DEST_CONSOLE
+ EXPECT_EQ(OutputOption::DEST_CONSOLE, getDestination("SOME_BAD_VALUE"));
+}
+
+TEST(OutputOption, getStream) {
+ EXPECT_EQ(OutputOption::STR_STDOUT, getStream("stdout"));
+ EXPECT_EQ(OutputOption::STR_STDOUT, getStream("STDOUT"));
+ EXPECT_EQ(OutputOption::STR_STDOUT, getStream("STdouT"));
+ EXPECT_EQ(OutputOption::STR_STDERR, getStream("stderr"));
+ EXPECT_EQ(OutputOption::STR_STDERR, getStream("STDERR"));
+ EXPECT_EQ(OutputOption::STR_STDERR, getStream("StDeRR"));
+
+ // bad values should default to stdout
+ EXPECT_EQ(OutputOption::STR_STDOUT, getStream("some bad value"));
+ EXPECT_EQ(OutputOption::STR_STDOUT, getStream(""));
+}
+
diff --git a/src/lib/log/tests/root_logger_name_unittest.cc b/src/lib/log/tests/root_logger_name_unittest.cc
deleted file mode 100644
index 8665794..0000000
--- a/src/lib/log/tests/root_logger_name_unittest.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <string>
-
-#include <gtest/gtest.h>
-
-#include <log/root_logger_name.h>
-
-using namespace isc;
-using namespace isc::log;
-
-class RootLoggerNameTest : public ::testing::Test {
-protected:
- RootLoggerNameTest()
- {
- }
-};
-
-// Check of the (only) functionality of the class.
-
-TEST_F(RootLoggerNameTest, SetGet) {
- const std::string name1 = "test1";
- const std::string name2 = "test2";
-
- // Check that Set/Get works
- setRootLoggerName(name1);
- EXPECT_EQ(name1, getRootLoggerName());
-
- // We could not test that the root logger name is initialised
- // correctly (as there is one instance of it and we don't know
- // when this test will be run) so to check that setName() actually
- // does change the name, run the test again with a different name.
- //
- // (There was always the outside chance that the root logger name
- // was initialised with name1 and that setName() has no effect.)
- setRootLoggerName(name2);
- EXPECT_EQ(name2, getRootLoggerName());
-}
diff --git a/src/lib/log/tests/run_time_init_test.sh.in b/src/lib/log/tests/run_time_init_test.sh.in
deleted file mode 100755
index e2bdf6f..0000000
--- a/src/lib/log/tests/run_time_init_test.sh.in
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/bin/sh
-# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
-
-failcount=0
-localmes=@abs_builddir@/localdef_mes_$$
-tempfile=@abs_builddir@/run_time_init_test_tempfile_$$
-
-passfail() {
- if [ $1 -eq 0 ]; then
- echo "pass"
- else
- echo "FAIL"
- fi
- failcount=`expr $failcount + $1`
-}
-
-# Create the local message file for testing
-
-cat > $localmes << .
-NOTHERE this message is not in the global dictionary
-MSGRDERR replacement read error, parameters: '%s' and '%s'
-UNRECDIR replacement unrecognised directive message, parameter is '%s'
-.
-
-echo -n "1. runInitTest default parameters: "
-cat > $tempfile << .
-FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
-ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
-WARN [alpha.dlm] MSGRDERR, error reading from message file a.txt: dummy test
-INFO [alpha.dlm] OPNMSGIN, unable to open message file example.msg for input: dummy test
-.
-./logger_support_test | cut -d' ' -f3- | diff $tempfile -
-passfail $?
-
-echo -n "2. Severity filter: "
-cat > $tempfile << .
-FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
-ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
-.
-./logger_support_test -s error | cut -d' ' -f3- | diff $tempfile -
-passfail $?
-
-echo -n "3. Debug level: "
-cat > $tempfile << .
-FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
-ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
-WARN [alpha.dlm] MSGRDERR, error reading from message file a.txt: dummy test
-INFO [alpha.dlm] OPNMSGIN, unable to open message file example.msg for input: dummy test
-DEBUG [alpha.example] UNRECDIR, unrecognised directive '[abc]'
-DEBUG [alpha.example] UNRECDIR, unrecognised directive '[24]'
-DEBUG [alpha.example] UNRECDIR, unrecognised directive '[25]'
-.
-./logger_support_test -s debug -d 25 | cut -d' ' -f3- | diff $tempfile -
-passfail $?
-
-echo -n "4. Local message replacement: "
-cat > $tempfile << .
-WARN [alpha.log] IDNOTFND, could not replace message for 'NOTHERE': no such message identification
-FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
-ERROR [alpha.example] UNRECDIR, replacement unrecognised directive message, parameter is 'false'
-WARN [alpha.dlm] MSGRDERR, replacement read error, parameters: 'a.txt' and 'dummy test'
-.
-./logger_support_test -s warn $localmes | cut -d' ' -f3- | diff $tempfile -
-passfail $?
-
-rm -f $localmes
-rm -f $tempfile
-
-if [ $failcount -eq 0 ]; then
- echo "PASS: run_time_init_test"
-elif [ $failcount -eq 1 ]; then
- echo "FAIL: run_time_init_test - 1 test failed"
-else
- echo "FAIL: run_time_init_test - $failcount tests failed"
-fi
-
-exit $failcount
diff --git a/src/lib/log/tests/run_unittests.cc b/src/lib/log/tests/run_unittests.cc
index bd3c4c9..8a9d1e5 100644
--- a/src/lib/log/tests/run_unittests.cc
+++ b/src/lib/log/tests/run_unittests.cc
@@ -13,9 +13,13 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+
+#include <log/logger_support.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+ isc::log::initLogger();
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/log/tests/severity_test.sh.in b/src/lib/log/tests/severity_test.sh.in
new file mode 100755
index 0000000..78d5050
--- /dev/null
+++ b/src/lib/log/tests/severity_test.sh.in
@@ -0,0 +1,89 @@
+#!/bin/sh
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# Checks that the logger will limit the output of messages less severe than
+# the severity/debug setting.
+
+testname="Severity test"
+echo $testname
+
+failcount=0
+tempfile=@abs_builddir@/severity_test_tempfile_$$
+
+passfail() {
+ if [ $1 -eq 0 ]; then
+ echo " pass"
+ else
+ echo " FAIL"
+ failcount=`expr $failcount + $1`
+ fi
+}
+
+echo -n "1. Default parameters:"
+cat > $tempfile << .
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+WARN [example] LOG_BAD_STREAM bad log console output stream: example
+WARN [example.alpha] LOG_READ_ERROR error reading from message file a.txt: dummy reason
+INFO [example.alpha] LOG_INPUT_OPEN_FAIL unable to open message file example.msg for input: dummy reason
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+WARN [example.beta] LOG_BAD_STREAM bad log console output stream: beta_warn
+INFO [example.beta] LOG_READ_ERROR error reading from message file beta: info
+.
+./logger_example -c stdout | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "2. Severity filter:"
+cat > $tempfile << .
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+.
+./logger_example -c stdout -s error | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "3. Debug level:"
+cat > $tempfile << .
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+WARN [example] LOG_BAD_STREAM bad log console output stream: example
+WARN [example.alpha] LOG_READ_ERROR error reading from message file a.txt: dummy reason
+INFO [example.alpha] LOG_INPUT_OPEN_FAIL unable to open message file example.msg for input: dummy reason
+DEBUG [example] LOG_READING_LOCAL_FILE reading local message file example/0
+DEBUG [example] LOG_READING_LOCAL_FILE reading local message file example/24
+DEBUG [example] LOG_READING_LOCAL_FILE reading local message file example/25
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+WARN [example.beta] LOG_BAD_STREAM bad log console output stream: beta_warn
+INFO [example.beta] LOG_READ_ERROR error reading from message file beta: info
+DEBUG [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta/25
+.
+./logger_example -c stdout -s debug -d 25 | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+if [ $failcount -eq 0 ]; then
+ echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+ echo "FAIL: $testname - 1 test failed"
+else
+ echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up
+rm -f $tempfile
+
+exit $failcount
diff --git a/src/lib/log/tests/strutil_unittest.cc b/src/lib/log/tests/strutil_unittest.cc
deleted file mode 100644
index b3fceef..0000000
--- a/src/lib/log/tests/strutil_unittest.cc
+++ /dev/null
@@ -1,214 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <string>
-
-#include <gtest/gtest.h>
-
-#include <log/strutil.h>
-
-using namespace isc;
-using namespace std;
-
-class StringUtilTest : public ::testing::Test {
-protected:
- StringUtilTest()
- {
- }
-};
-
-
-// Check for slash replacement
-
-TEST_F(StringUtilTest, Slash) {
-
- string instring = "";
- isc::strutil::normalizeSlash(instring);
- EXPECT_EQ("", instring);
-
- instring = "C:\\A\\B\\C.D";
- isc::strutil::normalizeSlash(instring);
- EXPECT_EQ("C:/A/B/C.D", instring);
-
- instring = "// \\ //";
- isc::strutil::normalizeSlash(instring);
- EXPECT_EQ("// / //", instring);
-}
-
-// Check that leading and trailing space trimming works
-
-TEST_F(StringUtilTest, Trim) {
-
- // Empty and full string.
- EXPECT_EQ("", isc::strutil::trim(""));
- EXPECT_EQ("abcxyz", isc::strutil::trim("abcxyz"));
-
- // Trim right-most blanks
- EXPECT_EQ("ABC", isc::strutil::trim("ABC "));
- EXPECT_EQ("ABC", isc::strutil::trim("ABC\t\t \n\t"));
-
- // Left-most blank trimming
- EXPECT_EQ("XYZ", isc::strutil::trim(" XYZ"));
- EXPECT_EQ("XYZ", isc::strutil::trim("\t\t \tXYZ"));
-
- // Right and left, with embedded spaces
- EXPECT_EQ("MN \t OP", isc::strutil::trim("\t\tMN \t OP \t"));
-}
-
-// Check tokenization. Note that ASSERT_EQ is used to check the size of the
-// returned vector; if not as expected, the following references may be invalid
-// so should not be used.
-
-TEST_F(StringUtilTest, Tokens) {
- vector<string> result;
-
- // Default delimiters
-
- // Degenerate cases
- result = isc::strutil::tokens(""); // Empty string
- EXPECT_EQ(0, result.size());
-
- result = isc::strutil::tokens(" \n "); // String is all delimiters
- EXPECT_EQ(0, result.size());
-
- result = isc::strutil::tokens("abc"); // String has no delimiters
- ASSERT_EQ(1, result.size());
- EXPECT_EQ(string("abc"), result[0]);
-
- // String containing leading and/or trailing delimiters, no embedded ones.
- result = isc::strutil::tokens("\txyz"); // One leading delimiter
- ASSERT_EQ(1, result.size());
- EXPECT_EQ(string("xyz"), result[0]);
-
- result = isc::strutil::tokens("\t \nxyz"); // Multiple leading delimiters
- ASSERT_EQ(1, result.size());
- EXPECT_EQ(string("xyz"), result[0]);
-
- result = isc::strutil::tokens("xyz\n"); // One trailing delimiter
- ASSERT_EQ(1, result.size());
- EXPECT_EQ(string("xyz"), result[0]);
-
- result = isc::strutil::tokens("xyz \t"); // Multiple trailing
- ASSERT_EQ(1, result.size());
- EXPECT_EQ(string("xyz"), result[0]);
-
- result = isc::strutil::tokens("\t xyz \n"); // Leading and trailing
- ASSERT_EQ(1, result.size());
- EXPECT_EQ(string("xyz"), result[0]);
-
- // Embedded delimiters
- result = isc::strutil::tokens("abc\ndef"); // 2 tokens, one separator
- ASSERT_EQ(2, result.size());
- EXPECT_EQ(string("abc"), result[0]);
- EXPECT_EQ(string("def"), result[1]);
-
- result = isc::strutil::tokens("abc\t\t\ndef"); // 2 tokens, 3 separators
- ASSERT_EQ(2, result.size());
- EXPECT_EQ(string("abc"), result[0]);
- EXPECT_EQ(string("def"), result[1]);
-
- result = isc::strutil::tokens("abc\n \tdef\t\tghi");
- ASSERT_EQ(3, result.size()); // Multiple tokens, many delims
- EXPECT_EQ(string("abc"), result[0]);
- EXPECT_EQ(string("def"), result[1]);
- EXPECT_EQ(string("ghi"), result[2]);
-
- // Embedded and non-embedded delimiters
-
- result = isc::strutil::tokens("\t\t \nabc\n \tdef\t\tghi \n\n");
- ASSERT_EQ(3, result.size()); // Multiple tokens, many delims
- EXPECT_EQ(string("abc"), result[0]);
- EXPECT_EQ(string("def"), result[1]);
- EXPECT_EQ(string("ghi"), result[2]);
-
- // Non-default delimiter
- result = isc::strutil::tokens("alpha/beta/ /gamma//delta/epsilon/", "/");
- ASSERT_EQ(6, result.size());
- EXPECT_EQ(string("alpha"), result[0]);
- EXPECT_EQ(string("beta"), result[1]);
- EXPECT_EQ(string(" "), result[2]);
- EXPECT_EQ(string("gamma"), result[3]);
- EXPECT_EQ(string("delta"), result[4]);
- EXPECT_EQ(string("epsilon"), result[5]);
-
- // Non-default delimiters (plural)
- result = isc::strutil::tokens("+*--alpha*beta+ -gamma**delta+epsilon-+**",
- "*+-");
- ASSERT_EQ(6, result.size());
- EXPECT_EQ(string("alpha"), result[0]);
- EXPECT_EQ(string("beta"), result[1]);
- EXPECT_EQ(string(" "), result[2]);
- EXPECT_EQ(string("gamma"), result[3]);
- EXPECT_EQ(string("delta"), result[4]);
- EXPECT_EQ(string("epsilon"), result[5]);
-}
-
-// Changing case
-
-TEST_F(StringUtilTest, ChangeCase) {
- string mixed("abcDEFghiJKLmno123[]{=+--+]}");
- string upper("ABCDEFGHIJKLMNO123[]{=+--+]}");
- string lower("abcdefghijklmno123[]{=+--+]}");
-
- string test = mixed;
- isc::strutil::lowercase(test);
- EXPECT_EQ(lower, test);
-
- test = mixed;
- isc::strutil::uppercase(test);
- EXPECT_EQ(upper, test);
-}
-
-// Formatting
-
-TEST_F(StringUtilTest, Formatting) {
-
- vector<string> args;
- args.push_back("arg1");
- args.push_back("arg2");
- args.push_back("arg3");
-
- string format1 = "This is a string with no tokens";
- EXPECT_EQ(format1, isc::strutil::format(format1, args));
-
- string format2 = ""; // Empty string
- EXPECT_EQ(format2, isc::strutil::format(format2, args));
-
- string format3 = " "; // Empty string
- EXPECT_EQ(format3, isc::strutil::format(format3, args));
-
- string format4 = "String with %d non-string tokens %lf";
- EXPECT_EQ(format4, isc::strutil::format(format4, args));
-
- string format5 = "String with %s correct %s number of tokens %s";
- string result5 = "String with arg1 correct arg2 number of tokens arg3";
- EXPECT_EQ(result5, isc::strutil::format(format5, args));
-
- string format6 = "String with %s too %s few tokens";
- string result6 = "String with arg1 too arg2 few tokens";
- EXPECT_EQ(result6, isc::strutil::format(format6, args));
-
- string format7 = "String with %s too %s many %s tokens %s !";
- string result7 = "String with arg1 too arg2 many arg3 tokens %s !";
- EXPECT_EQ(result7, isc::strutil::format(format7, args));
-
- string format8 = "String with embedded%s%s%stokens";
- string result8 = "String with embeddedarg1arg2arg3tokens";
- EXPECT_EQ(result8, isc::strutil::format(format8, args));
-
- // Handle an empty vector
- args.clear();
- string format9 = "%s %s";
- EXPECT_EQ(format9, isc::strutil::format(format9, args));
-}
diff --git a/src/lib/log/tests/tempdir.h.in b/src/lib/log/tests/tempdir.h.in
new file mode 100644
index 0000000..366fea3
--- /dev/null
+++ b/src/lib/log/tests/tempdir.h.in
@@ -0,0 +1,29 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __TEMPDIR_H
+#define __TEMPDIR_H
+
+/// \brief Define temporary directory
+///
+/// Defines the temporary directory in which temporary files used by the
+/// unit tests are created.
+
+#include <string>
+
+namespace {
+std::string TEMP_DIR("@builddir@");
+}
+
+#endif // __TEMPDIR_H
diff --git a/src/lib/log/tests/xdebuglevel_unittest.cc b/src/lib/log/tests/xdebuglevel_unittest.cc
deleted file mode 100644
index ca80e5a..0000000
--- a/src/lib/log/tests/xdebuglevel_unittest.cc
+++ /dev/null
@@ -1,203 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <iostream>
-#include <string>
-
-#include <gtest/gtest.h>
-
-#include <log4cxx/level.h>
-#include <log/xdebuglevel.h>
-#include <log/debug_levels.h>
-
-/// \brief XDebugLevel (Debug Extension to Level Class)
-///
-/// The class is an extension of the log4cxx Level class; this set of tests
-/// only test the extensions, they do not test the underlying Level class
-/// itself.
-
-using namespace log4cxx;
-
-class XDebugLevelTest : public ::testing::Test {
-protected:
- XDebugLevelTest()
- {
- }
-};
-
-// Check a basic assertion about the numeric values of the debug levels
-
-TEST_F(XDebugLevelTest, NumericValues) {
- EXPECT_EQ(XDebugLevel::XDEBUG_MIN_LEVEL_INT, Level::DEBUG_INT);
- EXPECT_EQ(XDebugLevel::XDEBUG_MAX_LEVEL_INT,
- Level::DEBUG_INT - MAX_DEBUG_LEVEL);
-
- // ... and check that assumptions used below - that the debug levels
- // range from 0 to 99 - are valid.
- EXPECT_EQ(0, MIN_DEBUG_LEVEL);
- EXPECT_EQ(99, MAX_DEBUG_LEVEL);
-}
-
-
-// Checks that the main function for generating logging level objects from
-// debug levels is working.
-
-TEST_F(XDebugLevelTest, GetExtendedDebug) {
-
- // Get a debug level of 0. This should be the same as the main DEBUG
- // level.
- LevelPtr debug0 = XDebugLevel::getExtendedDebug(0);
- EXPECT_EQ(std::string("DEBUG"), debug0->toString());
- EXPECT_EQ(Level::DEBUG_INT, debug0->toInt());
- EXPECT_TRUE(*Level::getDebug() == *debug0);
-
- // Get an arbitrary debug level in the allowed range.
- LevelPtr debug32 = XDebugLevel::getExtendedDebug(32);
- EXPECT_EQ(std::string("DEBUG32"), debug32->toString());
- EXPECT_TRUE((XDebugLevel::XDEBUG_MIN_LEVEL_INT - 32) == debug32->toInt());
-
- // Check that a value outside the range gives the nearest level.
- LevelPtr debug_more = XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL + 1);
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL) == *debug_more);
-
- LevelPtr debug_less = XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL - 1);
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL) == *debug_less);
-}
-
-
-// Creation of a level from an int - should return the default debug level
-// if outside the range.
-
-TEST_F(XDebugLevelTest, FromIntOneArg) {
-
- // Check that a valid debug level is as expected
- LevelPtr debug42 = XDebugLevel::toLevel(
- XDebugLevel::XDEBUG_MIN_LEVEL_INT - 42);
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(42) == *debug42);
-
- // ... and that an invalid one returns an object of type debug.
- LevelPtr debug_invalid = XDebugLevel::toLevel(Level::getInfo()->toInt());
- EXPECT_TRUE(*Level::getDebug() == *debug_invalid);
-}
-
-
-// Creation of a level from an int - should return the default level
-// if outside the range.
-
-TEST_F(XDebugLevelTest, FromIntTwoArg) {
-
- // Check that a valid debug level is as expected
- LevelPtr debug42 = XDebugLevel::toLevel(
- (XDebugLevel::XDEBUG_MIN_LEVEL_INT - 42), Level::getFatal());
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(42) == *debug42);
-
- // ... and that an invalid one returns an object of type debug.
- LevelPtr debug_invalid = XDebugLevel::toLevel(
- Level::getInfo()->toInt(), Level::getFatal());
- EXPECT_TRUE(*Level::getFatal() == *debug_invalid);
-}
-
-
-// Creation of a level from a string - should return the default debug level
-// if outside the range.
-
-TEST_F(XDebugLevelTest, FromStringOneArg) {
-
- // Check that a valid debug levels are as expected
- LevelPtr debug85 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG85"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(85) == *debug85);
-
- LevelPtr debug92 = XDebugLevel::toLevelLS(LOG4CXX_STR("debug92"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(92) == *debug92);
-
- LevelPtr debug27 = XDebugLevel::toLevelLS(LOG4CXX_STR("Debug27"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(27) == *debug27);
-
- LevelPtr debug0 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug0);
-
- // ... and that an invalid one returns an object of type debug (which is
- // the equivalent of a debug level 0 object).
- LevelPtr debug_invalid1 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBU"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid1);
-
- LevelPtr debug_invalid2 = XDebugLevel::toLevelLS(LOG4CXX_STR("EBU"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid2);
-
- LevelPtr debug_invalid3 = XDebugLevel::toLevelLS(LOG4CXX_STR(""));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid3);
-
- LevelPtr debug_invalid4 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUGTEN"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid4);
-
- LevelPtr debug_invalid5 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG105"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL) ==
- *debug_invalid5);
-
- LevelPtr debug_invalid6 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG-7"));
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL) ==
- *debug_invalid6);
-}
-
-
-// Creation of a level from a string - should return the default level
-// if outside the range.
-
-TEST_F(XDebugLevelTest, FromStringTwoArg) {
-
- // Check that a valid debug levels are as expected
- LevelPtr debug85 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG85"),
- Level::getFatal());
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(85) == *debug85);
-
- LevelPtr debug92 = XDebugLevel::toLevelLS(LOG4CXX_STR("debug92"),
- Level::getFatal());
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(92) == *debug92);
-
- LevelPtr debug27 = XDebugLevel::toLevelLS(LOG4CXX_STR("Debug27"),
- Level::getFatal());
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(27) == *debug27);
-
- LevelPtr debug0 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG"),
- Level::getFatal());
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug0);
-
- // ... and that an invalid one returns an object of type debug (which is
- // the equivalent of a debug level 0 object).
- LevelPtr debug_invalid1 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBU"),
- Level::getFatal());
- EXPECT_TRUE(*Level::getFatal() == *debug_invalid1);
-
- LevelPtr debug_invalid2 = XDebugLevel::toLevelLS(LOG4CXX_STR("EBU"),
- Level::getFatal());
- EXPECT_TRUE(*Level::getFatal() == *debug_invalid2);
-
- LevelPtr debug_invalid3 = XDebugLevel::toLevelLS(LOG4CXX_STR(""),
- Level::getFatal());
- EXPECT_TRUE(*Level::getFatal() == *debug_invalid3);
-
- LevelPtr debug_invalid4 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUGTEN"),
- Level::getFatal());
- EXPECT_TRUE(*Level::getFatal() == *debug_invalid4);
-
- LevelPtr debug_invalid5 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG105"),
- Level::getFatal());
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL) ==
- *debug_invalid5);
-
- LevelPtr debug_invalid6 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG-7"),
- Level::getFatal());
- EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL) ==
- *debug_invalid6);
-}
diff --git a/src/lib/log/xdebuglevel.cc b/src/lib/log/xdebuglevel.cc
deleted file mode 100644
index c17a515..0000000
--- a/src/lib/log/xdebuglevel.cc
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <cassert>
-#include <algorithm>
-#include <syslog.h>
-#include <string.h>
-#include <boost/lexical_cast.hpp>
-
-#include <xdebuglevel.h>
-#include <debug_levels.h>
-#include <log4cxx/helpers/stringhelper.h>
-
-using namespace log4cxx;
-using namespace log4cxx::helpers;
-
-// Storage for the logging level objects corresponding to each debug level
-
-bool XDebugLevel::dbglevels_unset_ = true;
-LevelPtr XDebugLevel::dbglevels_[NUM_DEBUG_LEVEL];
-
-// Register the class
-
-IMPLEMENT_LOG4CXX_LEVEL(XDebugLevel)
-
-
-// Create Extended Debug Level Objects
-
-LevelPtr
-XDebugLevel::getExtendedDebug(int level) {
-
- // Initialize the logging levels corresponding to the possible range of
- // debug if we have not already done so
- if (dbglevels_unset_) {
-
- // Asserting that the minimum debug level is zero - so corresponds
- // to DEBUG_INT - means that the lowest level is set to main DEBUG
- // level. This means that the existing logging level object can be
- // used.
- assert(MIN_DEBUG_LEVEL == 0);
- dbglevels_[0] = Level::getDebug();
-
- // Create the logging level objects for the rest of the debug levels.
- // They are given names of the form DEBUG<debug level> (e.g. DEBUG42).
- // They will all correspond to a syslog level of DEBUG.
- for (int i = 1; i < NUM_DEBUG_LEVEL; ++i) {
- std::string name = std::string("DEBUG") +
- boost::lexical_cast<std::string>(i);
- dbglevels_[i] = new XDebugLevel(
- (XDebugLevel::XDEBUG_MIN_LEVEL_INT - i),
- LOG4CXX_STR(name.c_str()), LOG_DEBUG);
- }
- dbglevels_unset_ = false;
- }
-
- // Now get the logging level object asked for. Coerce the debug level to
- // lie in the acceptable range.
- int actual = std::max(MIN_DEBUG_LEVEL, std::min(MAX_DEBUG_LEVEL, level));
-
- // ... and return a pointer to the appropriate logging level object
- return (dbglevels_[actual - MIN_DEBUG_LEVEL]);
-}
-
-// Convert an integer (an absolute logging level number, not a debug level) to a
-// logging level object. If it lies outside the valid range, an object
-// corresponding to the minimum debug value is returned.
-
-LevelPtr
-XDebugLevel::toLevel(int val) {
- return (toLevel(val, getExtendedDebug(MIN_DEBUG_LEVEL)));
-}
-
-LevelPtr
-XDebugLevel::toLevel(int val, const LevelPtr& defaultLevel) {
-
- // Note the reversal of the notion of MIN and MAX - see the header file for
- // details.
- if ((val >= XDEBUG_MAX_LEVEL_INT) && (val <= XDEBUG_MIN_LEVEL_INT)) {
- return (getExtendedDebug(XDEBUG_MIN_LEVEL_INT - val));
- }
- else {
- return (defaultLevel);
- }
-}
-
-// Convert string passed to a logging level or return default level.
-
-LevelPtr
-XDebugLevel::toLevelLS(const LogString& sArg) {
- return (toLevelLS(sArg, getExtendedDebug(0)));
-}
-
-LevelPtr
-XDebugLevel::toLevelLS(const LogString& sArg, const LevelPtr& defaultLevel) {
- std::string name = sArg; // Get to known type
- size_t length = name.size(); // Length of the string
-
- if (length < 5) {
-
- // String can't possibly start DEBUG so we don't know what it is.
- return (defaultLevel);
- }
- else {
- if (strncasecmp(name.c_str(), "DEBUG", 5) == 0) {
-
- // String starts "DEBUG" (or "debug" or any case mixture). The
- // rest of the string -if any - should be a number.
- if (length == 5) {
-
- // It is plain "DEBUG". Take this as level 0.
- return (getExtendedDebug(0));
- }
- else {
-
- // Try converting the remainder to an integer. The "5" is
- // the length of the string "DEBUG". Note that if the number
- // is outside the rangeof debug levels, it is coerced to the
- // nearest limit. Thus a level of DEBUG509 will end up as
- // if DEBUG99 has been specified.
- try {
- int level = boost::lexical_cast<int>(name.substr(5));
- return (getExtendedDebug(level));
- }
- catch ((boost::bad_lexical_cast&) ){
- return (defaultLevel);
- }
- }
- }
- else {
-
- // Unknown string - return default.
- return (defaultLevel);
- }
- }
-}
diff --git a/src/lib/log/xdebuglevel.h b/src/lib/log/xdebuglevel.h
deleted file mode 100644
index e580b77..0000000
--- a/src/lib/log/xdebuglevel.h
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __XDEBUGLEVEL_H
-#define __XDEBUGLEVEL_H
-
-#include <syslog.h>
-#include <log4cxx/level.h>
-
-#include <debug_levels.h>
-
-namespace log4cxx {
-
-/// \brief Debug Extension to Level Class
-///
-/// Based on the example given in the log4cxx distribution, this extends the
-/// log4cxx Level class to allow 100 debug levels.
-///
-/// First some terminology, as the use of the term "level" gets confusing. The
-/// code and comments here use the term "level" in two contexts:
-///
-/// Logging level: The category of messages to log. By default log4cxx defines
-/// the following logging levels: OFF, FATAL, ERROR, WARNING, INFO, DEBUG,
-/// TRACE, ALL. Within the context of BIND-10, OFF, TRACE and ALL are not used
-/// and the idea of DEBUG has been extended, as will be seen below.
-///
-/// Debug level: This is a number that ranges from 0 to 99 and is used by the
-/// application to control the detail of debug output. A value of 0 gives the
-/// highest-level debug output; a value of 99 gives the most verbose and most
-/// detailed. Debug messages (or whatever debug level) are only ever output
-/// when the logging level is set to DEBUG.
-///
-///
-/// With log4cxx, the various logging levels have a numeric value associated
-/// with them, such that FATAL > ERROR > WARNING etc. This suggests that the
-/// idea of debug levels can be incorporated into the existing logging level
-/// scheme by assigning them appropriate numeric values, i.e.
-///
-/// WARNING > INFO > DEBUG(0) > DEBUG(2) > ... > DEBUG(99)
-///
-/// Setting a numeric level of DEBUG enables the basic messages; setting lower
-/// numeric levels will enable progressively more messages. The lowest debug
-/// level (0) is chosen such that setting the general DEBUG logging level will
-/// automatically select that debug level.
-///
-/// This sub-class is needed because the log4cxx::Level class does not allow
-/// the setting of the numeric value of the current level to something other
-/// than the values enumerated in the class. It creates a set of log4cxx
-/// logging levels to correspond to the various debug levels. These levels have
-/// names in the range DEBUG1 to DEBUG99 (the existing Level DEBUG is used for
-/// a debug level of 0), although they are not used in BIND-10: instead the
-/// BIND-10 Logger class treats the logging levels and debug levels separately
-/// and combines them to choose the underlying log4cxx logging level.
-
-
-/// \brief Debug-Extended Level
-
-class XDebugLevel : public Level {
- DECLARE_LOG4CXX_LEVEL(XDebugLevel)
-
- /// Array of pointers to logging level objects, one for each debug level.
- /// The pointer corresponding to a debug level of 0 points to the DEBUG
- /// logging level object.
- static LevelPtr dbglevels_[NUM_DEBUG_LEVEL];
- static bool dbglevels_unset_;
-
-public:
-
- // Minimum and maximum debug levels. Note that XDEBUG_MIN_LEVEL_INT is the
- // number corresponding to the minimum debug level - and is actually larger
- // that XDEBUG_MAX_LEVEL_INT, the number corresponding to the maximum debug
- // level.
- enum {
- XDEBUG_MIN_LEVEL_INT = Level::DEBUG_INT - MIN_DEBUG_LEVEL,
- XDEBUG_MAX_LEVEL_INT = Level::DEBUG_INT - MAX_DEBUG_LEVEL
- };
-
- /// \brief Constructor
- ///
- /// \param level Numeric value of the logging level.
- /// \param name Name given to this logging level.
- /// \param syslogEquivalent The category to be used by syslog when it logs
- /// an event associated with the specified logging level.
- XDebugLevel(int level, const LogString& name, int syslogEquivalent) :
- Level(level, name, syslogEquivalent)
- {}
-
- /// \brief Create Logging Level Object
- ///
- /// Creates a logging level object corresponding to one of the debug levels.
- ///
- /// \param dbglevel The debug level, which ranges from MIN_DEBUG_LEVEL to
- /// MAX_DEBUG_LEVEL. It is coerced to that range if it lies outside it.
- ///
- /// \return Pointer to the desired logging level object.
- static LevelPtr getExtendedDebug(int dbglevel);
-
- /// \brief Convert Integer to a Logging Level
- ///
- /// Returns a logging level object corresponding to the given value (which
- /// is an absolute value of a logging level - it is not a debug level).
- /// If the number is invalid, an object of logging level DEBUG (the
- /// minimum debug logging level) is returned.
- ///
- /// \param val Number to convert to a logging level. This is an absolute
- /// logging level number, not a debug level.
- ///
- /// \return Pointer to the desired logging level object.
- static LevelPtr toLevel(int val);
-
- /// \brief Convert Integer to a Level
- ///
- /// Returns a logging level object corresponding to the given value (which
- /// is an absolute value of a logging level - it is not a debug level).
- /// If the number is invalid, the given default is returned.
- ///
- /// \param val Number to convert to a logging level. This is an absolute
- /// logging level number, not a debug level.
- /// \param defaultLevel Logging level to return if value is not recognised.
- ///
- /// \return Pointer to the desired logging level object.
- static LevelPtr toLevel(int val, const LevelPtr& defaultLevel);
-
- /// \brief Convert String to Logging Level
- ///
- /// Returns a logging level object corresponding to the given name. If the
- /// name is invalid, an object of logging level DEBUG (the minimum debug
- /// logging level) is returned.
- ///
- /// \param sArg Name of the logging level.
- ///
- /// \return Pointer to the desired logging level object.
- static LevelPtr toLevelLS(const LogString& sArg);
-
- /// \brief Convert String to Logging Level
- ///
- /// Returns a logging level object corresponding to the given name. If the
- /// name is invalid, the given default is returned.
- ///
- /// \param sArg name of the level.
- /// \param defaultLevel Logging level to return if name doesn't exist.
- ///
- /// \return Pointer to the desired logging level object.
- static LevelPtr toLevelLS(const LogString& sArg,
- const LevelPtr& defaultLevel);
-};
-
-} // namespace log4cxx
-
-
-#endif // __XDEBUGLEVEL_H
diff --git a/src/lib/nsas/Makefile.am b/src/lib/nsas/Makefile.am
index 04a765b..663afba 100644
--- a/src/lib/nsas/Makefile.am
+++ b/src/lib/nsas/Makefile.am
@@ -3,39 +3,61 @@ SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/nsas -I$(top_builddir)/src/lib/nsas
AM_CPPFLAGS += $(SQLITE_CFLAGS)
AM_CXXFLAGS = $(B10_CXXFLAGS)
-# Some versions of GCC warn about some versions of Boost regarding
-# missing initializer for members in its posix_time.
+# Some versions of GCC warn about some versions of Boost regarding missing
+# initializer for members in its posix_time.
# https://svn.boost.org/trac/boost/ticket/3477
# But older GCC compilers don't have the flag.
AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
-if USE_CLANGPP
# clang++ complains about unused function parameters in some boost header
# files.
+if USE_CLANGPP
AM_CXXFLAGS += -Wno-unused-parameter
endif
+# Define rule to build logging source files from message file
+nsas_messages.h nsas_messages.cc: nsas_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/nsas/nsas_messages.mes
+
+# What is being built.
lib_LTLIBRARIES = libnsas.la
+
+# Tell Automake that the nsas_messages.{cc,h} source files are created in the build
+# process, so it must create these before doing anything else. Although they
+# are a dependency of the library (so will be created from the message file
+# anyway), there is no guarantee as to exactly _when_ in the build they will be
+# created. As the .h file is included in other sources file (so must be
+# present when they are compiled), the safest option is to create it first.
+BUILT_SOURCES = nsas_messages.h nsas_messages.cc
+
+# Library sources. The generated files will not be in the distribution.
libnsas_la_SOURCES = address_entry.h address_entry.cc
libnsas_la_SOURCES += asiolink.h
libnsas_la_SOURCES += hash.cc hash.h
libnsas_la_SOURCES += hash_deleter.h
libnsas_la_SOURCES += hash_key.cc hash_key.h
-libnsas_la_SOURCES += locks.h
libnsas_la_SOURCES += hash_table.h
-libnsas_la_SOURCES += lru_list.h
libnsas_la_SOURCES += nameserver_address_store.cc nameserver_address_store.h
libnsas_la_SOURCES += nameserver_address.h nameserver_address.cc
libnsas_la_SOURCES += nameserver_entry.cc nameserver_entry.h
libnsas_la_SOURCES += nsas_entry_compare.h
libnsas_la_SOURCES += nsas_entry.h nsas_types.h
+libnsas_la_SOURCES += nsas_log.cc nsas_log.h
libnsas_la_SOURCES += zone_entry.cc zone_entry.h
libnsas_la_SOURCES += fetchable.h
libnsas_la_SOURCES += address_request_callback.h
-libnsas_la_SOURCES += random_number_generator.h
+libnsas_la_SOURCES += glue_hints.h glue_hints.cc
+
+nodist_libnsas_la_SOURCES = nsas_messages.h nsas_messages.cc
+
+# The message file should be in the distribution.
+EXTRA_DIST = nsas_messages.mes
-CLEANFILES = *.gcno *.gcda
+# Make sure that the generated files are got rid of in a clean operation
+CLEANFILES = *.gcno *.gcda nsas_messages.h nsas_messages.cc
diff --git a/src/lib/nsas/fetchable.h b/src/lib/nsas/fetchable.h
index e828611..461cfca 100644
--- a/src/lib/nsas/fetchable.h
+++ b/src/lib/nsas/fetchable.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 CZ NIC
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
diff --git a/src/lib/nsas/glue_hints.cc b/src/lib/nsas/glue_hints.cc
new file mode 100644
index 0000000..02c27ee
--- /dev/null
+++ b/src/lib/nsas/glue_hints.cc
@@ -0,0 +1,168 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "glue_hints.h"
+
+#include <stdlib.h>
+
+#include <dns/rrset.h>
+#include <dns/rdata.h>
+#include <dns/rrtype.h>
+#include <dns/rdataclass.h>
+
+#include <asiolink/io_address.h>
+#include <nsas/nameserver_entry.h>
+
+using namespace isc::dns;
+using namespace isc::nsas;
+
+// This is a simple implementation for finding glue
+//
+// It iterates over the AUTHORITY section of the given Message,
+// and for each NS RR it iterates over the ADDITIONAL section to
+// see if there are A or AAAA records.
+//
+// Of course, this could be done more efficiently. One option is to
+// reverse this; check for A and AAAA records (since those will only
+// be there if there actually is glue, while NS records will be present
+// in any delegation). However, it may be even better to let the
+// Response Classifier decide on glue, while it is validating the packet
+//
+// (er, TODO, so to speak. discuss.)
+
+// Helper functions
+namespace {
+ // Add the contents of the given A or AAAA rrset to the given
+ // addressvector
+ //
+ // This creates an 'dummy' NameserverEntry value, because that
+ // is enforced by NameserverAddress. We may want to reconsider
+ // the need for that (perhaps we can change it so that if it is
+ // NULL, all NSAS-related calls to the NameserverAddress object
+ // become nops)
+ void
+ addRRset(std::vector<NameserverAddress>& addresses,
+ const RRsetPtr rrset)
+ {
+ const std::string ns_name = rrset->getName().toText();
+ RdataIteratorPtr rdi = rrset->getRdataIterator();
+ while (!rdi->isLast()) {
+ AddressEntry entry(isc::asiolink::IOAddress(rdi->getCurrent().toText()));
+ boost::shared_ptr<NameserverEntry> ns_entry(new NameserverEntry(ns_name, rrset->getClass()));
+ NameserverAddress ns_address(ns_entry, entry, V4_ONLY);
+ addresses.push_back(ns_address);
+ rdi->next();
+ }
+ }
+}
+
+namespace isc {
+namespace nsas {
+
+GlueHints::GlueHints(const std::string& zone_name,
+ const isc::dns::Message& delegation_message)
+{
+ for (RRsetIterator rssi = delegation_message.beginSection(Message::SECTION_AUTHORITY);
+ rssi != delegation_message.endSection(Message::SECTION_AUTHORITY);
+ ++rssi) {
+ if ((*rssi)->getType() == RRType::NS() &&
+ (*rssi)->getName().toText() == zone_name) {
+ addGlueForRRset(*rssi, delegation_message);
+ }
+ }
+}
+
+
+bool
+GlueHints::hasGlue(AddressFamily family) const {
+ return ((addresses_v4.size() > 0 && (family == ANY_OK || family == V4_ONLY)) ||
+ (addresses_v6.size() > 0 && (family == ANY_OK || family == V6_ONLY)));
+}
+
+NameserverAddress
+GlueHints::getGlue(AddressFamily family) const {
+ // TODO: once we have a more general random lib, use that. Since
+ // this is simply glue, and we don't need a weighted selection,
+ // for now srandom should be good enough. Once #583 has been merged,
+ // (or better yet, once that one and the weighted random have gone
+ // together in a util lib), we can use that.
+ int max = 0;
+ size_t v4s = addresses_v4.size();
+ size_t v6s = addresses_v6.size();
+
+ if (family == ANY_OK || family == V4_ONLY) {
+ max += v4s;
+ }
+ if (family == ANY_OK || family == V6_ONLY) {
+ max += v6s;
+ }
+
+ assert(max > 0);
+ long int selection = random() % max;
+
+ if (family == ANY_OK) {
+ if (selection < v4s) {
+ return addresses_v4[selection];
+ } else {
+ return addresses_v6[selection-v4s];
+ }
+ } else if (family == V4_ONLY) {
+ return addresses_v4[selection];
+ } else if (family == V6_ONLY) {
+ return addresses_v6[selection];
+ } else {
+ // Unknown family
+ assert(false);
+ // Some compilers want something returned anyway
+ return NameserverAddress();
+ }
+}
+
+// Add the A and AAAA records from the given message for the given
+// NS name to the relevant address vector
+// (A rrsets are added to addresses_v4, AAAA rrsets are added to
+// addresses_v6).
+void
+GlueHints::addGlueForName(const Name& name, const Message& message)
+{
+ for (RRsetIterator rssi = message.beginSection(Message::SECTION_ADDITIONAL);
+ rssi != message.endSection(Message::SECTION_ADDITIONAL);
+ ++rssi) {
+ if ((*rssi)->getName() == name) {
+ if ((*rssi)->getType() == RRType::A()) {
+ addRRset(addresses_v4, *rssi);
+ } else if ((*rssi)->getType() == RRType::AAAA()) {
+ addRRset(addresses_v6, *rssi);
+ }
+ }
+ }
+}
+
+// Add the glue for the given NS RRset in the message to the
+// relevant vectors.
+void
+GlueHints::addGlueForRRset(const RRsetPtr rrset, const Message& message)
+{
+ RdataIteratorPtr rdi = rrset->getRdataIterator();
+ while (!rdi->isLast()) {
+ isc::dns::Name name(dynamic_cast<const rdata::generic::NS&>(
+ rdi->getCurrent()).getNSName());
+ addGlueForName(name, message);
+ rdi->next();
+ }
+}
+
+
+} // namespace nsas
+} // namespace isc
diff --git a/src/lib/nsas/glue_hints.h b/src/lib/nsas/glue_hints.h
new file mode 100644
index 0000000..8e6ecf1
--- /dev/null
+++ b/src/lib/nsas/glue_hints.h
@@ -0,0 +1,71 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __GLUE_HINTS_H
+#define __GLUE_HINTS_H
+
+#include <vector>
+
+#include <dns/message.h>
+
+#include "nsas_types.h"
+#include "nameserver_address.h"
+
+namespace isc {
+namespace nsas {
+
+class GlueHints {
+public:
+ /// \brief Empty constructor
+ GlueHints() {};
+
+ /// \brief Constructor
+ ///
+ /// Creates a glue hint object, with the glue data found in the
+ /// given packet.
+ ///
+ /// \param zone_name The name of the zone to find glue for
+ /// \param delegation_message The Message that may contain glue
+ GlueHints(const std::string& zone_name,
+ const isc::dns::Message& delegation_message);
+
+ /// \brief Check if there is glue for the given AddressFamily
+ ///
+ /// \param family the AddressFamily to check for glue for
+ /// \return true if there is glue for that family. false if not
+ bool hasGlue(AddressFamily family) const;
+
+ /// \brief Get a random glue address for the given family
+ ///
+ /// ONLY call this if hasGlue() returned true.
+ ///
+ /// \param family the AddressFamily to get glue for
+ /// \return a NameserverAddress specified by the glue
+ NameserverAddress getGlue(AddressFamily family) const;
+
+private:
+ void addGlueForName(const isc::dns::Name& name,
+ const isc::dns::Message& message);
+ void addGlueForRRset(const isc::dns::RRsetPtr rrset,
+ const isc::dns::Message& message);
+
+ std::vector<NameserverAddress> addresses_v4;
+ std::vector<NameserverAddress> addresses_v6;
+};
+
+}
+}
+
+
+#endif // __GLUE_HINTS_H
diff --git a/src/lib/nsas/hash_deleter.h b/src/lib/nsas/hash_deleter.h
index 29a32d7..27f066e 100644
--- a/src/lib/nsas/hash_deleter.h
+++ b/src/lib/nsas/hash_deleter.h
@@ -16,8 +16,9 @@
#define __HASH_DELETER_H
#include <boost/shared_ptr.hpp>
+#include <util/lru_list.h>
+
#include "hash_table.h"
-#include "lru_list.h"
namespace isc {
namespace nsas {
@@ -31,7 +32,7 @@ namespace nsas {
/// hash table without the need to be declared as "friend" or the need
/// to define accessor methods.
template <typename T>
-class HashDeleter : public LruList<T>::Dropped {
+class HashDeleter : public isc::util::LruList<T>::Dropped {
public:
/// \brief Constructor
diff --git a/src/lib/nsas/hash_table.h b/src/lib/nsas/hash_table.h
index c4a9913..c028fa4 100644
--- a/src/lib/nsas/hash_table.h
+++ b/src/lib/nsas/hash_table.h
@@ -19,7 +19,8 @@
#include <boost/shared_ptr.hpp>
-#include "locks.h"
+#include <util/locks.h>
+
#include "hash.h"
#include "hash_key.h"
@@ -47,7 +48,7 @@ struct HashTableSlot {
typedef typename std::list<boost::shared_ptr<T> >::iterator iterator;
///< Iterator over elements with same hash
- typedef isc::locks::upgradable_mutex mutex_type;
+ typedef isc::util::locks::upgradable_mutex mutex_type;
///< Mutex protecting this slot
//@}
@@ -114,11 +115,11 @@ public:
///
//@{
typedef typename
- isc::locks::sharable_lock<typename HashTableSlot<T>::mutex_type>
+ isc::util::locks::sharable_lock<typename HashTableSlot<T>::mutex_type>
sharable_lock; ///< Type for a scope-limited read-lock
typedef typename
- isc::locks::scoped_lock<typename HashTableSlot<T>::mutex_type>
+ isc::util::locks::scoped_lock<typename HashTableSlot<T>::mutex_type>
scoped_lock; ///< Type for a scope-limited write-lock
//@}
diff --git a/src/lib/nsas/locks.h b/src/lib/nsas/locks.h
deleted file mode 100644
index 98197c3..0000000
--- a/src/lib/nsas/locks.h
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-/// This file (right now) provides dummy locks
-/// It also contains code to use boost/threads locks:
-///
-/// if USE_BOOST_THREADS is defined, we typedef the relevant classes
-/// and derive from the relevant templates so our dummy locks are
-/// replaced by the boost locks (--enable-boost-threads)
-///
-/// If USE_BOOST_THREADS is NOT defined, all locks are dummy classes
-/// that don't actually do anything. At this moment, only the very
-/// minimal set of methods that we actually use is defined.
-///
-/// Note that we need to include <config.h> in our .cc files for that
-/// to be set. we might want to enfore this at compile time with a check
-/// (TODO)
-/// Note that this also contains a workaround for Sunstudio; which
-/// probably won't completely work right now (that is, if the TODO
-/// above is completed), since that would also require some changes
-/// in most (at first glance unrelated) Makefiles
-/// (TODO2)
-
-#ifndef __LOCKS_
-#define __LOCKS_
-
-#ifndef USE_BOOST_THREADS
-
-namespace isc {
-namespace locks {
-
-class mutex {
-};
-
-class recursive_mutex {
-};
-
-class upgradable_mutex {
-};
-
-template <typename T>
-class sharable_lock {
-public:
- sharable_lock(T) { }
-};
-
-template <typename T>
-class scoped_lock {
-public:
- scoped_lock(T) { }
-
- void lock() {}
- void unlock() {}
-};
-
-}
-}
-
-#else // USE_BOOST_THREADS
-
-// Workaround for a problem with boost and sunstudio 5.10
-// There is a version check in there that appears wrong,
-// which makes including boost/thread.hpp fail
-// This will probably be fixed in a future version of boost,
-// in which case this part can be removed then
-#ifdef NEED_SUNPRO_WORKAROUND
-#if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
-#undef __SUNPRO_CC
-#define __SUNPRO_CC 0x5090
-#endif
-#endif // NEED_SUNPRO_WORKAROUND
-
-#include <boost/thread.hpp>
-#include <boost/interprocess/sync/sharable_lock.hpp>
-#include <boost/interprocess/sync/scoped_lock.hpp>
-#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
-#include <boost/interprocess/sync/interprocess_recursive_mutex.hpp>
-
-namespace isc {
-namespace locks {
-
-typedef boost::mutex mutex;
-typedef boost::interprocess::interprocess_upgradable_mutex upgradable_mutex;
-typedef boost::interprocess::interprocess_recursive_mutex recursive_mutex;
-
-template <typename T>
-struct sharable_lock : public boost::interprocess::sharable_lock<T> {
-public:
- sharable_lock(T& mtype) : boost::interprocess::sharable_lock<T>(mtype) {}
-};
-
-
-template <class T>
-struct scoped_lock : public boost::interprocess::scoped_lock<T> {
-public:
- scoped_lock(T& mtype) : boost::interprocess::scoped_lock<T>(mtype) { }
-};
-
-}
-}
-
-
-#endif // USE_BOOST_THREADS
-
-#endif // __LOCKS_
diff --git a/src/lib/nsas/lru_list.h b/src/lib/nsas/lru_list.h
deleted file mode 100644
index b7bd386..0000000
--- a/src/lib/nsas/lru_list.h
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __LRU_LIST_H
-#define __LRU_LIST_H
-
-#include <list>
-#include <string>
-
-#include <boost/noncopyable.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include "locks.h"
-
-namespace isc {
-namespace nsas {
-
-/// \brief LRU List
-///
-/// Provides the LRU list for the zone and nameserver objects. The list is
-/// created with a specific size. Entries are added to the back of the list
-/// and removed from the front. It is also possible to pull an element out
-/// of the middle of the list and add it to the end of the list, an action that
-/// should be done when the element is referenced.
-///
-/// It is not intended that the class be copied, and the derivation from
-/// boost::noncopyable enforces this.
-template <typename T>
-class LruList : boost::noncopyable {
-public:
- typedef typename std::list<boost::shared_ptr<T> > lru_list;
- typedef typename lru_list::iterator iterator;
-
- /// \brief Dropped Operation
- ///
- /// When an object is dropped from the LRU list because it has not been
- /// accessed for some time, it is possible that the action should trigger
- /// some other functions. For this reason, it is possible to register
- /// a list-wide functor object to execute in this casee.
- ///
- /// Note that the function does not execute as the result of a call to
- /// remove() - that is an explicit call and it is assumed that the caller
- /// will handle any additional operations needed.
- class Dropped {
- public:
- /// \brief Constructor
- Dropped(){}
-
- /// \brief Virtual Destructor
- virtual ~Dropped(){}
-
- /// \brief Dropped Object Handler
- ///
- /// Function object called when the object drops off the end of the
- /// LRU list.
- ///
- /// \param drop Object being dropped.
- virtual void operator()(T* drop) const = 0;
- };
-
- /// \brief Constructor
- ///
- /// \param max_size Maximum size of the list before elements are dropped.
- /// \param dropped Pointer to a function object that will get called as
- /// elements are dropped. This object will be stored using a shared_ptr,
- /// so should be allocated with new().
- LruList(uint32_t max_size = 1000, Dropped* dropped = NULL) :
- max_size_(max_size), count_(0), dropped_(dropped)
- {}
-
- /// \brief Virtual Destructor
- virtual ~LruList()
- {}
-
- /// \brief Add Element
- ///
- /// Add a new element to the end of the list.
- ///
- /// \param element Reference to the element to add.
- ///
- /// \return Handle describing the element in the LRU list.
- virtual void add(boost::shared_ptr<T>& element);
-
- /// \brief Remove Element
- ///
- /// Removes an element from the list. If the element is not present (i.e.
- /// its internal list pointer is invalid), this is a no-op.
- ///
- /// \param element Reference to the element to remove.
- virtual void remove(boost::shared_ptr<T>& element);
-
- /// \brief Touch Element
- ///
- /// The name comes from the Unix "touch" command. All this does is to
- /// move the specified entry from the middle of the list to the end of
- /// the list.
- ///
- /// \param element Reference to the element to touch.
- virtual void touch(boost::shared_ptr<T>& element);
-
- /// \brief Return Size of the List
- ///
- /// An independent count is kept of the list size, as list.size() may take
- /// some time if the list is big.
- ///
- /// \return Number of elements in the list
- virtual uint32_t size() const {
-
- // Don't bother to lock the mutex. If an update is in progress, we
- // receive either the value just before the update or just after it.
- // Either way, this call could have come just before or just after
- // that operation, so the value would have been just as uncertain.
- return count_;
- }
-
- /// \brief Return Maximum Size
- ///
- /// \return Maximum size of the list
- virtual uint32_t getMaxSize() const {
- return max_size_;
- }
-
- /// \brief Set Maximum Size
- ///
- /// \param max_size New maximum list size
- virtual void setMaxSize(uint32_t max_size) {
- max_size_ = max_size;
- }
-
-private:
- isc::locks::mutex mutex_; ///< List protection
- std::list<boost::shared_ptr<T> > lru_; ///< The LRU list itself
- uint32_t max_size_; ///< Max size of the list
- uint32_t count_; ///< Count of elements
- boost::shared_ptr<Dropped> dropped_; ///< Dropped object
-};
-
-// Add entry to the list
-template <typename T>
-void LruList<T>::add(boost::shared_ptr<T>& element) {
-
- // Protect list against concurrent access
- isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
-
- // Add the entry and set its pointer field to point into the list.
- // insert() is used to get the pointer.
- element->setLruIterator(lru_.insert(lru_.end(), element));
-
- // ... and update the count while we have the mutex.
- ++count_;
-
- // If the count takes us above the maximum size of the list, remove elements
- // from the front. The current list size could be more than one above the
- // maximum size of the list if the maximum size was changed after
- // construction.
- while (count_ > max_size_) {
- if (!lru_.empty()) {
-
- // Run the drop handler (if there is one) on the
-
- // to-be-dropped object.
- if (dropped_) {
- (*dropped_)(lru_.begin()->get());
- }
-
- // ... and get rid of it from the list
- lru_.pop_front();
- --count_;
- }
- else {
-
- // TODO: Log this condition (count_ > 0 when list empty) -
- // it should not happen
- count_ = 0;
- break;
- }
- }
-}
-
-// Remove an element from the list
-template <typename T>
-void LruList<T>::remove(boost::shared_ptr<T>& element) {
-
- // An element can only be removed it its internal pointer is valid.
- // If it is, the pointer can be used to access the list because no matter
- // what other elements are added or removed, the pointer remains valid.
- //
- // If the pointer is not valid, this is a no-op.
- if (element->iteratorValid()) {
-
- // Is valid, so protect list against concurrent access
- isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
-
- lru_.erase(element->getLruIterator()); // Remove element from list
- element->invalidateIterator(); // Invalidate pointer
- --count_; // One less element
- }
-}
-
-// Touch an element - remove it from the list and add to the end
-template <typename T>
-void LruList<T>::touch(boost::shared_ptr<T>& element) {
-
- // As before, if the pointer is not valid, this is a no-op.
- if (element->iteratorValid()) {
-
- // Protect list against concurrent access
- isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
-
- // Move the element to the end of the list.
- lru_.splice(lru_.end(), lru_, element->getLruIterator());
-
- // Update the iterator in the element to point to it. We can
- // offset from end() as a list has a bidirectional iterator.
- iterator i = lru_.end();
- element->setLruIterator(--i);
- }
-}
-
-} // namespace nsas
-} // namespace isc
-
-#endif // __LRU_LIST_H
diff --git a/src/lib/nsas/nameserver_address.cc b/src/lib/nsas/nameserver_address.cc
index b76d7b8..19d18c5 100644
--- a/src/lib/nsas/nameserver_address.cc
+++ b/src/lib/nsas/nameserver_address.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 CZ NIC
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
diff --git a/src/lib/nsas/nameserver_address_store.cc b/src/lib/nsas/nameserver_address_store.cc
index 4efe491..867f028 100644
--- a/src/lib/nsas/nameserver_address_store.cc
+++ b/src/lib/nsas/nameserver_address_store.cc
@@ -20,16 +20,19 @@
#include <config.h>
#include <dns/rdataclass.h>
+#include <util/locks.h>
+#include <util/lru_list.h>
+#include <log/logger.h>
-#include "locks.h"
#include "hash_table.h"
-#include "lru_list.h"
#include "hash_deleter.h"
#include "nsas_entry_compare.h"
#include "nameserver_entry.h"
#include "nameserver_address_store.h"
#include "zone_entry.h"
+#include "glue_hints.h"
#include "address_request_callback.h"
+#include "nsas_log.h"
using namespace isc::dns;
using namespace std;
@@ -49,11 +52,11 @@ NameserverAddressStore::NameserverAddressStore(
zonehashsize)),
nameserver_hash_(new HashTable<NameserverEntry>(
new NsasEntryCompare<NameserverEntry>, nshashsize)),
- zone_lru_(new LruList<ZoneEntry>((3 * zonehashsize),
+ zone_lru_(new isc::util::LruList<ZoneEntry>((3 * zonehashsize),
new HashDeleter<ZoneEntry>(*zone_hash_))),
- nameserver_lru_(new LruList<NameserverEntry>((3 * nshashsize),
+ nameserver_lru_(new isc::util::LruList<NameserverEntry>((3 * nshashsize),
new HashDeleter<NameserverEntry>(*nameserver_hash_))),
- resolver_(resolver)
+ resolver_(resolver.get())
{ }
namespace {
@@ -66,12 +69,12 @@ namespace {
*/
boost::shared_ptr<ZoneEntry>
newZone(
- const boost::shared_ptr<isc::resolve::ResolverInterface>* resolver,
+ isc::resolve::ResolverInterface* resolver,
const string* zone, const RRClass* class_code,
const boost::shared_ptr<HashTable<NameserverEntry> >* ns_hash,
- const boost::shared_ptr<LruList<NameserverEntry> >* ns_lru)
+ const boost::shared_ptr<isc::util::LruList<NameserverEntry> >* ns_lru)
{
- boost::shared_ptr<ZoneEntry> result(new ZoneEntry(*resolver, *zone, *class_code,
+ boost::shared_ptr<ZoneEntry> result(new ZoneEntry(resolver, *zone, *class_code,
*ns_hash, *ns_lru));
return (result);
}
@@ -80,17 +83,22 @@ newZone(
void
NameserverAddressStore::lookup(const string& zone, const RRClass& class_code,
- boost::shared_ptr<AddressRequestCallback> callback, AddressFamily family)
+ boost::shared_ptr<AddressRequestCallback> callback, AddressFamily family,
+ const GlueHints& glue_hints)
{
- pair<bool, boost::shared_ptr<ZoneEntry> > zone_obj(zone_hash_->getOrAdd(HashKey(
- zone, class_code), boost::bind(newZone, &resolver_, &zone, &class_code,
- &nameserver_hash_, &nameserver_lru_)));
+ LOG_DEBUG(nsas_logger, NSAS_DBG_TRACE, NSAS_SEARCH_ZONE_NS).arg(zone);
+
+ pair<bool, boost::shared_ptr<ZoneEntry> > zone_obj(
+ zone_hash_->getOrAdd(HashKey(zone, class_code),
+ boost::bind(newZone, resolver_, &zone, &class_code,
+ &nameserver_hash_, &nameserver_lru_)));
if (zone_obj.first) {
zone_lru_->add(zone_obj.second);
} else {
zone_lru_->touch(zone_obj.second);
}
- zone_obj.second->addCallback(callback, family);
+
+ zone_obj.second->addCallback(callback, family, glue_hints);
}
void
@@ -99,6 +107,8 @@ NameserverAddressStore::cancel(const string& zone,
const boost::shared_ptr<AddressRequestCallback>& callback,
AddressFamily family)
{
+ LOG_DEBUG(nsas_logger, NSAS_DBG_TRACE, NSAS_LOOKUP_CANCEL).arg(zone);
+
boost::shared_ptr<ZoneEntry> entry(zone_hash_->get(HashKey(zone,
class_code)));
if (entry) {
diff --git a/src/lib/nsas/nameserver_address_store.h b/src/lib/nsas/nameserver_address_store.h
index 98eb2dd..87845c9 100644
--- a/src/lib/nsas/nameserver_address_store.h
+++ b/src/lib/nsas/nameserver_address_store.h
@@ -23,6 +23,7 @@
#include <resolve/resolver_interface.h>
#include "nsas_types.h"
+#include "glue_hints.h"
namespace isc {
// Some forward declarations, so we do not need to include so many headers
@@ -31,11 +32,13 @@ namespace dns {
class RRClass;
}
+namespace util {
+template<class T> class LruList;
+}
+
namespace nsas {
-class ResolverInterface;
template<class T> class HashTable;
-template<class T> class LruList;
class ZoneEntry;
class NameserverEntry;
class AddressRequestCallback;
@@ -85,7 +88,7 @@ public:
/// \param family Which address is requested.
void lookup(const std::string& zone, const dns::RRClass& class_code,
boost::shared_ptr<AddressRequestCallback> callback, AddressFamily
- family = ANY_OK);
+ family = ANY_OK, const GlueHints& = GlueHints());
/// \brief cancel the given lookup action
///
@@ -111,11 +114,11 @@ protected:
boost::shared_ptr<HashTable<NameserverEntry> > nameserver_hash_;
// ... and the LRU lists
- boost::shared_ptr<LruList<ZoneEntry> > zone_lru_;
- boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru_;
+ boost::shared_ptr<isc::util::LruList<ZoneEntry> > zone_lru_;
+ boost::shared_ptr<isc::util::LruList<NameserverEntry> > nameserver_lru_;
// The resolver we use
private:
- boost::shared_ptr<isc::resolve::ResolverInterface> resolver_;
+ isc::resolve::ResolverInterface* resolver_;
//}@
};
diff --git a/src/lib/nsas/nameserver_entry.cc b/src/lib/nsas/nameserver_entry.cc
index 5c7873e..553c35d 100644
--- a/src/lib/nsas/nameserver_entry.cc
+++ b/src/lib/nsas/nameserver_entry.cc
@@ -40,8 +40,9 @@
#include "address_entry.h"
#include "nameserver_address.h"
#include "nameserver_entry.h"
+#include "nsas_log.h"
-using namespace asiolink;
+using namespace isc::asiolink;
using namespace isc::nsas;
using namespace isc::dns;
using namespace std;
@@ -52,7 +53,7 @@ namespace nsas {
namespace {
// Just shorter type alias
-typedef isc::locks::scoped_lock<isc::locks::recursive_mutex> Lock;
+typedef isc::util::locks::scoped_lock<isc::util::locks::recursive_mutex> Lock;
}
@@ -174,7 +175,13 @@ NameserverEntry::updateAddressRTTAtIndex(uint32_t rtt, size_t index,
uint32_t old_rtt = addresses_[family][index].getRTT();
uint32_t new_rtt = (uint32_t)(old_rtt * UPDATE_RTT_ALPHA + rtt *
(1 - UPDATE_RTT_ALPHA));
+ if (new_rtt == 0) {
+ new_rtt = 1;
+ }
addresses_[family][index].setRTT(new_rtt);
+ LOG_DEBUG(nsas_logger, NSAS_DBG_RTT, NSAS_UPDATE_RTT)
+ .arg(addresses_[family][index].getAddress().toText())
+ .arg(old_rtt).arg(new_rtt);
}
void
@@ -200,7 +207,7 @@ NameserverEntry::setAddressUnreachable(const IOAddress& address) {
* \short A callback into the resolver.
*
* Whenever we ask the resolver something, this is created and the answer is
- * fed back trough this. It holds a shared pointer to the entry so it is not
+ * fed back through this. It holds a shared pointer to the entry so it is not
* destroyed too soon.
*/
class NameserverEntry::ResolverCallback :
@@ -227,6 +234,7 @@ class NameserverEntry::ResolverCallback :
if (!response_message ||
response_message->getRcode() != isc::dns::Rcode::NOERROR() ||
response_message->getRRCount(isc::dns::Message::SECTION_ANSWER) == 0) {
+ LOG_ERROR(nsas_logger, NSAS_INVALID_RESPONSE).arg(entry_->getName());
failureInternal(lock);
return;
}
@@ -240,7 +248,12 @@ class NameserverEntry::ResolverCallback :
if (response->getType() != type_ ||
response->getClass() != RRClass(entry_->getClass()))
{
- // TODO Log we got answer of different type
+ // Invalid response type or class
+ LOG_ERROR(nsas_logger, NSAS_WRONG_ANSWER)
+ .arg(entry_->getName()).arg(type_)
+ .arg(entry_->getClass()).arg(response->getType())
+ .arg(response->getClass());
+
failureInternal(lock);
return;
}
@@ -261,8 +274,10 @@ class NameserverEntry::ResolverCallback :
}
}
// If we found it, use it. If not, create a new one.
- entries.push_back(found ? *found : AddressEntry(IOAddress(
- i->getCurrent().toText()), 1));
+ entries.push_back(found ? *found : AddressEntry(
+ IOAddress(address), 1));
+ LOG_DEBUG(nsas_logger, NSAS_DBG_RESULTS, NSAS_FOUND_ADDRESS)
+ .arg(address).arg(entry_->getName());
}
// We no longer need the previous set of addresses, we have
@@ -307,6 +322,8 @@ class NameserverEntry::ResolverCallback :
* So mark the current address family as unreachable.
*/
virtual void failure() {
+ LOG_DEBUG(nsas_logger, NSAS_DBG_RESULTS, NSAS_NS_LOOKUP_FAIL)
+ .arg(type_).arg(entry_->getName());
Lock lock(entry_->mutex_);
failureInternal(lock);
}
@@ -380,8 +397,7 @@ class NameserverEntry::ResolverCallback :
};
void
-NameserverEntry::askIP(
- boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
+NameserverEntry::askIP(isc::resolve::ResolverInterface* resolver,
const RRType& type, AddressFamily family)
{
QuestionPtr question(new Question(Name(getName()), RRClass(getClass()),
@@ -392,8 +408,7 @@ NameserverEntry::askIP(
}
void
-NameserverEntry::askIP(
- boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
+NameserverEntry::askIP(isc::resolve::ResolverInterface* resolver,
boost::shared_ptr<Callback> callback, AddressFamily family)
{
Lock lock(mutex_);
@@ -421,6 +436,8 @@ NameserverEntry::askIP(
// Ask for both types of addresses
// We are unlocked here, as the callback from that might want to lock
lock.unlock();
+
+ LOG_DEBUG(nsas_logger, NSAS_DBG_TRACE, NSAS_FIND_NS_ADDRESS).arg(getName());
askIP(resolver, RRType::A(), V4_ONLY);
askIP(resolver, RRType::AAAA(), V6_ONLY);
// Make sure we end the routine when we are not locked
diff --git a/src/lib/nsas/nameserver_entry.h b/src/lib/nsas/nameserver_entry.h
index 77937d1..0f214c6 100644
--- a/src/lib/nsas/nameserver_entry.h
+++ b/src/lib/nsas/nameserver_entry.h
@@ -25,11 +25,12 @@
#include <resolve/resolver_interface.h>
+#include <util/lru_list.h>
+
#include "address_entry.h"
#include "asiolink.h"
#include "nsas_types.h"
#include "hash_key.h"
-#include "lru_list.h"
#include "fetchable.h"
#include "nsas_entry.h"
#include "nameserver_address.h"
@@ -241,12 +242,12 @@ public:
* even when there are addresses, if there are no addresses for this
* family.
*/
- void askIP(boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
+ void askIP(isc::resolve::ResolverInterface* resolver,
boost::shared_ptr<Callback> callback, AddressFamily family);
//@}
private:
- mutable isc::locks::recursive_mutex mutex_;///< Mutex protecting this object
+ mutable isc::util::locks::recursive_mutex mutex_;///< Mutex protecting this object
std::string name_; ///< Canonical name of the nameserver
isc::dns::RRClass classCode_; ///< Class of the nameserver
/**
@@ -273,7 +274,7 @@ private:
/// \short Private version that does the actual asking of one address type
///
/// Call unlocked.
- void askIP(boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
+ void askIP(isc::resolve::ResolverInterface* resolver,
const isc::dns::RRType&, AddressFamily);
};
diff --git a/src/lib/nsas/nsas_entry.h b/src/lib/nsas/nsas_entry.h
index f739e8d..9cbed11 100644
--- a/src/lib/nsas/nsas_entry.h
+++ b/src/lib/nsas/nsas_entry.h
@@ -19,10 +19,10 @@
#include <iostream>
#include <exceptions/exceptions.h>
+#include <util/lru_list.h>
#include "hash_key.h"
#include "hash_table.h"
-#include "lru_list.h"
namespace isc {
namespace nsas {
@@ -66,7 +66,7 @@ public:
/// This class is inherited from boost::enable_shared_from_this class
/// So within a member function a shared_ptr to current object can be obtained
template <typename T>
-class NsasEntry : public boost::enable_shared_from_this <T> {
+class NsasEntry : public boost::enable_shared_from_this <T> {
public:
/// \brief Default Constructor
@@ -93,7 +93,7 @@ public:
/// Sets the iterator of an object and, as a side effect, marks it as valid.
///
/// \param iterator Iterator of this element in the list
- virtual void setLruIterator(typename LruList<T>::iterator iterator) {
+ virtual void setLruIterator(typename isc::util::LruList<T>::iterator iterator) {
iterator_ = iterator;
valid_ = true;
}
@@ -103,7 +103,7 @@ public:
/// \return iterator Iterator of this element in the list.
///
/// \exception InvalidLruIterator Thrown if the iterator is not valid.
- virtual typename LruList<T>::iterator getLruIterator() const {
+ virtual typename isc::util::LruList<T>::iterator getLruIterator() const {
if (! valid_) {
isc_throw(InvalidLruIterator,
"pointer of element into LRU list was not valid");
@@ -127,7 +127,7 @@ public:
}
private:
- typename LruList<T>::iterator iterator_; ///< Handle into the LRU List
+ typename isc::util::LruList<T>::iterator iterator_; ///< Handle into the LRU List
bool valid_; ///< true if handle is valid
};
diff --git a/src/lib/nsas/nsas_log.cc b/src/lib/nsas/nsas_log.cc
new file mode 100644
index 0000000..931b131
--- /dev/null
+++ b/src/lib/nsas/nsas_log.cc
@@ -0,0 +1,26 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// Defines the logger used by the NSAS
+
+#include "nsas/nsas_log.h"
+
+namespace isc {
+namespace nsas {
+
+isc::log::Logger nsas_logger("nsas");
+
+} // namespace nsas
+} // namespace isc
+
diff --git a/src/lib/nsas/nsas_log.h b/src/lib/nsas/nsas_log.h
new file mode 100644
index 0000000..ec6844f
--- /dev/null
+++ b/src/lib/nsas/nsas_log.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __NSAS_LOG__H
+#define __NSAS_LOG__H
+
+#include <log/macros.h>
+#include "nsas_messages.h"
+
+namespace isc {
+namespace nsas {
+
+/// \brief NSAS Logging
+///
+/// Defines the levels used to output debug messages in the NSAS. Note that
+/// higher numbers equate to more verbose (and detailed) output.
+
+// The first level traces normal operations - asking the NSAS for an address,
+// and cancelling a lookup. It also records when the NSAS calls back to the
+// resolver to resolve something.
+const int NSAS_DBG_TRACE = 10;
+
+// The next level extends the normal operations and records the results of the
+// lookups.
+const int NSAS_DBG_RESULTS = 20;
+
+// Additional information on the usage of the names - the RTT values obtained
+// when queries were done.
+const int NSAS_DBG_RTT = 30;
+
+
+/// \brief NSAS Logger
+///
+/// Define the logger used to log messages. We could define it in multiple
+/// modules, but defining in a single module and linking to it saves time and
+/// space.
+extern isc::log::Logger nsas_logger; // isc::nsas::logger is the NSAS logger
+
+} // namespace nsas
+} // namespace isc
+
+#endif // __NSAS_LOG__H
diff --git a/src/lib/nsas/nsas_messages.mes b/src/lib/nsas/nsas_messages.mes
new file mode 100644
index 0000000..512fcd5
--- /dev/null
+++ b/src/lib/nsas/nsas_messages.mes
@@ -0,0 +1,69 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+$NAMESPACE isc::nsas
+
+% NSAS_FIND_NS_ADDRESS asking resolver to obtain A and AAAA records for %1
+A debug message issued when the NSAS (nameserver address store - part
+of the resolver) is making a callback into the resolver to retrieve the
+address records for the specified nameserver.
+
+% NSAS_FOUND_ADDRESS found address %1 for %2
+A debug message issued when the NSAS (nameserver address store - part
+of the resolver) has retrieved the given address for the specified
+nameserver through an external query.
+
+% NSAS_INVALID_RESPONSE queried for %1 but got invalid response
+The NSAS (nameserver address store - part of the resolver) made a query
+for a RR for the specified nameserver but received an invalid response.
+Either the success function was called without a DNS message or the
+message was invalid on some way. (In the latter case, the error should
+have been picked up elsewhere in the processing logic, hence the raising
+of the error here.)
+
+This message indicates an internal error in the NSAS. Please raise a
+bug report.
+
+% NSAS_LOOKUP_CANCEL lookup for zone %1 has been canceled
+A debug message issued when an NSAS (nameserver address store - part of
+the resolver) lookup for a zone has been canceled.
+
+% NSAS_NS_LOOKUP_FAIL failed to lookup any %1 for %2
+A debug message issued when the NSAS (nameserver address store - part of
+the resolver) has been unable to retrieve the specified resource record
+for the specified nameserver. This is not necessarily a problem - the
+nameserver may be unreachable, in which case the NSAS will try other
+nameservers in the zone.
+
+% NSAS_SEARCH_ZONE_NS searching NSAS for nameservers for zone %1
+A debug message output when a call is made to the NSAS (nameserver
+address store - part of the resolver) to obtain the nameservers for
+the specified zone.
+
+% NSAS_UPDATE_RTT update RTT for %1: was %2 ms, is now %3 ms
+A NSAS (nameserver address store - part of the resolver) debug message
+reporting the update of a round-trip time (RTT) for a query made to the
+specified nameserver. The RTT has been updated using the value given
+and the new RTT is displayed. (The RTT is subject to a calculation that
+damps out sudden changes. As a result, the new RTT used by the NSAS in
+future decisions of which nameserver to use is not necessarily equal to
+the RTT reported.)
+
+% NSAS_WRONG_ANSWER queried for %1 RR of type/class %2/%3, received response %4/%5
+A NSAS (nameserver address store - part of the resolver) made a query for
+a resource record of a particular type and class, but instead received
+an answer with a different given type and class.
+
+This message indicates an internal error in the NSAS. Please raise a
+bug report.
diff --git a/src/lib/nsas/random_number_generator.h b/src/lib/nsas/random_number_generator.h
deleted file mode 100644
index e80ebcb..0000000
--- a/src/lib/nsas/random_number_generator.h
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __NSAS_RANDOM_NUMBER_GENERATOR_H
-#define __NSAS_RANDOM_NUMBER_GENERATOR_H
-
-#include <cmath>
-#include <numeric>
-#include <boost/random/mersenne_twister.hpp>
-#include <boost/random/uniform_int.hpp>
-#include <boost/random/uniform_real.hpp>
-#include <boost/random/variate_generator.hpp>
-
-namespace isc {
-namespace nsas {
-
-/// \brief Uniform random integer generator
-///
-/// Generate uniformly distributed integers in range of [min, max]
-class UniformRandomIntegerGenerator{
-public:
- /// \brief Constructor
- ///
- /// \param min The minimum number in the range
- /// \param max The maximum number in the range
- UniformRandomIntegerGenerator(int min, int max):
- min_(min), max_(max), dist_(min, max), generator_(rng_, dist_)
- {
- // Init with the current time
- rng_.seed(time(NULL));
- }
-
- /// \brief Generate uniformly distributed integer
- int operator()() { return generator_(); }
-private:
- /// Hide default and copy constructor
- UniformRandomIntegerGenerator();///< Default constructor
- UniformRandomIntegerGenerator(const UniformRandomIntegerGenerator&); ///< Copy constructor
-
- int min_; ///< The minimum integer that can generate
- int max_; ///< The maximum integer that can generate
- boost::uniform_int<> dist_; ///< Distribute uniformly.
- boost::mt19937 rng_; ///< Mersenne Twister: A 623-dimensionally equidistributed uniform pseudo-random number generator
- boost::variate_generator<boost::mt19937&, boost::uniform_int<> > generator_; ///< Uniform generator
-};
-
-/// \brief Weighted random integer generator
-///
-/// Generate random integers according different probabilities
-class WeightedRandomIntegerGenerator {
-public:
- /// \brief Constructor
- ///
- /// \param probabilities The probabies for all the integers, the probability must be
- /// between 0 and 1.0, the sum of probabilities must be equal to 1.
- /// For example, if the probabilities contains the following values:
- /// 0.5 0.3 0.2, the 1st integer will be generated more frequently than the
- /// other integers and the probability is proportional to its value.
- /// \param min The minimum integer that generated, other integers will be
- /// min, min + 1, ..., min + probabilities.size() - 1
- WeightedRandomIntegerGenerator(const std::vector<double>& probabilities,
- size_t min = 0):
- dist_(0, 1.0), uniform_real_gen_(rng_, dist_), min_(min)
- {
- // The probabilities must be valid
- assert(isProbabilitiesValid(probabilities));
- // Calculate the partial sum of probabilities
- std::partial_sum(probabilities.begin(), probabilities.end(),
- std::back_inserter(cumulative_));
- // Init with the current time
- rng_.seed(time(NULL));
- }
-
- /// \brief Default constructor
- ///
- WeightedRandomIntegerGenerator():
- dist_(0, 1.0), uniform_real_gen_(rng_, dist_), min_(0)
- {
- }
-
- /// \brief Reset the probabilities
- ///
- /// Change the weights of each integers
- /// \param probabilities The probabies for all the integers
- /// \param min The minimum integer that generated
- void reset(const std::vector<double>& probabilities, size_t min = 0)
- {
- // The probabilities must be valid
- assert(isProbabilitiesValid(probabilities));
-
- // Reset the cumulative sum
- cumulative_.clear();
-
- // Calculate the partial sum of probabilities
- std::partial_sum(probabilities.begin(), probabilities.end(),
- std::back_inserter(cumulative_));
-
- // Reset the minimum integer
- min_ = min;
- }
-
- /// \brief Generate weighted random integer
- size_t operator()()
- {
- return std::lower_bound(cumulative_.begin(), cumulative_.end(), uniform_real_gen_())
- - cumulative_.begin() + min_;
- }
-
-private:
- /// \brief Check the validation of probabilities vector
- ///
- /// The probability must be in range of [0, 1.0] and the sum must be equal to 1.0
- /// Empty probabilities is also valid.
- bool isProbabilitiesValid(const std::vector<double>& probabilities) const
- {
- typedef std::vector<double>::const_iterator Iterator;
- double sum = probabilities.empty() ? 1.0 : 0.0;
- for(Iterator it = probabilities.begin(); it != probabilities.end(); ++it){
- //The probability must be in [0, 1.0]
- if(*it < 0.0 || *it > 1.0) {
- return false;
- }
-
- sum += *it;
- }
-
- double epsilon = 0.0001;
- // The sum must be equal to 1
- return std::fabs(sum - 1.0) < epsilon;
- }
-
- std::vector<double> cumulative_; ///< The partial sum of the probabilities
- boost::mt19937 rng_; ///< Mersenne Twister: A 623-dimensionally equidistributed uniform pseudo-random number generator
- boost::uniform_real<> dist_; ///< Uniformly distributed real numbers
-
- // Shortcut typedef
- // This typedef is placed directly before its use, as the sunstudio
- // compiler could not handle it being anywhere else (don't know why)
- typedef boost::variate_generator<boost::mt19937&, boost::uniform_real<> > UniformRealGenerator;
- UniformRealGenerator uniform_real_gen_; ///< Uniformly distributed random real numbers generator
-
- size_t min_; ///< The minimum integer that will be generated
-};
-
-} // namespace dns
-} // namespace isc
-
-
-#endif//__NSAS_RANDOM_NUMBER_GENERATOR_H
diff --git a/src/lib/nsas/tests/Makefile.am b/src/lib/nsas/tests/Makefile.am
index 9d9e61c..420e897 100644
--- a/src/lib/nsas/tests/Makefile.am
+++ b/src/lib/nsas/tests/Makefile.am
@@ -3,6 +3,7 @@ SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/nsas -I$(top_builddir)/src/lib/nsas
AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -33,7 +34,6 @@ run_unittests_SOURCES += hash_deleter_unittest.cc
run_unittests_SOURCES += hash_key_unittest.cc
run_unittests_SOURCES += hash_table_unittest.cc
run_unittests_SOURCES += hash_unittest.cc
-run_unittests_SOURCES += lru_list_unittest.cc
run_unittests_SOURCES += nameserver_address_unittest.cc
run_unittests_SOURCES += nameserver_address_store_unittest.cc
run_unittests_SOURCES += nameserver_entry_unittest.cc
@@ -41,11 +41,10 @@ run_unittests_SOURCES += nsas_entry_compare_unittest.cc
run_unittests_SOURCES += nsas_test.h
run_unittests_SOURCES += zone_entry_unittest.cc
run_unittests_SOURCES += fetchable_unittest.cc
-run_unittests_SOURCES += random_number_generator_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD)
# NOTE: we may have to clean up this hack later (see the note in configure.ac)
if NEED_LIBBOOST_THREAD
@@ -53,8 +52,11 @@ run_unittests_LDADD += -lboost_thread
endif
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/nsas/tests/address_entry_unittest.cc b/src/lib/nsas/tests/address_entry_unittest.cc
index 02fef51..60aa3cc 100644
--- a/src/lib/nsas/tests/address_entry_unittest.cc
+++ b/src/lib/nsas/tests/address_entry_unittest.cc
@@ -32,7 +32,7 @@ static std::string V4B_TEXT("5.6.7.8");
static std::string V6A_TEXT("2001:dead:beef::");
static std::string V6B_TEXT("1984:1985::1986:1987");
-using namespace asiolink;
+using namespace isc::asiolink;
using namespace std;
using namespace isc::nsas;
diff --git a/src/lib/nsas/tests/fetchable_unittest.cc b/src/lib/nsas/tests/fetchable_unittest.cc
index f94cd16..4e9f3b4 100644
--- a/src/lib/nsas/tests/fetchable_unittest.cc
+++ b/src/lib/nsas/tests/fetchable_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 CZ NIC
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
diff --git a/src/lib/nsas/tests/hash_deleter_unittest.cc b/src/lib/nsas/tests/hash_deleter_unittest.cc
index 97fecbe..c370857 100644
--- a/src/lib/nsas/tests/hash_deleter_unittest.cc
+++ b/src/lib/nsas/tests/hash_deleter_unittest.cc
@@ -22,11 +22,11 @@
#include <boost/lexical_cast.hpp>
#include <dns/rrclass.h>
+#include <util/lru_list.h>
#include "../nsas_entry.h"
#include "../hash_table.h"
#include "../hash_key.h"
-#include "../lru_list.h"
#include "../hash_deleter.h"
#include "nsas_test.h"
@@ -34,6 +34,7 @@
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
namespace isc {
namespace nsas {
diff --git a/src/lib/nsas/tests/lru_list_unittest.cc b/src/lib/nsas/tests/lru_list_unittest.cc
deleted file mode 100644
index 0161f2b..0000000
--- a/src/lib/nsas/tests/lru_list_unittest.cc
+++ /dev/null
@@ -1,289 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <config.h>
-
-#include <iostream>
-#include <algorithm>
-#include <string>
-#include <vector>
-
-#include <gtest/gtest.h>
-#include <boost/lexical_cast.hpp>
-
-#include "../nsas_entry.h"
-#include "../lru_list.h"
-
-#include "nsas_test.h"
-
-using namespace std;
-
-namespace isc {
-namespace nsas {
-
-/// \brief Dropped Functor Class
-///
-/// Functor object is called when an object is dropped from the LRU list.
-/// To prove that it has run, this function does nothing more than set the
-/// MS bit on the 16-bit class value.
-class Dropped : public LruList<TestEntry>::Dropped {
-public:
- virtual void operator()(TestEntry* entry) const {
- entry->setClass(RRClass(entry->getClass().getCode() | 0x8000));
- }
-};
-
-
-/// \brief Text Fixture Class
-class LruListTest : public ::testing::Test {
-protected:
- LruListTest() :
- entry1_(new TestEntry("alpha", RRClass::IN())),
- entry2_(new TestEntry("beta", RRClass::CH())),
- entry3_(new TestEntry("gamma", RRClass::HS())),
- entry4_(new TestEntry("delta", RRClass::IN())),
- entry5_(new TestEntry("epsilon", RRClass::HS())),
- entry6_(new TestEntry("zeta", RRClass::CH())),
- entry7_(new TestEntry("eta", RRClass::IN()))
- {}
-
- virtual ~LruListTest()
- {}
-
- boost::shared_ptr<TestEntry> entry1_;
- boost::shared_ptr<TestEntry> entry2_;
- boost::shared_ptr<TestEntry> entry3_;
- boost::shared_ptr<TestEntry> entry4_;
- boost::shared_ptr<TestEntry> entry5_;
- boost::shared_ptr<TestEntry> entry6_;
- boost::shared_ptr<TestEntry> entry7_;
-};
-
-
-// Test of the constructor
-TEST_F(LruListTest, Constructor) {
- LruList<TestEntry> lru(100);
- EXPECT_EQ(100, lru.getMaxSize());
- EXPECT_EQ(0, lru.size());
-}
-
-// Test of Get/Set the maximum number of entrys
-TEST_F(LruListTest, GetSet) {
- LruList<TestEntry> lru(100);
- EXPECT_EQ(100, lru.getMaxSize());
- lru.setMaxSize(42);
- EXPECT_EQ(42, lru.getMaxSize());
-}
-
-// Test that adding an entry really does add an entry
-TEST_F(LruListTest, Add) {
- LruList<TestEntry> lru(100);
- EXPECT_EQ(0, lru.size());
-
- lru.add(entry1_);
- EXPECT_EQ(1, lru.size());
-
- lru.add(entry2_);
- EXPECT_EQ(2, lru.size());
-}
-
-// Test that removing an entry really does remove it.
-TEST_F(LruListTest, Remove) {
- LruList<TestEntry> lru(100);
- EXPECT_EQ(0, lru.size());
-
- EXPECT_FALSE(entry1_->iteratorValid());
- lru.add(entry1_);
- EXPECT_TRUE(entry1_->iteratorValid());
- EXPECT_EQ(1, lru.size());
-
- EXPECT_FALSE(entry2_->iteratorValid());
- lru.add(entry2_);
- EXPECT_TRUE(entry2_->iteratorValid());
- EXPECT_EQ(2, lru.size());
-
- lru.remove(entry1_);
- EXPECT_FALSE(entry1_->iteratorValid());
- EXPECT_EQ(1, lru.size());
-}
-
-// Check that adding a new entry to a limited size list does delete the
-// oldest entry from the list.
-TEST_F(LruListTest, SizeLimit) {
- LruList<TestEntry> lru(3);
- EXPECT_EQ(0, lru.size());
-
- // Add first entry and check that the shared pointer's reference count
- // has increased. There will be two references: one from the "entry1_"
- // member in the test fixture class, and one from the list.
- EXPECT_EQ(1, entry1_.use_count());
- lru.add(entry1_);
- EXPECT_EQ(2, entry1_.use_count());
- EXPECT_EQ(1, lru.size());
-
- // Same for entry 2.
- EXPECT_EQ(1, entry2_.use_count());
- lru.add(entry2_);
- EXPECT_EQ(2, entry2_.use_count());
- EXPECT_EQ(2, lru.size());
-
- // Same for entry 3.
- EXPECT_EQ(1, entry3_.use_count());
- lru.add(entry3_);
- EXPECT_EQ(2, entry3_.use_count());
- EXPECT_EQ(3, lru.size());
-
- // Adding entry 4 should remove entry 1 from the list. This will
- // delete the list's shared pointer to the entry and will therefore
- // drop the reference count back to one (from the "entry1_" member in
- // the text fixture class).
- EXPECT_EQ(2, entry1_.use_count());
- EXPECT_EQ(1, entry4_.use_count());
- lru.add(entry4_);
- EXPECT_EQ(1, entry1_.use_count());
- EXPECT_EQ(2, entry4_.use_count());
- EXPECT_EQ(3, lru.size());
-
- // Adding entry 5 should remove entry 2 from the list.
- EXPECT_EQ(2, entry2_.use_count());
- EXPECT_EQ(1, entry5_.use_count());
- lru.add(entry5_);
- EXPECT_EQ(1, entry2_.use_count());
- EXPECT_EQ(2, entry5_.use_count());
- EXPECT_EQ(3, lru.size());
-}
-
-// Check that "touching" an entry adds it to the back of the list.
-TEST_F(LruListTest, Touch) {
-
- // Create the list
- LruList<TestEntry> lru(3);
- EXPECT_EQ(0, lru.size());
- lru.add(entry1_);
- lru.add(entry2_);
- lru.add(entry3_);
-
- // Check the reference counts of the entrys and the list size
- EXPECT_EQ(2, entry1_.use_count());
- EXPECT_EQ(2, entry2_.use_count());
- EXPECT_EQ(2, entry3_.use_count());
- EXPECT_EQ(1, entry4_.use_count());
- EXPECT_EQ(1, entry5_.use_count());
- EXPECT_EQ(1, entry6_.use_count());
- EXPECT_EQ(1, entry7_.use_count());
- EXPECT_EQ(3, lru.size());
-
- // "Touch" the first entry
- lru.touch(entry1_);
-
- // Adding two more entries should not remove the touched entry.
- lru.add(entry4_);
- lru.add(entry5_);
-
- // Check the status of the entrys and the list.
- EXPECT_EQ(2, entry1_.use_count());
- EXPECT_EQ(1, entry2_.use_count());
- EXPECT_EQ(1, entry3_.use_count());
- EXPECT_EQ(2, entry4_.use_count());
- EXPECT_EQ(2, entry5_.use_count());
- EXPECT_EQ(1, entry6_.use_count());
- EXPECT_EQ(1, entry7_.use_count());
- EXPECT_EQ(3, lru.size());
-
- // Now touch the entry agin to move it to the back of the list.
- // This checks that the iterator stored in the entry as a result of the
- // last touch operation is valid.
- lru.touch(entry1_);
-
- // Check this by adding two more entrys and checking reference counts
- // to see what is stored.
- lru.add(entry6_);
- lru.add(entry7_);
-
- EXPECT_EQ(2, entry1_.use_count());
- EXPECT_EQ(1, entry2_.use_count());
- EXPECT_EQ(1, entry3_.use_count());
- EXPECT_EQ(1, entry4_.use_count());
- EXPECT_EQ(1, entry5_.use_count());
- EXPECT_EQ(2, entry6_.use_count());
- EXPECT_EQ(2, entry7_.use_count());
- EXPECT_EQ(3, lru.size());
-}
-
-// Dropped functor tests: tests that the function object is called when an
-// object expires from the list.
-TEST_F(LruListTest, Dropped) {
-
- // Create an object with an expiration handler.
- LruList<TestEntry> lru(3, new Dropped());
-
- // Fill the list
- lru.add(entry1_);
- lru.add(entry2_);
- lru.add(entry3_);
-
- EXPECT_EQ(RRClass::IN(), entry1_->getClass());
- EXPECT_EQ(RRClass::CH(), entry2_->getClass());
-
- // Add another entry and check that the handler runs.
- EXPECT_EQ(0, (entry1_->getClass().getCode() & 0x8000));
- lru.add(entry4_);
- EXPECT_NE(0, (entry1_->getClass().getCode() & 0x8000));
-
- EXPECT_EQ(0, (entry2_->getClass().getCode() & 0x8000));
- lru.add(entry5_);
- EXPECT_NE(0, (entry2_->getClass().getCode() & 0x8000));
-
- // Delete an entry and check that the handler does not run.
- EXPECT_EQ(0, (entry3_->getClass().getCode() & 0x8000));
- lru.remove(entry3_);
- EXPECT_EQ(0, (entry3_->getClass().getCode() & 0x8000));
-}
-
-// Miscellaneous tests - pathological conditions
-TEST_F(LruListTest, Miscellaneous) {
-
- // Zero size list should not allow entrys to be added
- LruList<TestEntry> lru_1(0);
- lru_1.add(entry1_);
- EXPECT_EQ(0, lru_1.size());
- EXPECT_EQ(1, entry1_.use_count());
-
- // Removing an uninserted entry should not affect the list.
- LruList<TestEntry> lru_2(100);
- lru_2.add(entry1_);
- lru_2.add(entry2_);
- lru_2.add(entry3_);
- EXPECT_EQ(3, lru_2.size());
-
- lru_2.remove(entry4_);
- EXPECT_EQ(2, entry1_.use_count());
- EXPECT_EQ(2, entry2_.use_count());
- EXPECT_EQ(2, entry3_.use_count());
- EXPECT_EQ(1, entry4_.use_count());
- EXPECT_EQ(1, entry5_.use_count());
- EXPECT_EQ(3, lru_2.size());
-
- // Touching an uninserted entry should not affect the list.
- lru_2.touch(entry5_);
- EXPECT_EQ(2, entry1_.use_count());
- EXPECT_EQ(2, entry2_.use_count());
- EXPECT_EQ(2, entry3_.use_count());
- EXPECT_EQ(1, entry4_.use_count());
- EXPECT_EQ(1, entry5_.use_count());
- EXPECT_EQ(3, lru_2.size());
-}
-
-} // namespace nsas
-} // namespace isc
diff --git a/src/lib/nsas/tests/nameserver_address_store_unittest.cc b/src/lib/nsas/tests/nameserver_address_store_unittest.cc
index 95b46a8..abbac1d 100644
--- a/src/lib/nsas/tests/nameserver_address_store_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_address_store_unittest.cc
@@ -12,34 +12,36 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <config.h>
-
-/// \brief Test Deleter Objects
+/// \brief Nameserver Address Store Tests
///
-/// This file contains tests for the "deleter" classes within the nameserver
-/// address store. These act to delete zones from the zone hash table when
-/// the element reaches the top of the LRU list.
+/// This file contains tests for the nameserver address store as a whole.
-#include <dns/rrttl.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
+#include <algorithm>
+#include <cassert>
+#include <string.h>
+#include <vector>
-#include <gtest/gtest.h>
-#include <boost/shared_ptr.hpp>
-#include <boost/lexical_cast.hpp>
#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
-#include <string.h>
-#include <cassert>
+#include <config.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+
+#include "../address_request_callback.h"
#include "../nameserver_address_store.h"
-#include "../nsas_entry_compare.h"
#include "../nameserver_entry.h"
+#include "../nsas_entry_compare.h"
#include "../zone_entry.h"
-#include "../address_request_callback.h"
#include "nsas_test.h"
using namespace isc::dns;
+using namespace isc::util;
using namespace std;
namespace isc {
@@ -66,43 +68,62 @@ public:
virtual ~DerivedNsas()
{}
- /// \brief Add Nameserver Entry to Hash and LRU Tables
+ /// \brief Add Nameserver Entry to hash and LRU tables
+ ///
+ /// \param entry Nameserver Entry to add.
void AddNameserverEntry(boost::shared_ptr<NameserverEntry>& entry) {
HashKey h = entry->hashKey();
nameserver_hash_->add(entry, h);
nameserver_lru_->add(entry);
}
- /// \brief Add Zone Entry to Hash and LRU Tables
+ /// \brief Add Zone Entry to hash and LRU tables
+ ///
+ /// \param entry Zone Entry to add.
void AddZoneEntry(boost::shared_ptr<ZoneEntry>& entry) {
HashKey h = entry->hashKey();
zone_hash_->add(entry, h);
zone_lru_->add(entry);
}
- /**
- * \short Just wraps the common lookup
- *
- * It calls the lookup and provides the authority section
- * if it is asked for by the resolver.
- */
+
+ /// \brief Wrap the common lookup
+ ///
+ /// Calls the lookup and provides the authority section if it is asked
+ /// for by the resolver.
+ ///
+ /// \param name Name of zone for which an address is required
+ /// \param class_code Class of the zone
+ /// \param authority Pointer to authority section RRset to which NS
+ /// records will be added.
+ /// \param callback Callback object used to pass result back to caller
void lookupAndAnswer(const string& name, const RRClass& class_code,
- RRsetPtr authority,
- boost::shared_ptr<AddressRequestCallback> callback)
+ RRsetPtr authority,
+ boost::shared_ptr<AddressRequestCallback> callback)
{
+ // Note how many requests are in the resolver's queue
size_t size(resolver_->requests.size());
+
+ // Lookup the name. This should generate a request for NS records.
NameserverAddressStore::lookup(name, class_code, callback, ANY_OK);
- // It asked something, the only thing it can ask is the NS list
if (size < resolver_->requests.size()) {
+
+ // It asked something, the only thing it can ask is the NS list.
+ // Once answered, drop the request so no-one else sees it
resolver_->provideNS(size, authority);
- // Once answered, drop the request so noone else sees it
resolver_->requests.erase(resolver_->requests.begin() + size);
+
} else {
- ADD_FAILURE() << "Not asked for NS";
+
+ // The test resolver's requests queue has not increased in size,
+ // so the lookup did not generate a request.
+ ADD_FAILURE() << "Lookup did not generate a request for NS records";
}
}
+
private:
- boost::shared_ptr<TestResolver> resolver_;
-};
+ boost::shared_ptr<TestResolver> resolver_;
+ ///< Resolver used to answer generated requests
+};
@@ -110,6 +131,7 @@ private:
class NameserverAddressStoreTest : public TestWithRdata {
protected:
+ /// \brief Constructor
NameserverAddressStoreTest() :
authority_(new RRset(Name("example.net."), RRClass::IN(), RRType::NS(),
RRTTL(128))),
@@ -117,8 +139,8 @@ protected:
RRType::NS(), RRTTL(128))),
resolver_(new TestResolver)
{
- // Constructor - initialize a set of nameserver and zone objects. For
- // convenience, these are stored in vectors.
+ // Initialize a set of nameserver and zone objects. For convenience,
+ // these are stored in vectors.
for (int i = 1; i <= 9; ++i) {
std::string name = "nameserver" + boost::lexical_cast<std::string>(
i);
@@ -131,7 +153,7 @@ protected:
for (int i = 1; i <= 9; ++i) {
std::string name = "zone" + boost::lexical_cast<std::string>(i);
zones_.push_back(boost::shared_ptr<ZoneEntry>(new ZoneEntry(
- resolver_, name, RRClass(40 + i),
+ resolver_.get(), name, RRClass(40 + i),
boost::shared_ptr<HashTable<NameserverEntry> >(),
boost::shared_ptr<LruList<NameserverEntry> >())));
}
@@ -144,57 +166,70 @@ protected:
NSASCallback::results.clear();
}
+ /// \brief Internal callback object
+ ///
+ /// Callback object. It just records whether the success() or
+ /// unreachable() methods were called and if success, a copy of the
+ /// Nameserver object. (The data is held in a static object that will
+ /// outlive the lifetime of the callback object.)
+ struct NSASCallback : public AddressRequestCallback {
+ typedef pair<bool, NameserverAddress> Result;
+ static vector<Result> results;
+
+ virtual void success(const NameserverAddress& address) {
+ results.push_back(Result(true, address));
+ }
+ virtual void unreachable() {
+ results.push_back(Result(false, NameserverAddress()));
+ }
+ };
+
+ /// \brief Return pointer to callback object
+ boost::shared_ptr<AddressRequestCallback> getCallback() {
+ return (boost::shared_ptr<AddressRequestCallback>(new NSASCallback));
+ }
+
+ // Member variables
+
// Vector of pointers to nameserver and zone entries.
std::vector<boost::shared_ptr<NameserverEntry> > nameservers_;
std::vector<boost::shared_ptr<ZoneEntry> > zones_;
- RRsetPtr authority_, empty_authority_;
+ // Authority sections used in the tests
+ RRsetPtr authority_;
+ RRsetPtr empty_authority_;
+ // ... and the resolver
boost::shared_ptr<TestResolver> resolver_;
-
- class NSASCallback : public AddressRequestCallback {
- public:
- typedef pair<bool, NameserverAddress> Result;
- static vector<Result> results;
- virtual void success(const NameserverAddress& address) {
- results.push_back(Result(true, address));
- }
- virtual void unreachable() {
- results.push_back(Result(false, NameserverAddress()));
- }
- };
-
- boost::shared_ptr<AddressRequestCallback> getCallback() {
- return (boost::shared_ptr<AddressRequestCallback>(new NSASCallback));
- }
};
+/// Definition of the static results object
vector<NameserverAddressStoreTest::NSASCallback::Result>
NameserverAddressStoreTest::NSASCallback::results;
/// \brief Remove Zone Entry from Hash Table
///
-/// Check that when an entry reaches the top of the zone LRU list, it is removed from the
-/// hash table as well.
+/// Check that when an entry reaches the top of the zone LRU list, it is removed
+/// from the hash table as well.
TEST_F(NameserverAddressStoreTest, ZoneDeletionCheck) {
- // Create a NSAS with a hash size of three and a LRU size of 9 (both zone and
- // nameserver tables).
+ // Create a NSAS with a hash size of three and a LRU size of 9 (both zone
+ // and nameserver tables).
DerivedNsas nsas(resolver_, 2, 2);
- // Add six entries to the tables. After addition the reference count of each element
- // should be 3 - one for the entry in the zones_ vector, and one each for the entries
- // in the LRU list and hash table.
+ // Add six entries to the tables. After addition the reference count of
+ // each element should be 3 - one for the entry in the zones_ vector, and
+ // one each for the entries in the LRU list and hash table.
for (int i = 1; i <= 6; ++i) {
EXPECT_EQ(1, zones_[i].use_count());
nsas.AddZoneEntry(zones_[i]);
EXPECT_EQ(3, zones_[i].use_count());
}
- // Adding another entry should cause the first one to drop off the LRU list, which
- // should also trigger the deletion of the entry from the hash table. This should
- // reduce its use count to 1.
+ // Adding another entry should cause the first one to drop off the LRU list,
+ // which should also trigger the deletion of the entry from the hash table.
+ // This should reduce its use count to 1.
EXPECT_EQ(1, zones_[7].use_count());
nsas.AddZoneEntry(zones_[7]);
EXPECT_EQ(3, zones_[7].use_count());
@@ -205,26 +240,26 @@ TEST_F(NameserverAddressStoreTest, ZoneDeletionCheck) {
/// \brief Remove Entry from Hash Table
///
-/// Check that when an entry reaches the top of the LRU list, it is removed from the
-/// hash table as well.
+/// Check that when an entry reaches the top of the LRU list, it is removed from
+/// the hash table as well.
TEST_F(NameserverAddressStoreTest, NameserverDeletionCheck) {
- // Create a NSAS with a hash size of three and a LRU size of 9 (both zone and
- // nameserver tables).
+ // Create a NSAS with a hash size of three and a LRU size of 9 (both zone
+ // and nameserver tables).
DerivedNsas nsas(resolver_, 2, 2);
- // Add six entries to the tables. After addition the reference count of each element
- // should be 3 - one for the entry in the nameservers_ vector, and one each for the entries
- // in the LRU list and hash table.
+ // Add six entries to the tables. After addition the reference count of
+ // each element should be 3 - one for the entry in the nameservers_ vector,
+ // and one each for the entries in the LRU list and hash table.
for (int i = 1; i <= 6; ++i) {
EXPECT_EQ(1, nameservers_[i].use_count());
nsas.AddNameserverEntry(nameservers_[i]);
EXPECT_EQ(3, nameservers_[i].use_count());
}
- // Adding another entry should cause the first one to drop off the LRU list, which
- // should also trigger the deletion of the entry from the hash table. This should
- // reduce its use count to 1.
+ // Adding another entry should cause the first one to drop off the LRU list,
+ // which should also trigger the deletion of the entry from the hash table.
+ // This should reduce its use count to 1.
EXPECT_EQ(1, nameservers_[7].use_count());
nsas.AddNameserverEntry(nameservers_[7]);
EXPECT_EQ(3, nameservers_[7].use_count());
@@ -232,21 +267,22 @@ TEST_F(NameserverAddressStoreTest, NameserverDeletionCheck) {
EXPECT_EQ(1, nameservers_[1].use_count());
}
-/**
- * \short Try lookup on empty store.
- *
- * Check if it asks correct questions and it keeps correct internal state.
- */
+/// \brief Try lookup on empty store.
+///
+/// Check if it asks correct questions and it keeps correct internal state.
TEST_F(NameserverAddressStoreTest, emptyLookup) {
DerivedNsas nsas(resolver_, 10, 10);
+
// Ask it a question
nsas.lookupAndAnswer("example.net.", RRClass::IN(), authority_,
getCallback());
+
// It should ask for IP addresses for ns.example.com.
EXPECT_NO_THROW(resolver_->asksIPs(Name("ns.example.com."), 0, 1));
// Ask another question for the same zone
nsas.lookup("example.net.", RRClass::IN(), getCallback());
+
// It should ask no more questions now
EXPECT_EQ(2, resolver_->requests.size());
@@ -254,6 +290,7 @@ TEST_F(NameserverAddressStoreTest, emptyLookup) {
authority_->setName(Name("example.com."));
nsas.lookupAndAnswer("example.com.", RRClass::IN(), authority_,
getCallback());
+
// It still should ask nothing
EXPECT_EQ(2, resolver_->requests.size());
@@ -268,35 +305,36 @@ TEST_F(NameserverAddressStoreTest, emptyLookup) {
}
}
-/**
- * \short Try looking up a zone that does not have any nameservers.
- *
- * It should not ask anything and say it is unreachable right away.
- */
+/// \brief Try looking up a zone that does not have any nameservers.
+///
+/// It should not ask anything and say it is unreachable right away.
TEST_F(NameserverAddressStoreTest, zoneWithoutNameservers) {
DerivedNsas nsas(resolver_, 10, 10);
+
// Ask it a question
nsas.lookupAndAnswer("example.net.", RRClass::IN(), empty_authority_,
getCallback());
+
// There should be no questions, because there's nothing to ask
EXPECT_EQ(0, resolver_->requests.size());
+
// And there should be one "unreachable" answer for the query
ASSERT_EQ(1, NSASCallback::results.size());
EXPECT_FALSE(NSASCallback::results[0].first);
}
-/**
- * \short Try looking up a zone that has only an unreachable nameserver.
- *
- * It should be unreachable. Furthermore, subsequent questions for that zone
- * or other zone with the same nameserver should be unreachable right away,
- * without further asking.
- */
+/// \brief Try looking up a zone that has only an unreachable nameserver.
+///
+/// It should be unreachable. Furthermore, subsequent questions for that zone
+/// or other zone with the same nameserver should be unreachable right away,
+/// without further asking.
TEST_F(NameserverAddressStoreTest, unreachableNS) {
DerivedNsas nsas(resolver_, 10, 10);
+
// Ask it a question
nsas.lookupAndAnswer("example.net.", RRClass::IN(), authority_,
getCallback());
+
// It should ask for IP addresses for example.com.
EXPECT_NO_THROW(resolver_->asksIPs(Name("ns.example.com."), 0, 1));
@@ -304,6 +342,7 @@ TEST_F(NameserverAddressStoreTest, unreachableNS) {
authority_->setName(Name("example.com."));
nsas.lookupAndAnswer("example.com.", RRClass::IN(), authority_,
getCallback());
+
// It should ask nothing more now
EXPECT_EQ(2, resolver_->requests.size());
@@ -313,12 +352,14 @@ TEST_F(NameserverAddressStoreTest, unreachableNS) {
// We should have 2 answers now
EXPECT_EQ(2, NSASCallback::results.size());
+
// When we ask one same and one other zone with the same nameserver,
// it should generate no questions and answer right away
nsas.lookup("example.net.", RRClass::IN(), getCallback());
authority_->setName(Name("example.org."));
nsas.lookupAndAnswer("example.org.", RRClass::IN(), authority_,
getCallback());
+
// There should be 4 negative answers now
EXPECT_EQ(4, NSASCallback::results.size());
BOOST_FOREACH(const NSASCallback::Result& result, NSASCallback::results) {
@@ -326,25 +367,28 @@ TEST_F(NameserverAddressStoreTest, unreachableNS) {
}
}
-/**
- * \short Try to stress it little bit by having multiple zones and nameservers.
- *
- * Does some asking, on a set of zones that share some nameservers, with
- * slower answering, evicting data, etc.
- */
+/// \short Try to stress it little bit by having multiple zones and nameservers.
+///
+/// Does some asking, on a set of zones that share some nameservers, with
+/// slower answering, evicting data, etc.
TEST_F(NameserverAddressStoreTest, CombinedTest) {
+
// Create small caches, so we get some evictions
DerivedNsas nsas(resolver_, 1, 1);
+
// Ask for example.net. It has single nameserver out of the zone
nsas.lookupAndAnswer("example.net.", RRClass::IN(), authority_,
getCallback());
+
// It should ask for the nameserver IP addresses
EXPECT_NO_THROW(resolver_->asksIPs(Name("ns.example.com."), 0, 1));
EXPECT_EQ(0, NSASCallback::results.size());
+
// But we do not answer it right away. We create a new zone and
// let this nameserver entry get out.
rrns_->addRdata(rdata::generic::NS("example.cz"));
nsas.lookupAndAnswer(EXAMPLE_CO_UK, RRClass::IN(), rrns_, getCallback());
+
// It really should ask something, one of the nameservers
// (or both)
ASSERT_GT(resolver_->requests.size(), 2);
@@ -354,8 +398,8 @@ TEST_F(NameserverAddressStoreTest, CombinedTest) {
EXPECT_NO_THROW(resolver_->asksIPs(name, 2, 3));
EXPECT_EQ(0, NSASCallback::results.size());
- size_t request_count(resolver_->requests.size());
// This should still be in the hash table, so try it asks no more questions
+ size_t request_count(resolver_->requests.size());
nsas.lookup("example.net.", RRClass::IN(), getCallback());
EXPECT_EQ(request_count, resolver_->requests.size());
EXPECT_EQ(0, NSASCallback::results.size());
@@ -363,6 +407,7 @@ TEST_F(NameserverAddressStoreTest, CombinedTest) {
// We respond to one of the 3 nameservers
EXPECT_NO_THROW(resolver_->answer(2, name, RRType::A(),
rdata::in::A("192.0.2.1")));
+
// That should trigger one answer
EXPECT_EQ(1, NSASCallback::results.size());
EXPECT_TRUE(NSASCallback::results[0].first);
@@ -370,6 +415,7 @@ TEST_F(NameserverAddressStoreTest, CombinedTest) {
NSASCallback::results[0].second.getAddress().toText());
EXPECT_NO_THROW(resolver_->answer(3, name, RRType::AAAA(),
rdata::in::AAAA("2001:bd8::1")));
+
// And there should be yet another query
ASSERT_GT(resolver_->requests.size(), 4);
EXPECT_NE(name, resolver_->requests[4].first->getName());
@@ -387,9 +433,9 @@ TEST_F(NameserverAddressStoreTest, CombinedTest) {
EXPECT_EQ(request_count + 2, resolver_->requests.size());
EXPECT_NO_THROW(resolver_->asksIPs(Name("ns.example.com."), request_count,
request_count + 1));
- // Now, we answer both queries for the same address
- // and three (one for the original, one for this one) more answers should
- // arrive
+
+ // Now, we answer both queries for the same address and three (one for the
+ // original, one for this one) more answers should arrive
NSASCallback::results.clear();
EXPECT_NO_THROW(resolver_->answer(0, Name("ns.example.com."), RRType::A(),
rdata::in::A("192.0.2.2")));
@@ -402,5 +448,113 @@ TEST_F(NameserverAddressStoreTest, CombinedTest) {
}
}
+// Check that we can update the RTT associated with nameservers successfully.
+// Also checks that we can't set the RTT to zero (which would cause problems
+// with selection algorithm).
+TEST_F(NameserverAddressStoreTest, updateRTT) {
+
+ // Initialization.
+ string zone_name = "example.net.";
+ string ns_name = "ns.example.com.";
+ vector<string> address;
+ address.push_back("192.0.2.1");
+ address.push_back("192.0.2.2");
+
+ uint32_t HIGH_RTT = 10000; // 1E4; When squared, the result fits in 32 bits
+
+ DerivedNsas nsas(resolver_, 103, 107); // Arbitrary cache sizes
+
+ // Ensure that location holding the addresses returned is empty. We'll
+ // be using this throughout the tests. As the full name is a bit of a
+ // mouthful, set up an alias.
+ vector<NameserverAddressStoreTest::NSASCallback::Result>& results =
+ NameserverAddressStoreTest::NSASCallback::results;
+ results.clear();
+
+ // Initialize the test resolver with the answer for the A record query
+ // for ns.example.com (the nameserver set for example.net in the class
+ // initialization). We'll set two addresses.
+ Name ns_example_com(ns_name);
+ RRsetPtr ns_address = boost::shared_ptr<RRset>(new RRset(
+ ns_example_com, RRClass::IN(), RRType::A(), RRTTL(300)));
+ BOOST_FOREACH(string addr, address) {
+ ns_address->addRdata(rdata::in::A(addr));
+ }
+
+ // All set. Ask for example.net.
+ boost::shared_ptr<AddressRequestCallback> callback = getCallback();
+ nsas.lookupAndAnswer(zone_name, RRClass::IN(), authority_, getCallback());
+
+ // This should generate two requests - one for A and one for AAAA.
+ EXPECT_EQ(2, resolver_->requests.size());
+
+ // Provide an answer that has two A records. This should generate one
+ // result.
+ EXPECT_NO_THROW(resolver_->answer(0, ns_address));
+ EXPECT_EQ(1, results.size());
+
+ // We expect the lookup to be successful. Check that the address is one of
+ // the two we've set and that the RTT associated with this nameserver is
+ // non-zero.
+ EXPECT_EQ(true, results[0].first);
+ vector<string>::iterator addr1 = find(address.begin(), address.end(),
+ results[0].second.getAddress().toText());
+ EXPECT_TRUE(addr1 != address.end());
+
+ // The RTT we got should be non-zero and less than the high value we are
+ // using for the test.
+ uint32_t rtt1 = results[0].second.getAddressEntry().getRTT();
+ EXPECT_NE(0, rtt1);
+ EXPECT_LT(rtt1, HIGH_RTT);
+
+ // Update the address with a very high RTT. Owning to the way the NSAS is
+ // written, we can update the RTT but cannot read the new value back from
+ // the new object.
+ results[0].second.updateRTT(HIGH_RTT);
+
+ // Get another nameserver. As the probability of returning a particular
+ // address is proporational to 1/t^2, we should get the second address
+ // since the first now has a larger RTT. However, this is not guaranteed
+ // - this is a probability (albeit small) of getting the first again. We'll
+ // allow three chances of getting the "wrong" address before we declare
+ // an error.
+ int attempt = 0;
+ vector<string>::iterator addr2 = addr1;
+ for (attempt = 0; (attempt < 3) && (*addr1 == *addr2); ++attempt) {
+ results.clear();
+ nsas.lookup(zone_name, RRClass::IN(),
+ getCallback(), ANY_OK);
+ addr2 = find(address.begin(), address.end(),
+ results[0].second.getAddress().toText());
+ }
+ EXPECT_LT(attempt, 3);
+
+ // Ensure that the RTT is non-zero.
+ // obtained earlier.
+ uint32_t rtt2 = results[0].second.getAddressEntry().getRTT();
+ EXPECT_NE(0, rtt2);
+
+ // The test has shown that the code can return the two nameservers. Now
+ // try to set the RTT for the last one returned to zero. As there is a
+ // smoothing effect in the calculations which damps out an abrupt change
+ // in the RTT, the underlying RTT will not be set to zero immediately. So
+ // loop a large number of times, each time setting it to zero.
+ //
+ // Between each setting of the RTT, we have to retrieve the nameserver from
+ // the NSAS again. This means that we _could_ occasionally get the address
+ // of the one whose RTT we have raised to HIGH_RTT. We overcome this by
+ // looping a _very_ large number of times. Ultimately the RTT of both
+ // addresses should decay to a small value.
+ for (int i = 0; i < 2000; ++i) { // 1000 times for each nameserver
+ results.clear();
+ nsas.lookup(zone_name, RRClass::IN(), getCallback(), ANY_OK);
+ EXPECT_EQ(1, results.size());
+ uint32_t rtt3 = results[0].second.getAddressEntry().getRTT();
+ EXPECT_NE(0, rtt3);
+ results[0].second.updateRTT(0);
+ }
+}
+
+
} // namespace nsas
} // namespace isc
diff --git a/src/lib/nsas/tests/nameserver_address_unittest.cc b/src/lib/nsas/tests/nameserver_address_unittest.cc
index 1f924b3..1b211e9 100644
--- a/src/lib/nsas/tests/nameserver_address_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_address_unittest.cc
@@ -39,7 +39,9 @@ class NameserverEntrySample {
public:
NameserverEntrySample():
name_("example.org"),
- rrv4_(new RRset(name_, RRClass::IN(), RRType::A(), RRTTL(1200)))
+ rrv4_(new RRset(name_, RRClass::IN(), RRType::A(), RRTTL(1200))),
+ ns_(new NameserverEntry(name_.toText(), RRClass::IN())),
+ resolver_(new TestResolver())
{
// Add some sample A records
rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("1.2.3.4")));
@@ -47,17 +49,17 @@ public:
rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("9.10.11.12")));
ns_.reset(new NameserverEntry(name_.toText(), RRClass::IN()));
- boost::shared_ptr<TestResolver> resolver(new TestResolver);
- ns_->askIP(resolver, boost::shared_ptr<Callback>(new Callback), ANY_OK);
- resolver->asksIPs(name_, 0, 1);
- resolver->requests[0].second->success(createResponseMessage(rrv4_));
+ ns_->askIP(resolver_.get(), boost::shared_ptr<Callback>(new Callback), ANY_OK);
+ resolver_->asksIPs(name_, 0, 1);
+ resolver_->requests[0].second->success(
+ isc::util::unittests::createResponseMessage(rrv4_));
}
// Return the sample NameserverEntry
boost::shared_ptr<NameserverEntry>& getNameserverEntry() { return ns_; }
// Return the IOAddress corresponding to the index in rrv4_
- asiolink::IOAddress getAddressAtIndex(uint32_t index) {
+ isc::asiolink::IOAddress getAddressAtIndex(uint32_t index) {
return ns_.get()->getAddressAtIndex(index, V4_ONLY);
}
@@ -75,6 +77,7 @@ private:
Name name_; ///< Name of the sample
RRsetPtr rrv4_; ///< Standard RRSet - IN, A, lowercase name
boost::shared_ptr<NameserverEntry> ns_; ///< Shared_ptr that points to a NameserverEntry object
+ boost::shared_ptr<TestResolver> resolver_;
class Callback : public NameserverEntry::Callback {
public:
@@ -105,7 +108,7 @@ TEST_F(NameserverAddressTest, Address) {
boost::shared_ptr<NameserverEntry> empty_ne((NameserverEntry*)NULL);
// It will throw an NullNameserverEntryPointer exception with the empty NameserverEntry shared pointer
ASSERT_THROW({NameserverAddress empty_ns_address(empty_ne,
- asiolink::IOAddress("127.0.0.1"), V4_ONLY);},
+ isc::asiolink::IOAddress("127.0.0.1"), V4_ONLY);},
NullNameserverEntryPointer);
}
diff --git a/src/lib/nsas/tests/nameserver_entry_unittest.cc b/src/lib/nsas/tests/nameserver_entry_unittest.cc
index 398c568..3435d26 100644
--- a/src/lib/nsas/tests/nameserver_entry_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_entry_unittest.cc
@@ -39,7 +39,7 @@
#include "nsas_test.h"
using namespace isc::nsas;
-using namespace asiolink;
+using namespace isc::asiolink;
using namespace std;
using namespace isc::dns;
using namespace rdata;
@@ -72,7 +72,8 @@ private:
RRsetPtr set)
{
if (set) {
- resolver->requests[index].second->success(createResponseMessage(set));
+ resolver->requests[index].second->success(
+ isc::util::unittests::createResponseMessage(set));
} else {
resolver->requests[index].second->failure();
}
@@ -86,7 +87,7 @@ protected:
boost::shared_ptr<TestResolver> resolver(new TestResolver);
boost::shared_ptr<Callback> callback(new Callback);
// Let it ask for data
- entry->askIP(resolver, callback, ANY_OK);
+ entry->askIP(resolver.get(), callback, ANY_OK);
// Check it really asked and sort the queries
EXPECT_TRUE(resolver->asksIPs(Name(entry->getName()), 0, 1));
// Respond with answers
@@ -266,7 +267,7 @@ TEST_F(NameserverEntryTest, IPCallbacks) {
boost::shared_ptr<Callback> callback(new Callback);
boost::shared_ptr<TestResolver> resolver(new TestResolver);
- entry->askIP(resolver, callback, ANY_OK);
+ entry->askIP(resolver.get(), callback, ANY_OK);
// Ensure it becomes IN_PROGRESS
EXPECT_EQ(Fetchable::IN_PROGRESS, entry->getState());
// Now, there should be two queries in the resolver
@@ -274,12 +275,12 @@ TEST_F(NameserverEntryTest, IPCallbacks) {
ASSERT_TRUE(resolver->asksIPs(Name(EXAMPLE_CO_UK), 0, 1));
// Another one might ask
- entry->askIP(resolver, callback, V4_ONLY);
+ entry->askIP(resolver.get(), callback, V4_ONLY);
// There should still be only two queries in the resolver
ASSERT_EQ(2, resolver->requests.size());
// Another one, with need of IPv6 address
- entry->askIP(resolver, callback, V6_ONLY);
+ entry->askIP(resolver.get(), callback, V6_ONLY);
// Answer one and see that the callbacks are called
resolver->answer(0, Name(EXAMPLE_CO_UK), RRType::A(),
@@ -316,7 +317,7 @@ TEST_F(NameserverEntryTest, IPCallbacksUnreachable) {
boost::shared_ptr<TestResolver> resolver(new TestResolver);
// Ask for its IP
- entry->askIP(resolver, callback, ANY_OK);
+ entry->askIP(resolver.get(), callback, ANY_OK);
// Check it asks the resolver
EXPECT_EQ(2, resolver->requests.size());
ASSERT_TRUE(resolver->asksIPs(Name(EXAMPLE_CO_UK), 0, 1));
@@ -352,7 +353,7 @@ TEST_F(NameserverEntryTest, DirectAnswer) {
RRType::AAAA()), RRsetPtr());
// A successfull test first
- entry->askIP(resolver, callback, ANY_OK);
+ entry->askIP(resolver.get(), callback, ANY_OK);
EXPECT_EQ(0, resolver->requests.size());
EXPECT_EQ(1, callback->count);
NameserverEntry::AddressVector addresses;
@@ -362,7 +363,7 @@ TEST_F(NameserverEntryTest, DirectAnswer) {
// An unsuccessfull test
callback->count = 0;
entry.reset(new NameserverEntry(EXAMPLE_NET, RRClass::IN()));
- entry->askIP(resolver, callback, ANY_OK);
+ entry->askIP(resolver.get(), callback, ANY_OK);
EXPECT_EQ(0, resolver->requests.size());
EXPECT_EQ(1, callback->count);
addresses.clear();
@@ -381,8 +382,8 @@ TEST_F(NameserverEntryTest, ChangedExpired) {
boost::shared_ptr<TestResolver> resolver(new TestResolver);
// Ask the first time
- entry->askIP(resolver, callback, V4_ONLY);
- entry->askIP(resolver, callback, V6_ONLY);
+ entry->askIP(resolver.get(), callback, V4_ONLY);
+ entry->askIP(resolver.get(), callback, V6_ONLY);
EXPECT_TRUE(resolver->asksIPs(Name(EXAMPLE_CO_UK), 0, 1));
EXPECT_EQ(Fetchable::IN_PROGRESS, entry->getState());
resolver->answer(0, Name(EXAMPLE_CO_UK), RRType::A(),
@@ -402,8 +403,8 @@ TEST_F(NameserverEntryTest, ChangedExpired) {
// Ask the second time. The callbacks should not fire right away and it
// should request the addresses again
- entry->askIP(resolver, callback, V4_ONLY);
- entry->askIP(resolver, callback, V6_ONLY);
+ entry->askIP(resolver.get(), callback, V4_ONLY);
+ entry->askIP(resolver.get(), callback, V6_ONLY);
EXPECT_EQ(2, callback->count);
EXPECT_TRUE(resolver->asksIPs(Name(EXAMPLE_CO_UK), 2, 3));
EXPECT_EQ(Fetchable::IN_PROGRESS, entry->getState());
@@ -431,8 +432,8 @@ TEST_F(NameserverEntryTest, KeepRTT) {
boost::shared_ptr<TestResolver> resolver(new TestResolver);
// Ask the first time
- entry->askIP(resolver, callback, V4_ONLY);
- entry->askIP(resolver, callback, V6_ONLY);
+ entry->askIP(resolver.get(), callback, V4_ONLY);
+ entry->askIP(resolver.get(), callback, V6_ONLY);
EXPECT_TRUE(resolver->asksIPs(Name(EXAMPLE_CO_UK), 0, 1));
EXPECT_EQ(Fetchable::IN_PROGRESS, entry->getState());
resolver->answer(0, Name(EXAMPLE_CO_UK), RRType::A(),
@@ -455,8 +456,8 @@ TEST_F(NameserverEntryTest, KeepRTT) {
// Ask the second time. The callbacks should not fire right away and it
// should request the addresses again
- entry->askIP(resolver, callback, V4_ONLY);
- entry->askIP(resolver, callback, V6_ONLY);
+ entry->askIP(resolver.get(), callback, V4_ONLY);
+ entry->askIP(resolver.get(), callback, V6_ONLY);
EXPECT_EQ(2, callback->count);
EXPECT_TRUE(resolver->asksIPs(Name(EXAMPLE_CO_UK), 2, 3));
EXPECT_EQ(Fetchable::IN_PROGRESS, entry->getState());
@@ -513,6 +514,11 @@ TEST_F(NameserverEntryTest, UpdateRTT) {
// The rtt should be close to stable rtt value
EXPECT_TRUE((stable_rtt - new_rtt) < (new_rtt - init_rtt));
+
+ // Finally, try updating the RTT to a very large value (large enough for
+ // RTT^2 - used in the internal calculation - to exceed a 32-bit value).
+ EXPECT_NO_THROW(vec[0].updateRTT(1000000000)); // 10^9
+
}
} // namespace
diff --git a/src/lib/nsas/tests/nsas_test.h b/src/lib/nsas/tests/nsas_test.h
index 926e859..2dd95ef 100644
--- a/src/lib/nsas/tests/nsas_test.h
+++ b/src/lib/nsas/tests/nsas_test.h
@@ -26,8 +26,9 @@
#include <config.h>
+#include <util/buffer.h>
+#include <util/unittests/resolver.h>
#include <dns/message.h>
-#include <dns/buffer.h>
#include <dns/rdata.h>
#include <dns/rrtype.h>
#include <dns/rrttl.h>
@@ -35,23 +36,12 @@
#include <dns/rcode.h>
#include <dns/messagerenderer.h>
#include <dns/rdataclass.h>
-#include <resolve/resolver_interface.h>
#include "../nsas_entry.h"
using namespace isc::dns::rdata;
using namespace isc::dns;
-
-namespace {
- MessagePtr
- createResponseMessage(RRsetPtr answer_rrset)
- {
- MessagePtr response(new Message(Message::RENDER));
- response->setOpcode(Opcode::QUERY());
- response->setRcode(Rcode::NOERROR());
- response->addRRset(Message::SECTION_ANSWER, answer_rrset);
- return response;
- }
-}
+using namespace isc::util;
+using isc::util::unittests::TestResolver;
namespace isc {
namespace dns {
@@ -124,7 +114,7 @@ public:
virtual void toWire(OutputBuffer& buffer) const;
/// \brief render the \Rdata in the wire format to a \c MessageRenderer
- virtual void toWire(MessageRenderer& renderer) const;
+ virtual void toWire(AbstractMessageRenderer& renderer) const;
/// \brief Comparison Method
virtual int compare(const Rdata& other) const;
@@ -140,7 +130,7 @@ void RdataTest<T>::toWire(OutputBuffer&) const {
}
template <typename T>
-void RdataTest<T>::toWire(MessageRenderer&) const {
+void RdataTest<T>::toWire(AbstractMessageRenderer&) const {
}
template <typename T>
@@ -222,126 +212,6 @@ private:
static const uint32_t HASHTABLE_DEFAULT_SIZE = 1009; ///< First prime above 1000
-} // namespace nsas
-} // namespace isc
-
-namespace {
-
-using namespace std;
-
-/*
- * This pretends to be a resolver. It stores the queries and
- * they can be answered.
- */
-class TestResolver : public isc::resolve::ResolverInterface {
- private:
- bool checkIndex(size_t index) {
- return (requests.size() > index);
- }
-
- typedef std::map<isc::dns::Question, RRsetPtr >
- PresetAnswers;
- PresetAnswers answers_;
- public:
- typedef pair<QuestionPtr, CallbackPtr> Request;
- vector<Request> requests;
- virtual void resolve(const QuestionPtr& q, const CallbackPtr& c) {
- PresetAnswers::iterator it(answers_.find(*q));
- if (it == answers_.end()) {
- requests.push_back(Request(q, c));
- } else {
- if (it->second) {
- c->success(createResponseMessage(it->second));
- } else {
- c->failure();
- }
- }
- }
-
- /*
- * Add a preset answer. If shared_ptr() is passed (eg. NULL),
- * it will generate failure. If the question is not preset,
- * it goes to requests and you can answer later.
- */
- void addPresetAnswer(const isc::dns::Question& question,
- RRsetPtr answer)
- {
- answers_[question] = answer;
- }
-
- // Thrown if the query at the given index does not exist.
- class NoSuchRequest : public std::exception { };
-
- // Thrown if the answer does not match the query
- class DifferentRequest : public std::exception { };
-
- QuestionPtr operator[](size_t index) {
- if (index >= requests.size()) {
- throw NoSuchRequest();
- }
- return (requests[index].first);
- }
- /*
- * Looks if the two provided requests in resolver are A and AAAA.
- * Sorts them so index1 is A.
- *
- * Returns false if there aren't enough elements
- */
- bool asksIPs(const Name& name, size_t index1, size_t index2) {
- size_t max = (index1 < index2) ? index2 : index1;
- if (!checkIndex(max)) {
- return false;
- }
- EXPECT_EQ(name, (*this)[index1]->getName());
- EXPECT_EQ(name, (*this)[index2]->getName());
- EXPECT_EQ(RRClass::IN(), (*this)[index1]->getClass());
- EXPECT_EQ(RRClass::IN(), (*this)[index2]->getClass());
- // If they are the other way around, swap
- if ((*this)[index1]->getType() == RRType::AAAA() &&
- (*this)[index2]->getType() == RRType::A())
- {
- TestResolver::Request tmp((*this).requests[index1]);
- (*this).requests[index1] =
- (*this).requests[index2];
- (*this).requests[index2] = tmp;
- }
- // Check the correct addresses
- EXPECT_EQ(RRType::A(), (*this)[index1]->getType());
- EXPECT_EQ(RRType::AAAA(), (*this)[index2]->getType());
- return (true);
- }
-
- /*
- * Sends a simple answer to a query.
- * Provide index of a query and the address to pass.
- */
- void answer(size_t index, const Name& name, const RRType& type,
- const rdata::Rdata& rdata, size_t TTL = 100)
- {
- if (index >= requests.size()) {
- throw NoSuchRequest();
- }
- RRsetPtr set(new RRset(name, RRClass::IN(),
- type, RRTTL(TTL)));
- set->addRdata(rdata);
- requests[index].second->success(createResponseMessage(set));
- }
-
- void provideNS(size_t index,
- RRsetPtr nameservers)
- {
- if (index >= requests.size()) {
- throw NoSuchRequest();
- }
- if (requests[index].first->getName() != nameservers->getName() ||
- requests[index].first->getType() != RRType::NS())
- {
- throw DifferentRequest();
- }
- requests[index].second->success(createResponseMessage(nameservers));
- }
-};
-
// String constants. These should end in a dot.
static const std::string EXAMPLE_CO_UK("example.co.uk.");
static const std::string EXAMPLE_NET("example.net.");
@@ -420,6 +290,7 @@ protected:
Name ns_name_; ///< Nameserver name of ns.example.net
};
-} // Empty namespace
+} // namespace nsas
+} // namespace isc
#endif // __NSAS_TEST_H
diff --git a/src/lib/nsas/tests/random_number_generator_unittest.cc b/src/lib/nsas/tests/random_number_generator_unittest.cc
deleted file mode 100644
index c306b09..0000000
--- a/src/lib/nsas/tests/random_number_generator_unittest.cc
+++ /dev/null
@@ -1,307 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <config.h>
-
-#include <gtest/gtest.h>
-#include <boost/shared_ptr.hpp>
-
-#include <algorithm>
-#include <iostream>
-#include <vector>
-
-#include "random_number_generator.h"
-
-namespace isc {
-namespace nsas {
-
-using namespace std;
-
-/// \brief Test Fixture Class for uniform random number generator
-///
-/// The hard part for this test is how to test that the number is random?
-/// and how to test that the number is uniformly distributed?
-/// Or maybe we can trust the boost implementation
-class UniformRandomIntegerGeneratorTest : public ::testing::Test {
-public:
- UniformRandomIntegerGeneratorTest():
- gen_(min_, max_)
- {
- }
- virtual ~UniformRandomIntegerGeneratorTest(){}
-
- int gen() { return gen_(); }
- int max() const { return max_; }
- int min() const { return min_; }
-
-private:
- UniformRandomIntegerGenerator gen_;
-
- const static int min_ = 1;
- const static int max_ = 10;
-};
-
-// Some validation tests will incur performance penalty, so the tests are
-// made only in "debug" version with assert(). But if NDEBUG is defined
-// the tests will be failed since assert() is non-op in non-debug version.
-// The "#ifndef NDEBUG" is added to make the tests be performed only in
-// non-debug environment.
-// Note: the death test is not supported by all platforms. We need to
-// compile tests using it selectively.
-#if !defined(NDEBUG) && defined(GTEST_HAS_DEATH_TEST)
-// Test of the constructor
-TEST_F(UniformRandomIntegerGeneratorTest, Constructor) {
- // The range must be min<=max
- ASSERT_DEATH(UniformRandomIntegerGenerator(3, 2), "");
-}
-#endif
-
-// Test of the generated integers are in the range [min, max]
-TEST_F(UniformRandomIntegerGeneratorTest, IntegerRange) {
- vector<int> numbers;
-
- // Generate a lot of random integers
- for (int i = 0; i < max()*10; ++i) {
- numbers.push_back(gen());
- }
-
- // Remove the duplicated values
- sort(numbers.begin(), numbers.end());
- vector<int>::iterator it = unique(numbers.begin(), numbers.end());
-
- // make sure the numbers are in range [min, max]
- ASSERT_EQ(it - numbers.begin(), max() - min() + 1);
-}
-
-/// \brief Test Fixture Class for weighted random number generator
-class WeightedRandomIntegerGeneratorTest : public ::testing::Test {
-public:
- WeightedRandomIntegerGeneratorTest()
- { }
-
- virtual ~WeightedRandomIntegerGeneratorTest()
- { }
-};
-
-// Test of the weighted random number generator constructor
-TEST_F(WeightedRandomIntegerGeneratorTest, Constructor) {
- vector<double> probabilities;
-
- // If no probabilities is provided, the smallest integer will always be generated
- WeightedRandomIntegerGenerator gen(probabilities, 123);
- for (int i = 0; i < 100; ++i) {
- ASSERT_EQ(gen(), 123);
- }
-
-/// Some validation tests will incur performance penalty, so the tests are
-/// made only in "debug" version with assert(). But if NDEBUG is defined
-/// the tests will be failed since assert() is non-op in non-debug version.
-/// The "#ifndef NDEBUG" is added to make the tests be performed only in
-/// non-debug environment.
-#if !defined(NDEBUG) && defined(GTEST_HAS_DEATH_TEST)
- //The probability must be >= 0
- probabilities.push_back(-0.1);
- probabilities.push_back(1.1);
- ASSERT_DEATH(WeightedRandomIntegerGenerator gen2(probabilities), "");
-
- //The probability must be <= 1.0
- probabilities.clear();
- probabilities.push_back(0.1);
- probabilities.push_back(1.1);
- ASSERT_DEATH(WeightedRandomIntegerGenerator gen3(probabilities), "");
-
- //The sum must be equal to 1.0
- probabilities.clear();
- probabilities.push_back(0.2);
- probabilities.push_back(0.9);
- ASSERT_DEATH(WeightedRandomIntegerGenerator gen4(probabilities), "");
-
- //The sum must be equal to 1.0
- probabilities.clear();
- probabilities.push_back(0.3);
- probabilities.push_back(0.2);
- probabilities.push_back(0.1);
- ASSERT_DEATH(WeightedRandomIntegerGenerator gen5(probabilities), "");
-#endif
-}
-
-// Test the randomization of the generator
-TEST_F(WeightedRandomIntegerGeneratorTest, WeightedRandomization) {
- const int repeats = 100000;
- // We repeat the simulation for N=repeats times
- // for each probability p, its average is mu = N*p
- // variance sigma^2 = N * p * (1-p)
- // sigma = sqrt(N*2/9)
- // we should make sure that mu - 4sigma < count < mu + 4sigma
- // which means for 99.99366% of the time this should be true
- {
- double p = 0.5;
- vector<double> probabilities;
- probabilities.push_back(p);
- probabilities.push_back(p);
-
- // Uniformly generated integers
- WeightedRandomIntegerGenerator gen(probabilities);
- int c1 = 0;
- int c2 = 0;
- for (int i = 0; i < repeats; ++i){
- int n = gen();
- if (n == 0) {
- ++c1;
- } else if (n == 1) {
- ++c2;
- }
- }
- double mu = repeats * p;
- double sigma = sqrt(repeats * p * (1 - p));
- ASSERT_TRUE(fabs(c1 - mu) < 4*sigma);
- ASSERT_TRUE(fabs(c2 - mu) < 4*sigma);
- }
-
- {
- vector<double> probabilities;
- int c1 = 0;
- int c2 = 0;
- double p1 = 0.2;
- double p2 = 0.8;
- probabilities.push_back(p1);
- probabilities.push_back(p2);
- WeightedRandomIntegerGenerator gen(probabilities);
- for (int i = 0; i < repeats; ++i) {
- int n = gen();
- if (n == 0) {
- ++c1;
- } else if (n == 1) {
- ++c2;
- }
- }
- double mu1 = repeats * p1;
- double mu2 = repeats * p2;
- double sigma1 = sqrt(repeats * p1 * (1 - p1));
- double sigma2 = sqrt(repeats * p2 * (1 - p2));
- ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
- ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
- }
-
- {
- vector<double> probabilities;
- int c1 = 0;
- int c2 = 0;
- double p1 = 0.8;
- double p2 = 0.2;
- probabilities.push_back(p1);
- probabilities.push_back(p2);
- WeightedRandomIntegerGenerator gen(probabilities);
- for (int i = 0; i < repeats; ++i) {
- int n = gen();
- if (n == 0) {
- ++c1;
- } else if (n == 1) {
- ++c2;
- }
- }
- double mu1 = repeats * p1;
- double mu2 = repeats * p2;
- double sigma1 = sqrt(repeats * p1 * (1 - p1));
- double sigma2 = sqrt(repeats * p2 * (1 - p2));
- ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
- ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
- }
-
- {
- vector<double> probabilities;
- int c1 = 0;
- int c2 = 0;
- int c3 = 0;
- double p1 = 0.5;
- double p2 = 0.25;
- double p3 = 0.25;
- probabilities.push_back(p1);
- probabilities.push_back(p2);
- probabilities.push_back(p3);
- WeightedRandomIntegerGenerator gen(probabilities);
- for (int i = 0; i < repeats; ++i){
- int n = gen();
- if (n == 0) {
- ++c1;
- } else if (n == 1) {
- ++c2;
- } else if (n == 2) {
- ++c3;
- }
- }
- double mu1 = repeats * p1;
- double mu2 = repeats * p2;
- double mu3 = repeats * p3;
- double sigma1 = sqrt(repeats * p1 * (1 - p1));
- double sigma2 = sqrt(repeats * p2 * (1 - p2));
- double sigma3 = sqrt(repeats * p3 * (1 - p3));
- ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
- ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
- ASSERT_TRUE(fabs(c3 - mu3) < 4*sigma3);
- }
-}
-
-// Test the reset function of generator
-TEST_F(WeightedRandomIntegerGeneratorTest, ResetProbabilities) {
- const int repeats = 100000;
- vector<double> probabilities;
- int c1 = 0;
- int c2 = 0;
- double p1 = 0.8;
- double p2 = 0.2;
- probabilities.push_back(p1);
- probabilities.push_back(p2);
- WeightedRandomIntegerGenerator gen(probabilities);
- for (int i = 0; i < repeats; ++i) {
- int n = gen();
- if (n == 0) {
- ++c1;
- } else if (n == 1) {
- ++c2;
- }
- }
- double mu1 = repeats * p1;
- double mu2 = repeats * p2;
- double sigma1 = sqrt(repeats * p1 * (1 - p1));
- double sigma2 = sqrt(repeats * p2 * (1 - p2));
- ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
- ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
-
- // Reset the probabilities
- probabilities.clear();
- c1 = c2 = 0;
- p1 = 0.2;
- p2 = 0.8;
- probabilities.push_back(p1);
- probabilities.push_back(p2);
- gen.reset(probabilities);
- for (int i = 0; i < repeats; ++i) {
- int n = gen();
- if (n == 0) {
- ++c1;
- } else if (n == 1) {
- ++c2;
- }
- }
- mu1 = repeats * p1;
- mu2 = repeats * p2;
- sigma1 = sqrt(repeats * p1 * (1 - p1));
- sigma2 = sqrt(repeats * p2 * (1 - p2));
- ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
- ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
-}
-
-} // namespace nsas
-} // namespace isc
diff --git a/src/lib/nsas/tests/run_unittests.cc b/src/lib/nsas/tests/run_unittests.cc
index d1277ad..e469e03 100644
--- a/src/lib/nsas/tests/run_unittests.cc
+++ b/src/lib/nsas/tests/run_unittests.cc
@@ -12,15 +12,13 @@
// 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 <dns/tests/unittest_util.h>
+#include <log/logger_support.h>
+#include <util/unittests/run_all.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
-
- return (RUN_ALL_TESTS());
+ isc::log::initLogger();
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/nsas/tests/zone_entry_unittest.cc b/src/lib/nsas/tests/zone_entry_unittest.cc
index d10f12d..1982299 100644
--- a/src/lib/nsas/tests/zone_entry_unittest.cc
+++ b/src/lib/nsas/tests/zone_entry_unittest.cc
@@ -33,9 +33,10 @@
#include "nsas_test.h"
using namespace isc::nsas;
-using namespace asiolink;
+using namespace isc::asiolink;
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
namespace {
@@ -47,7 +48,7 @@ class InheritedZoneEntry : public ZoneEntry {
const std::string& name, const RRClass& class_code,
boost::shared_ptr<HashTable<NameserverEntry> > nameserver_table,
boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru) :
- ZoneEntry(resolver, name, class_code, nameserver_table,
+ ZoneEntry(resolver.get(), name, class_code, nameserver_table,
nameserver_lru)
{ }
NameserverVector& nameservers() { return nameservers_; }
@@ -569,7 +570,7 @@ TEST_F(ZoneEntryTest, NameserverEntryReady) {
// Inject the entry
boost::shared_ptr<NameserverEntry> nse(injectEntry());
// Fill it with data
- nse->askIP(resolver_, nseCallback(), ANY_OK);
+ nse->askIP(resolver_.get(), nseCallback(), ANY_OK);
EXPECT_EQ(Fetchable::IN_PROGRESS, nse->getState());
EXPECT_TRUE(resolver_->asksIPs(ns_name_, 0, 1));
EXPECT_NO_THROW(resolver_->answer(0, ns_name_, RRType::A(),
@@ -594,7 +595,7 @@ TEST_F(ZoneEntryTest, NameserverEntryNotAsked) {
TEST_F(ZoneEntryTest, NameserverEntryInProgress) {
// Prepare the nameserver entry
boost::shared_ptr<NameserverEntry> nse(injectEntry());
- nse->askIP(resolver_, nseCallback(), ANY_OK);
+ nse->askIP(resolver_.get(), nseCallback(), ANY_OK);
EXPECT_EQ(Fetchable::IN_PROGRESS, nse->getState());
EXPECT_TRUE(resolver_->asksIPs(ns_name_, 0, 1));
@@ -604,7 +605,7 @@ TEST_F(ZoneEntryTest, NameserverEntryInProgress) {
/// \short Check Zone's reaction to found expired nameserver
TEST_F(ZoneEntryTest, NameserverEntryExpired) {
boost::shared_ptr<NameserverEntry> nse(injectEntry());
- nse->askIP(resolver_, nseCallback(), ANY_OK);
+ nse->askIP(resolver_.get(), nseCallback(), ANY_OK);
EXPECT_EQ(Fetchable::IN_PROGRESS, nse->getState());
EXPECT_TRUE(resolver_->asksIPs(ns_name_, 0, 1));
EXPECT_NO_THROW(resolver_->answer(0, ns_name_, RRType::A(),
@@ -623,7 +624,7 @@ TEST_F(ZoneEntryTest, NameserverEntryExpired) {
/// \short Check how it reacts to an unreachable zone already in the table
TEST_F(ZoneEntryTest, NameserverEntryUnreachable) {
boost::shared_ptr<NameserverEntry> nse(injectEntry());
- nse->askIP(resolver_, nseCallback(), ANY_OK);
+ nse->askIP(resolver_.get(), nseCallback(), ANY_OK);
ASSERT_EQ(2, resolver_->requests.size());
resolver_->requests[0].second->failure();
resolver_->requests[1].second->failure();
diff --git a/src/lib/nsas/zone_entry.cc b/src/lib/nsas/zone_entry.cc
index 36e01d2..1c5df03 100644
--- a/src/lib/nsas/zone_entry.cc
+++ b/src/lib/nsas/zone_entry.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 CZ NIC
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -31,12 +31,14 @@ using namespace std;
namespace isc {
-using namespace dns;
+using namespace isc::dns;
+using namespace isc::util;
+using namespace isc::util::random;
namespace nsas {
ZoneEntry::ZoneEntry(
- boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
+ isc::resolve::ResolverInterface* resolver,
const std::string& name, const isc::dns::RRClass& class_code,
boost::shared_ptr<HashTable<NameserverEntry> > nameserver_table,
boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru) :
@@ -51,7 +53,7 @@ ZoneEntry::ZoneEntry(
namespace {
// Shorter aliases for frequently used types
-typedef isc::locks::scoped_lock<isc::locks::recursive_mutex> Lock; // Local lock, nameservers not locked
+typedef isc::util::locks::scoped_lock<isc::util::locks::recursive_mutex> Lock; // Local lock, nameservers not locked
typedef boost::shared_ptr<AddressRequestCallback> CallbackPtr;
/*
@@ -122,7 +124,7 @@ class ZoneEntry::ResolverCallback :
* do), so we can just reuse them instead of looking them up in
* the table or creating them.
*/
- map<string, NameserverPtr> old;
+ std::map<string, NameserverPtr> old;
BOOST_FOREACH(const NameserverPtr& ptr, entry_->nameservers_) {
old[ptr->getName()] = ptr;
}
@@ -224,7 +226,8 @@ class ZoneEntry::ResolverCallback :
};
void
-ZoneEntry::addCallback(CallbackPtr callback, AddressFamily family) {
+ZoneEntry::addCallback(CallbackPtr callback, AddressFamily family,
+ const GlueHints& glue_hints) {
Lock lock(mutex_);
bool ask(false);
@@ -238,11 +241,18 @@ ZoneEntry::addCallback(CallbackPtr callback, AddressFamily family) {
if (getState() == EXPIRED || getState() == NOT_ASKED) {
ask = true;
}
-
+
// We do not have the answer right away, just queue the callback
bool execute(!ask && getState() != IN_PROGRESS &&
callbacks_[family].empty());
- callbacks_[family].push_back(callback);
+
+ // Unless there was glue
+ if (ask && glue_hints.hasGlue(family)) {
+ callback->success(glue_hints.getGlue(family));
+ } else {
+ callbacks_[family].push_back(callback);
+ }
+
if (execute) {
// Try to process it right away, store if not possible to handle
process(family, NameserverPtr());
diff --git a/src/lib/nsas/zone_entry.h b/src/lib/nsas/zone_entry.h
index a1f12bc..f772784 100644
--- a/src/lib/nsas/zone_entry.h
+++ b/src/lib/nsas/zone_entry.h
@@ -25,13 +25,15 @@
#include <resolve/resolver_interface.h>
-#include "locks.h"
+#include <util/locks.h>
+#include <util/random/random_number_generator.h>
+
#include "hash_key.h"
#include "nsas_entry.h"
#include "asiolink.h"
#include "fetchable.h"
#include "nsas_types.h"
-#include "random_number_generator.h"
+#include "glue_hints.h"
namespace isc {
namespace nsas {
@@ -68,11 +70,10 @@ public:
* \todo Move to cc file, include the lookup (if NSAS uses resolver for
* everything)
*/
- ZoneEntry(
- boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
+ ZoneEntry(isc::resolve::ResolverInterface* resolver,
const std::string& name, const isc::dns::RRClass& class_code,
boost::shared_ptr<HashTable<NameserverEntry> > nameserver_table,
- boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru);
+ boost::shared_ptr<isc::util::LruList<NameserverEntry> > nameserver_lru);
/// \return Name of the zone
std::string getName() const {
@@ -97,9 +98,13 @@ public:
*
* \param callback The callback itself.
* \param family Which address family is acceptable as an answer?
+ * \param glue_hints If a non-empty glue-hints object is passed,
+ * and the NSAS does not have an immediate answer, it will
+ * call back immediately with one of the glue hints.
*/
void addCallback(boost::shared_ptr<AddressRequestCallback>
- callback, AddressFamily family);
+ callback, AddressFamily family,
+ const GlueHints& glue_hints = GlueHints());
/**
* \short Remove a callback from the list
@@ -128,7 +133,7 @@ protected:
time_t expiry_; ///< Expiry time of this entry, 0 means not set
//}@
private:
- mutable isc::locks::recursive_mutex mutex_;///< Mutex protecting this zone entry
+ mutable isc::util::locks::recursive_mutex mutex_;///< Mutex protecting this zone entry
std::string name_; ///< Canonical zone name
isc::dns::RRClass class_code_; ///< Class code
/**
@@ -153,11 +158,11 @@ private:
void process(AddressFamily family,
const boost::shared_ptr<NameserverEntry>& nameserver);
// Resolver we use
- boost::shared_ptr<isc::resolve::ResolverInterface> resolver_;
+ isc::resolve::ResolverInterface* resolver_;
// We store the nameserver table and lru, so we can look up when there's
// update
boost::shared_ptr<HashTable<NameserverEntry> > nameserver_table_;
- boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru_;
+ boost::shared_ptr<isc::util::LruList<NameserverEntry> > nameserver_lru_;
// Resolver callback class, documentation with the class declaration
class ResolverCallback;
// It has direct access to us
@@ -178,7 +183,7 @@ private:
void insertCallback(NameserverPtr nameserver, AddressFamily family);
// A random generator for this zone entry
// TODO: A more global one? Per thread one?
- WeightedRandomIntegerGenerator address_selector;
+ isc::util::random::WeightedRandomIntegerGenerator address_selector;
};
} // namespace nsas
diff --git a/src/lib/python/Makefile.am b/src/lib/python/Makefile.am
index f7eb333..5924294 100644
--- a/src/lib/python/Makefile.am
+++ b/src/lib/python/Makefile.am
@@ -1,6 +1,7 @@
SUBDIRS = isc
python_PYTHON = bind10_config.py
+pythondir = $(pyexecdir)
# Explicitly define DIST_COMMON so ${python_PYTHON} is not included
# as we don't want the generated file included in distributed tarfile.
@@ -10,3 +11,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in bind10_config.py.in
EXTRA_DIST = bind10_config.py.in
CLEANFILES = bind10_config.pyc
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/bind10_config.py.in b/src/lib/python/bind10_config.py.in
index 3f2947d..69b17ed 100644
--- a/src/lib/python/bind10_config.py.in
+++ b/src/lib/python/bind10_config.py.in
@@ -17,7 +17,38 @@
# variables to python scripts and libraries.
import os
-BIND10_MSGQ_SOCKET_FILE = os.path.join("@localstatedir@",
- "@PACKAGE_NAME@",
- "msgq_socket").replace("${prefix}",
- "@prefix@")
+def reload():
+ # In a function, for testing purposes
+ global BIND10_MSGQ_SOCKET_FILE
+ global DATA_PATH
+ global PLUGIN_PATHS
+ global PREFIX
+ BIND10_MSGQ_SOCKET_FILE = os.path.join("@localstatedir@",
+ "@PACKAGE_NAME@",
+ "msgq_socket").replace("${prefix}",
+ "@prefix@")
+ PREFIX = "@prefix@"
+
+ # If B10_FROM_SOURCE is set in the environment, we use data files
+ # from a directory relative to the value of that variable, or, if defined,
+ # relative to the value of B10_FROM_SOURCE_LOCALSTATEDIR. Otherwise
+ # we use the ones installed on the system.
+ # B10_FROM_SOURCE_LOCALSTATEDIR is specifically intended to be used for
+ # tests where we want to use variuos types of configuration within the test
+ # environment. (We may want to make it even more generic so that the path is
+ # passed from the boss process)
+ if "B10_FROM_SOURCE" in os.environ:
+ if "B10_FROM_SOURCE_LOCALSTATEDIR" in os.environ:
+ DATA_PATH = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"]
+ else:
+ DATA_PATH = os.environ["B10_FROM_SOURCE"]
+ PLUGIN_PATHS = [os.environ["B10_FROM_SOURCE"] +
+ '/src/bin/cfgmgr/plugins']
+ else:
+ DATA_PATH = "@localstatedir@/@PACKAGE@".replace("${prefix}", PREFIX)
+ PLUGIN_PATHS = ["@prefix@/share/@PACKAGE@/config_plugins"]
+ # For testing the plugins so they can find their own spec files
+ if "B10_TEST_PLUGIN_DIR" in os.environ:
+ PLUGIN_PATHS = os.environ["B10_TEST_PLUGIN_DIR"].split(':')
+
+reload()
diff --git a/src/lib/python/isc/Makefile.am b/src/lib/python/isc/Makefile.am
index bda911b..bfc5a91 100644
--- a/src/lib/python/isc/Makefile.am
+++ b/src/lib/python/isc/Makefile.am
@@ -1,5 +1,10 @@
-SUBDIRS = datasrc cc config log net notify util
+SUBDIRS = datasrc cc config log net notify util testutils
python_PYTHON = __init__.py
pythondir = $(pyexecdir)/isc
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/__init__.py b/src/lib/python/isc/__init__.py
index 9204792..8fcbf42 100644
--- a/src/lib/python/isc/__init__.py
+++ b/src/lib/python/isc/__init__.py
@@ -2,4 +2,3 @@ import isc.datasrc
import isc.cc
import isc.config
#import isc.dns
-import isc.log
diff --git a/src/lib/python/isc/cc/Makefile.am b/src/lib/python/isc/cc/Makefile.am
index a2246db..b0ba3b2 100644
--- a/src/lib/python/isc/cc/Makefile.am
+++ b/src/lib/python/isc/cc/Makefile.am
@@ -3,3 +3,8 @@ SUBDIRS = . tests
python_PYTHON = __init__.py data.py session.py message.py
pythondir = $(pyexecdir)/isc/cc
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/cc/message.py b/src/lib/python/isc/cc/message.py
index 3601c41..3ebcc43 100644
--- a/src/lib/python/isc/cc/message.py
+++ b/src/lib/python/isc/cc/message.py
@@ -35,7 +35,7 @@ def from_wire(data):
Raises an AttributeError if the given object has no decode()
method (which should return a string).
'''
- return json.loads(data.decode('utf8'))
+ return json.loads(data.decode('utf8'), strict=False)
if __name__ == "__main__":
import doctest
diff --git a/src/lib/python/isc/cc/session.py b/src/lib/python/isc/cc/session.py
index fb7dd06..f6b6265 100644
--- a/src/lib/python/isc/cc/session.py
+++ b/src/lib/python/isc/cc/session.py
@@ -93,6 +93,19 @@ class Session:
self._socket.send(msg)
def recvmsg(self, nonblock = True, seq = None):
+ """Reads a message. If nonblock is true, and there is no
+ message to read, it returns (None, None).
+ If seq is not None, it should be a value as returned by
+ group_sendmsg(), in which case only the response to
+ that message is returned, and others will be queued until
+ the next call to this method.
+ If seq is None, only messages that are *not* responses
+ will be returned, and responses will be queued.
+ The queue is checked for relevant messages before data
+ is read from the socket.
+ Raises a SessionError if there is a JSON decode problem in
+ the message that is read, or if the session has been closed
+ prior to the call of recvmsg()"""
with self._lock:
if len(self._queue) > 0:
i = 0;
@@ -109,16 +122,22 @@ class Session:
if data and len(data) > 2:
header_length = struct.unpack('>H', data[0:2])[0]
data_length = len(data) - 2 - header_length
- if data_length > 0:
- env = isc.cc.message.from_wire(data[2:header_length+2])
- msg = isc.cc.message.from_wire(data[header_length + 2:])
- if (seq == None and "reply" not in env) or (seq != None and "reply" in env and seq == env["reply"]):
- return env, msg
+ try:
+ if data_length > 0:
+ env = isc.cc.message.from_wire(data[2:header_length+2])
+ msg = isc.cc.message.from_wire(data[header_length + 2:])
+ if (seq == None and "reply" not in env) or (seq != None and "reply" in env and seq == env["reply"]):
+ return env, msg
+ else:
+ self._queue.append((env,msg))
+ return self.recvmsg(nonblock, seq)
else:
- self._queue.append((env,msg))
- return self.recvmsg(nonblock, seq)
- else:
- return isc.cc.message.from_wire(data[2:header_length+2]), None
+ return isc.cc.message.from_wire(data[2:header_length+2]), None
+ except ValueError as ve:
+ # TODO: when we have logging here, add a debug
+ # message printing the data that we were unable
+ # to parse as JSON
+ raise SessionError(ve)
return None, None
def _receive_bytes(self, size):
diff --git a/src/lib/python/isc/cc/tests/Makefile.am b/src/lib/python/isc/cc/tests/Makefile.am
index dc19758..4e49501 100644
--- a/src/lib/python/isc/cc/tests/Makefile.am
+++ b/src/lib/python/isc/cc/tests/Makefile.am
@@ -6,6 +6,13 @@ EXTRA_DIST = $(PYTESTS)
EXTRA_DIST += sendcmd.py
EXTRA_DIST += test_session.py
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
if ENABLE_PYTHON_COVERAGE
@@ -15,6 +22,7 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
+ $(LIBRARY_PATH_PLACEHOLDER) \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
BIND10_TEST_SOCKET_FILE=$(builddir)/test_socket.sock \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
diff --git a/src/lib/python/isc/cc/tests/message_test.py b/src/lib/python/isc/cc/tests/message_test.py
index 2024201..c417068 100644
--- a/src/lib/python/isc/cc/tests/message_test.py
+++ b/src/lib/python/isc/cc/tests/message_test.py
@@ -31,6 +31,10 @@ class MessageTest(unittest.TestCase):
self.msg2_str = "{\"aaa\": [1, 1.1, true, false, null]}";
self.msg2_wire = self.msg2_str.encode()
+ self.msg3 = { "aaa": [ 1, 1.1, True, False, "string\n" ] }
+ self.msg3_str = "{\"aaa\": [1, 1.1, true, false, \"string\n\" ]}";
+ self.msg3_wire = self.msg3_str.encode()
+
def test_encode_json(self):
self.assertEqual(self.msg1_wire, isc.cc.message.to_wire(self.msg1))
self.assertEqual(self.msg2_wire, isc.cc.message.to_wire(self.msg2))
@@ -40,6 +44,7 @@ class MessageTest(unittest.TestCase):
def test_decode_json(self):
self.assertEqual(self.msg1, isc.cc.message.from_wire(self.msg1_wire))
self.assertEqual(self.msg2, isc.cc.message.from_wire(self.msg2_wire))
+ self.assertEqual(self.msg3, isc.cc.message.from_wire(self.msg3_wire))
self.assertRaises(AttributeError, isc.cc.message.from_wire, 1)
self.assertRaises(ValueError, isc.cc.message.from_wire, b'\x001')
diff --git a/src/lib/python/isc/cc/tests/session_test.py b/src/lib/python/isc/cc/tests/session_test.py
index fe35a6c..772ed0c 100644
--- a/src/lib/python/isc/cc/tests/session_test.py
+++ b/src/lib/python/isc/cc/tests/session_test.py
@@ -274,6 +274,16 @@ class testSession(unittest.TestCase):
self.assertEqual({"hello": "b"}, msg)
self.assertFalse(sess.has_queued_msgs())
+ def test_recv_bad_msg(self):
+ sess = MySession()
+ self.assertFalse(sess.has_queued_msgs())
+ sess._socket.addrecv({'to': 'someone' }, {'hello': 'b'})
+ sess._socket.addrecv({'to': 'someone', 'reply': 1}, {'hello': 'a'})
+ # mangle the bytes a bit
+ sess._socket.recvqueue[5] = sess._socket.recvqueue[5] - 2
+ sess._socket.recvqueue = sess._socket.recvqueue[:-2]
+ self.assertRaises(SessionError, sess.recvmsg, True, 1)
+
def test_next_sequence(self):
sess = MySession()
self.assertEqual(sess._sequence, 1)
diff --git a/src/lib/python/isc/config/Makefile.am b/src/lib/python/isc/config/Makefile.am
index 916a522..1efb6fc 100644
--- a/src/lib/python/isc/config/Makefile.am
+++ b/src/lib/python/isc/config/Makefile.am
@@ -1,5 +1,19 @@
SUBDIRS = . tests
python_PYTHON = __init__.py ccsession.py cfgmgr.py config_data.py module_spec.py
+pyexec_DATA = cfgmgr_messages.py
pythondir = $(pyexecdir)/isc/config
+
+# Define rule to build logging source files from message file
+cfgmgr_messages.py: cfgmgr_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/lib/python/isc/config/cfgmgr_messages.mes
+
+CLEANFILES = cfgmgr_messages.py cfgmgr_messages.pyc
+
+CLEANDIRS = __pycache__
+
+EXTRA_DIST = cfgmgr_messages.mes
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py
index 0e602b7..bff4f58 100644
--- a/src/lib/python/isc/config/ccsession.py
+++ b/src/lib/python/isc/config/ccsession.py
@@ -1,5 +1,4 @@
# Copyright (C) 2009 Internet Systems Consortium.
-# Copyright (C) 2010 CZ NIC
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -40,6 +39,10 @@
from isc.cc import Session
from isc.config.config_data import ConfigData, MultiConfigData, BIND10_CONFIG_DATA_VERSION
import isc
+from isc.util.file import path_search
+import bind10_config
+from isc.log import log_config_update
+import json
class ModuleCCSessionError(Exception): pass
@@ -117,6 +120,18 @@ def create_command(command_name, params = None):
msg = { 'command': cmd }
return msg
+def default_logconfig_handler(new_config, config_data):
+ errors = []
+
+ if config_data.get_module_spec().validate_config(False, new_config, errors):
+ isc.log.log_config_update(json.dumps(new_config),
+ json.dumps(config_data.get_module_spec().get_full_spec()))
+ else:
+ # no logging here yet, TODO: log these errors
+ print("Error in logging configuration, ignoring config update: ")
+ for err in errors:
+ print(err)
+
class ModuleCCSession(ConfigData):
"""This class maintains a connection to the command channel, as
well as configuration options for modules. The module provides
@@ -127,14 +142,29 @@ class ModuleCCSession(ConfigData):
callbacks are called when 'check_command' is called on the
ModuleCCSession"""
- def __init__(self, spec_file_name, config_handler, command_handler, cc_session = None):
+ def __init__(self, spec_file_name, config_handler, command_handler, cc_session=None, handle_logging_config=False):
"""Initialize a ModuleCCSession. This does *NOT* send the
specification and request the configuration yet. Use start()
for that once the ModuleCCSession has been initialized.
- specfile_name is the path to the specification file
+
+ specfile_name is the path to the specification file.
+
config_handler and command_handler are callback functions,
see set_config_handler and set_command_handler for more
- information on their signatures."""
+ information on their signatures.
+
+ cc_session can be used to pass in an existing CCSession,
+ if it is None, one will be set up. This is mainly intended
+ for testing purposes.
+
+ handle_logging_config: if True, the module session will
+ automatically handle logging configuration for the module;
+ it will read the system-wide Logging configuration and call
+ the logger manager to apply it. It will also inform the
+ logger manager when the logging configuration gets updated.
+ The module does not need to do anything except intializing
+ its loggers, and provide log messages
+ """
module_spec = isc.config.module_spec_from_file(spec_file_name)
ConfigData.__init__(self, module_spec)
@@ -150,8 +180,16 @@ class ModuleCCSession(ConfigData):
self._session.group_subscribe(self._module_name, "*")
self._remote_module_configs = {}
+ self._remote_module_callbacks = {}
+
+ if handle_logging_config:
+ self.add_remote_config(path_search('logging.spec', bind10_config.PLUGIN_PATHS),
+ default_logconfig_handler)
def __del__(self):
+ # If the CC Session obejct has been closed, it returns
+ # immediately.
+ if self._session._closed: return
self._session.group_unsubscribe(self._module_name, "*")
for module_name in self._remote_module_configs:
self._session.group_unsubscribe(module_name)
@@ -216,6 +254,9 @@ class ModuleCCSession(ConfigData):
newc = self._remote_module_configs[module_name].get_local_config()
isc.cc.data.merge(newc, new_config)
self._remote_module_configs[module_name].set_local_config(newc)
+ if self._remote_module_callbacks[module_name] != None:
+ self._remote_module_callbacks[module_name](new_config,
+ self._remote_module_configs[module_name])
# For other modules, we're not supposed to answer
return
@@ -258,7 +299,7 @@ class ModuleCCSession(ConfigData):
and return an answer created with create_answer()"""
self._command_handler = command_handler
- def add_remote_config(self, spec_file_name):
+ def add_remote_config(self, spec_file_name, config_update_callback = None):
"""Gives access to the configuration of a different module.
These remote module options can at this moment only be
accessed through get_remote_config_value(). This function
@@ -287,9 +328,12 @@ class ModuleCCSession(ConfigData):
if rcode == 0:
if value != None and module_spec.validate_config(False, value):
module_cfg.set_local_config(value);
+ if config_update_callback is not None:
+ config_update_callback(value, module_cfg)
# all done, add it
self._remote_module_configs[module_name] = module_cfg
+ self._remote_module_callbacks[module_name] = config_update_callback
return module_name
def remove_remote_config(self, module_name):
@@ -297,6 +341,7 @@ class ModuleCCSession(ConfigData):
if module_name in self._remote_module_configs:
self._session.group_unsubscribe(module_name)
del self._remote_module_configs[module_name]
+ del self._remote_module_callbacks[module_name]
def get_remote_config_value(self, module_name, identifier):
"""Returns the current setting for the given identifier at the
@@ -327,10 +372,18 @@ class ModuleCCSession(ConfigData):
if answer:
rcode, value = parse_answer(answer)
if rcode == 0:
- if value != None and self.get_module_spec().validate_config(False, value):
- self.set_local_config(value);
- if self._config_handler:
- self._config_handler(value)
+ errors = []
+ if value != None:
+ if self.get_module_spec().validate_config(False,
+ value,
+ errors):
+ self.set_local_config(value);
+ if self._config_handler:
+ self._config_handler(value)
+ else:
+ raise ModuleCCSessionError(
+ "Wrong data in configuration: " +
+ " ".join(errors))
else:
# log error
print("[" + self._module_name + "] Error requesting configuration: " + value)
diff --git a/src/lib/python/isc/config/cfgmgr.py b/src/lib/python/isc/config/cfgmgr.py
index e347f6a..18e001c 100644
--- a/src/lib/python/isc/config/cfgmgr.py
+++ b/src/lib/python/isc/config/cfgmgr.py
@@ -28,7 +28,13 @@ import tempfile
import json
import errno
from isc.cc import data
-from isc.config import ccsession, config_data
+from isc.config import ccsession, config_data, module_spec
+from isc.util.file import path_search
+import bind10_config
+import isc.log
+from cfgmgr_messages import *
+
+logger = isc.log.Logger("cfgmgr")
class ConfigManagerDataReadError(Exception):
"""This exception is thrown when there is an error while reading
@@ -44,25 +50,36 @@ class ConfigManagerData:
"""This class hold the actual configuration information, and
reads it from and writes it to persistent storage"""
- def __init__(self, data_path, file_name = "b10-config.db"):
+ def __init__(self, data_path, file_name):
"""Initialize the data for the configuration manager, and
set the version and path for the data store. Initializing
this does not yet read the database, a call to
- read_from_file is needed for that."""
+ read_from_file is needed for that.
+
+ In case the file_name is absolute, data_path is ignored
+ and the directory where the file_name lives is used instead.
+ """
self.data = {}
self.data['version'] = config_data.BIND10_CONFIG_DATA_VERSION
- self.data_path = data_path
- self.db_filename = data_path + os.sep + file_name
-
- def read_from_file(data_path, file_name = "b10-config.db"):
- """Read the current configuration found in the file at
- data_path. If the file does not exist, a
- ConfigManagerDataEmpty exception is raised. If there is a
- parse error, or if the data in the file has the wrong
- version, a ConfigManagerDataReadError is raised. In the first
- case, it is probably safe to log and ignore. In the case of
- the second exception, the best way is probably to report the
- error and stop loading the system."""
+ if os.path.isabs(file_name):
+ self.db_filename = file_name
+ self.data_path = os.path.dirname(file_name)
+ else:
+ self.db_filename = data_path + os.sep + file_name
+ self.data_path = data_path
+
+ def read_from_file(data_path, file_name):
+ """Read the current configuration found in the file file_name.
+ If file_name is absolute, data_path is ignored. Otherwise
+ we look for the file_name in data_path directory.
+
+ If the file does not exist, a ConfigManagerDataEmpty exception is
+ raised. If there is a parse error, or if the data in the file has
+ the wrong version, a ConfigManagerDataReadError is raised. In the
+ first case, it is probably safe to log and ignore. In the case of
+ the second exception, the best way is probably to report the error
+ and stop loading the system.
+ """
config = ConfigManagerData(data_path, file_name)
file = None
try:
@@ -78,7 +95,7 @@ class ConfigManagerData:
elif file_config['version'] == 1:
# only format change, no other changes necessary
file_config['version'] = 2
- print("[b10-cfgmgr] Updating configuration database version from 1 to 2")
+ logger.info(CFGMGR_AUTOMATIC_CONFIG_DATABASE_UPDATE, 1, 2)
config.data = file_config
else:
if config_data.BIND10_CONFIG_DATA_VERSION > file_config['version']:
@@ -120,12 +137,9 @@ class ConfigManagerData:
else:
os.rename(filename, self.db_filename)
except IOError as ioe:
- # TODO: log this (level critical)
- print("[b10-cfgmgr] Unable to write configuration file; configuration not stored: " + str(ioe))
- # TODO: debug option to keep file?
+ logger.error(CFGMGR_IOERROR_WHILE_WRITING_CONFIGURATION, ioe)
except OSError as ose:
- # TODO: log this (level critical)
- print("[b10-cfgmgr] Unable to write configuration file; configuration not stored: " + str(ose))
+ logger.error(CFGMGR_OSERROR_WHILE_WRITING_CONFIGURATION, ose)
try:
if filename and os.path.exists(filename):
os.remove(filename)
@@ -142,20 +156,28 @@ class ConfigManagerData:
class ConfigManager:
"""Creates a configuration manager. The data_path is the path
- to the directory containing the b10-config.db file.
+ to the directory containing the configuraton file,
+ database_filename points to the configuration file.
If session is set, this will be used as the communication
channel session. If not, a new session will be created.
The ability to specify a custom session is for testing purposes
and should not be needed for normal usage."""
- def __init__(self, data_path, session = None):
+ def __init__(self, data_path, database_filename, session=None):
"""Initialize the configuration manager. The data_path string
is the path to the directory where the configuration is
- stored (in <data_path>/b10-config.db). Session is an optional
+ stored (in <data_path>/<database_filename> or in
+ <database_filename>, if it is absolute). The dabase_filename
+ is the config file to load. Session is an optional
cc-channel session. If this is not given, a new one is
- created"""
+ created."""
self.data_path = data_path
+ self.database_filename = database_filename
self.module_specs = {}
- self.config = ConfigManagerData(data_path)
+ # Virtual modules are the ones which have no process running. The
+ # checking of validity is done by functions presented here instead
+ # of some other process
+ self.virtual_modules = {}
+ self.config = ConfigManagerData(data_path, database_filename)
if session:
self.cc = session
else:
@@ -163,6 +185,20 @@ class ConfigManager:
self.cc.group_subscribe("ConfigManager")
self.cc.group_subscribe("Boss", "ConfigManager")
self.running = False
+ # As a core module, CfgMgr is different than other modules,
+ # as it does not use a ModuleCCSession, and hence needs
+ # to handle logging config on its own
+ self.log_config_data = config_data.ConfigData(
+ isc.config.module_spec_from_file(
+ path_search('logging.spec',
+ bind10_config.PLUGIN_PATHS)))
+ # store the logging 'module' name for easier reference
+ self.log_module_name = self.log_config_data.get_module_spec().get_module_name()
+
+ def check_logging_config(self, config):
+ if self.log_module_name in config:
+ ccsession.default_logconfig_handler(config[self.log_module_name],
+ self.log_config_data)
def notify_boss(self):
"""Notifies the Boss module that the Config Manager is running"""
@@ -172,11 +208,20 @@ class ConfigManager:
"""Adds a ModuleSpec"""
self.module_specs[spec.get_module_name()] = spec
+ def set_virtual_module(self, spec, check_func):
+ """Adds a virtual module with its spec and checking function."""
+ self.module_specs[spec.get_module_name()] = spec
+ self.virtual_modules[spec.get_module_name()] = check_func
+
def remove_module_spec(self, module_name):
"""Removes the full ModuleSpec for the given module_name.
+ Also removes the virtual module check function if it
+ was present.
Does nothing if the module was not present."""
if module_name in self.module_specs:
del self.module_specs[module_name]
+ if module_name in self.virtual_modules:
+ del self.virtual_modules[module_name]
def get_module_spec(self, module_name = None):
"""Returns the full ModuleSpec for the module with the given
@@ -186,7 +231,7 @@ class ConfigManager:
is returned"""
if module_name:
if module_name in self.module_specs:
- return self.module_specs[module_name]
+ return self.module_specs[module_name].get_full_spec()
else:
# TODO: log error?
return {}
@@ -223,17 +268,19 @@ class ConfigManager:
return commands
def read_config(self):
- """Read the current configuration from the b10-config.db file
- at the path specificied at init()"""
+ """Read the current configuration from the file specificied at init()"""
try:
- self.config = ConfigManagerData.read_from_file(self.data_path)
+ self.config = ConfigManagerData.read_from_file(self.data_path,
+ self.\
+ database_filename)
+ self.check_logging_config(self.config.data);
except ConfigManagerDataEmpty:
# ok, just start with an empty config
- self.config = ConfigManagerData(self.data_path)
+ self.config = ConfigManagerData(self.data_path,
+ self.database_filename)
def write_config(self):
- """Write the current configuration to the b10-config.db file
- at the path specificied at init()"""
+ """Write the current configuration to the file specificied at init()"""
self.config.write_to_file()
def _handle_get_module_spec(self, cmd):
@@ -243,7 +290,12 @@ class ConfigManager:
if type(cmd) == dict:
if 'module_name' in cmd and cmd['module_name'] != '':
module_name = cmd['module_name']
- answer = ccsession.create_answer(0, self.get_module_spec(module_name))
+ spec = self.get_module_spec(cmd['module_name'])
+ if type(spec) != type({}):
+ # this is a ModuleSpec object. Extract the
+ # internal spec.
+ spec = spec.get_full_spec()
+ answer = ccsession.create_answer(0, spec)
else:
answer = ccsession.create_answer(1, "Bad module_name in get_module_spec command")
else:
@@ -283,27 +335,54 @@ class ConfigManager:
# todo: use api (and check the data against the definition?)
old_data = copy.deepcopy(self.config.data)
conf_part = data.find_no_exc(self.config.data, module_name)
+ update_cmd = None
+ use_part = None
if conf_part:
data.merge(conf_part, cmd)
- update_cmd = ccsession.create_command(ccsession.COMMAND_CONFIG_UPDATE,
- conf_part)
- seq = self.cc.group_sendmsg(update_cmd, module_name)
- try:
- answer, env = self.cc.group_recvmsg(False, seq)
- except isc.cc.SessionTimeout:
- answer = ccsession.create_answer(1, "Timeout waiting for answer from " + module_name)
+ use_part = conf_part
else:
conf_part = data.set(self.config.data, module_name, {})
data.merge(conf_part[module_name], cmd)
- # send out changed info
- update_cmd = ccsession.create_command(ccsession.COMMAND_CONFIG_UPDATE,
- conf_part[module_name])
+ use_part = conf_part[module_name]
+
+ # The command to send
+ update_cmd = ccsession.create_command(ccsession.COMMAND_CONFIG_UPDATE,
+ use_part)
+
+ # TODO: This design might need some revisiting. We might want some
+ # polymorphism instead of branching. But it just might turn out it
+ # will get solved by itself when we move everything to virtual modules
+ # (which is possible solution to the offline configuration problem)
+ # or when we solve the incorect behaviour here when a config is
+ # rejected (spying modules don't know it was rejected and some modules
+ # might have been commited already).
+ if module_name in self.virtual_modules:
+ # The module is virtual, so call it to get the answer
+ try:
+ error = self.virtual_modules[module_name](use_part)
+ if error is None:
+ answer = ccsession.create_answer(0)
+ # OK, it is successful, send the notify, but don't wait
+ # for answer
+ seq = self.cc.group_sendmsg(update_cmd, module_name)
+ else:
+ answer = ccsession.create_answer(1, error)
+ # Make sure just a validating plugin don't kill the whole manager
+ except Exception as excp:
+ # Provide answer
+ answer = ccsession.create_answer(1, "Exception: " + str(excp))
+ else:
+ # Real module, send it over the wire to it
+ # send out changed info and wait for answer
seq = self.cc.group_sendmsg(update_cmd, module_name)
- # replace 'our' answer with that of the module
try:
+ # replace 'our' answer with that of the module
answer, env = self.cc.group_recvmsg(False, seq)
except isc.cc.SessionTimeout:
answer = ccsession.create_answer(1, "Timeout waiting for answer from " + module_name)
+ except isc.cc.SessionError as se:
+ logger.error(CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE, module_name, se)
+ answer = ccsession.create_answer(1, "Unable to parse response from " + module_name + ": " + str(se))
if answer:
rcode, val = ccsession.parse_answer(answer)
if rcode == 0:
@@ -330,6 +409,8 @@ class ConfigManager:
got_error = True
err_list.append(val)
if not got_error:
+ # if Logging config is in there, update our config as well
+ self.check_logging_config(cmd);
self.write_config()
return ccsession.create_answer(0)
else:
@@ -383,8 +464,6 @@ class ConfigManager:
elif cmd == ccsession.COMMAND_SET_CONFIG:
answer = self._handle_set_config(arg)
elif cmd == ccsession.COMMAND_SHUTDOWN:
- # TODO: logging
- #print("[b10-cfgmgr] Received shutdown command")
self.running = False
answer = ccsession.create_answer(0)
elif cmd == ccsession.COMMAND_MODULE_SPEC:
diff --git a/src/lib/python/isc/config/cfgmgr_messages.mes b/src/lib/python/isc/config/cfgmgr_messages.mes
new file mode 100644
index 0000000..61a63ed
--- /dev/null
+++ b/src/lib/python/isc/config/cfgmgr_messages.mes
@@ -0,0 +1,57 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# No namespace declaration - these constants go in the global namespace
+# of the xfrin messages python module.
+
+% CFGMGR_AUTOMATIC_CONFIG_DATABASE_UPDATE Updating configuration database from version %1 to %2
+An older version of the configuration database has been found, from which
+there was an automatic upgrade path to the current version. These changes
+are now applied, and no action from the administrator is necessary.
+
+% CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE Unable to parse response from module %1: %2
+The configuration manager sent a configuration update to a module, but
+the module responded with an answer that could not be parsed. The answer
+message appears to be invalid JSON data, or not decodable to a string.
+This is likely to be a problem in the module in question. The update is
+assumed to have failed, and will not be stored.
+
+% CFGMGR_CC_SESSION_ERROR Error connecting to command channel: %1
+The configuration manager daemon was unable to connect to the messaging
+system. The most likely cause is that msgq is not running.
+
+% CFGMGR_DATA_READ_ERROR error reading configuration database from disk: %1
+There was a problem reading the persistent configuration data as stored
+on disk. The file may be corrupted, or it is of a version from where
+there is no automatic upgrade path. The file needs to be repaired or
+removed. The configuration manager daemon will now shut down.
+
+% CFGMGR_IOERROR_WHILE_WRITING_CONFIGURATION Unable to write configuration file; configuration not stored: %1
+There was an IO error from the system while the configuration manager
+was trying to write the configuration database to disk. The specific
+error is given. The most likely cause is that the directory where
+the file is stored does not exist, or is not writable. The updated
+configuration is not stored.
+
+% CFGMGR_OSERROR_WHILE_WRITING_CONFIGURATION Unable to write configuration file; configuration not stored: %1
+There was an OS error from the system while the configuration manager
+was trying to write the configuration database to disk. The specific
+error is given. The most likely cause is that the system does not have
+write access to the configuration database file. The updated
+configuration is not stored.
+
+% CFGMGR_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
+There was a keyboard interrupt signal to stop the cfgmgr daemon. The
+daemon will now shut down.
+
diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py
index 582c11c..1efe4a9 100644
--- a/src/lib/python/isc/config/config_data.py
+++ b/src/lib/python/isc/config/config_data.py
@@ -108,6 +108,52 @@ def convert_type(spec_part, value):
except TypeError as err:
raise isc.cc.data.DataTypeError(str(err))
+def _get_map_or_list(spec_part):
+ """Returns the list or map specification if this is a list or a
+ map specification part. If not, returns the given spec_part
+ itself"""
+ if "map_item_spec" in spec_part:
+ return spec_part["map_item_spec"]
+ elif "list_item_spec" in spec_part:
+ return spec_part["list_item_spec"]
+ else:
+ return spec_part
+
+def _find_spec_part_single(cur_spec, id_part):
+ """Find the spec part for the given (partial) name. This partial
+ name does not contain separators ('/'), and the specification
+ part should be a direct child of the given specification part.
+ id_part may contain list selectors, which will be ignored.
+ Returns the child part.
+ Raises DataNotFoundError if it was not found."""
+ # strip list selector part
+ # don't need it for the spec part, so just drop it
+ id, list_indices = isc.cc.data.split_identifier_list_indices(id_part)
+
+ # The specification we want a sub-part for should be either a
+ # list or a map, which is internally represented by a dict with
+ # an element 'map_item_spec', a dict with an element 'list_item_spec',
+ # or a list (when it is the 'main' config_data element of a module).
+ if type(cur_spec) == dict and 'map_item_spec' in cur_spec.keys():
+ for cur_spec_item in cur_spec['map_item_spec']:
+ if cur_spec_item['item_name'] == id:
+ return cur_spec_item
+ # not found
+ raise isc.cc.data.DataNotFoundError(id + " not found")
+ elif type(cur_spec) == dict and 'list_item_spec' in cur_spec.keys():
+ if cur_spec['item_name'] == id:
+ return cur_spec['list_item_spec']
+ # not found
+ raise isc.cc.data.DataNotFoundError(id + " not found")
+ elif type(cur_spec) == list:
+ for cur_spec_item in cur_spec:
+ if cur_spec_item['item_name'] == id:
+ return cur_spec_item
+ # not found
+ raise isc.cc.data.DataNotFoundError(id + " not found")
+ else:
+ raise isc.cc.data.DataNotFoundError("Not a correct config specification")
+
def find_spec_part(element, identifier):
"""find the data definition for the given identifier
returns either a map with 'item_name' etc, or a list of those"""
@@ -117,38 +163,15 @@ def find_spec_part(element, identifier):
id_parts[:] = (value for value in id_parts if value != "")
cur_el = element
- for id_part in id_parts:
- # strip list selector part
- # don't need it for the spec part, so just drop it
- id, list_indices = isc.cc.data.split_identifier_list_indices(id_part)
- # is this part still needed? (see below)
- if type(cur_el) == dict and 'map_item_spec' in cur_el.keys():
- found = False
- for cur_el_item in cur_el['map_item_spec']:
- if cur_el_item['item_name'] == id:
- cur_el = cur_el_item
- found = True
- if not found:
- raise isc.cc.data.DataNotFoundError(id + " not found")
- elif type(cur_el) == dict and 'list_item_spec' in cur_el.keys():
- cur_el = cur_el['list_item_spec']
- elif type(cur_el) == list:
- found = False
- for cur_el_item in cur_el:
- if cur_el_item['item_name'] == id:
- cur_el = cur_el_item
- # if we need to go further, we may need to 'skip' a step here
- # but not if we're done
- if id_parts[-1] != id_part and type(cur_el) == dict:
- if "map_item_spec" in cur_el:
- cur_el = cur_el["map_item_spec"]
- elif "list_item_spec" in cur_el:
- cur_el = cur_el["list_item_spec"]
- found = True
- if not found:
- raise isc.cc.data.DataNotFoundError(id + " not found")
- else:
- raise isc.cc.data.DataNotFoundError("Not a correct config specification")
+ # up to the last element, if the result is a map or a list,
+ # we want its subspecification (i.e. list_item_spec or
+ # map_item_spec). For the last element in the identifier we
+ # always want the 'full' spec of the item
+ for id_part in id_parts[:-1]:
+ cur_el = _find_spec_part_single(cur_el, id_part)
+ cur_el = _get_map_or_list(cur_el)
+
+ cur_el = _find_spec_part_single(cur_el, id_parts[-1])
return cur_el
def spec_name_list(spec, prefix="", recurse=False):
@@ -213,6 +236,15 @@ class ConfigData:
return spec['item_default'], True
return None, False
+ def get_default_value(self, identifier):
+ """Returns the default from the specification, or None if there
+ is no default"""
+ spec = find_spec_part(self.specification.get_config_spec(), identifier)
+ if spec and 'item_default' in spec:
+ return spec['item_default']
+ else:
+ return None
+
def get_module_spec(self):
"""Returns the ModuleSpec object associated with this ConfigData"""
return self.specification
@@ -461,8 +493,8 @@ class MultiConfigData:
spec_part_list = spec_part['list_item_spec']
list_value, status = self.get_value(identifier)
if list_value is None:
- print("Error: identifier '%s' not found" % identifier)
- return
+ raise isc.cc.data.DataNotFoundError(identifier)
+
if type(list_value) != list:
# the identifier specified a single element
self._append_value_item(result, spec_part_list, identifier, all)
diff --git a/src/lib/python/isc/config/module_spec.py b/src/lib/python/isc/config/module_spec.py
index 6c90677..6171149 100644
--- a/src/lib/python/isc/config/module_spec.py
+++ b/src/lib/python/isc/config/module_spec.py
@@ -87,7 +87,7 @@ class ModuleSpec:
validate only a part of a configuration tree (like a list of
non-default values)"""
data_def = self.get_config_spec()
- if data_def:
+ if data_def is not None:
return _validate_spec_list(data_def, full, data, errors)
else:
# no spec, always bad
@@ -345,7 +345,7 @@ def _validate_spec_list(module_spec, full, data, errors):
for spec_item in module_spec:
if spec_item["item_name"] == item_name:
found = True
- if not found:
+ if not found and item_name != "version":
if errors != None:
errors.append("unknown item " + item_name)
validated = False
diff --git a/src/lib/python/isc/config/tests/Makefile.am b/src/lib/python/isc/config/tests/Makefile.am
index 622b23c..47ccc41 100644
--- a/src/lib/python/isc/config/tests/Makefile.am
+++ b/src/lib/python/isc/config/tests/Makefile.am
@@ -4,6 +4,13 @@ PYTESTS += module_spec_test.py
EXTRA_DIST = $(PYTESTS)
EXTRA_DIST += unittest_fakesession.py
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
if ENABLE_PYTHON_COVERAGE
@@ -13,8 +20,15 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
- env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
+ $(LIBRARY_PATH_PLACEHOLDER) \
+ env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/python/isc/config \
+ B10_TEST_PLUGIN_DIR=$(abs_top_srcdir)/src/bin/cfgmgr/plugins \
CONFIG_TESTDATA_PATH=$(abs_top_srcdir)/src/lib/config/tests/testdata \
CONFIG_WR_TESTDATA_PATH=$(abs_top_builddir)/src/lib/config/tests/testdata \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py
index 2ae37f5..830cbd7 100644
--- a/src/lib/python/isc/config/tests/ccsession_test.py
+++ b/src/lib/python/isc/config/tests/ccsession_test.py
@@ -22,6 +22,7 @@ import os
from isc.config.ccsession import *
from isc.config.config_data import BIND10_CONFIG_DATA_VERSION
from unittest_fakesession import FakeModuleCCSession, WouldBlockForever
+import bind10_config
class TestHelperFunctions(unittest.TestCase):
def test_parse_answer(self):
@@ -220,6 +221,31 @@ class TestModuleCCSession(unittest.TestCase):
self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
fake_session.get_message('ConfigManager', None))
+ def test_start5(self):
+ fake_session = FakeModuleCCSession()
+ mccs = self.create_session("spec2.spec", None, None, fake_session)
+ mccs.set_config_handler(self.my_config_handler_ok)
+ self.assertEqual(len(fake_session.message_queue), 0)
+ fake_session.group_sendmsg(None, 'Spec2')
+ fake_session.group_sendmsg(None, 'Spec2')
+ self.assertRaises(ModuleCCSessionError, mccs.start)
+ self.assertEqual(len(fake_session.message_queue), 2)
+ self.assertEqual({'command': ['module_spec', mccs.specification._module_spec]},
+ fake_session.get_message('ConfigManager', None))
+ self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
+ fake_session.get_message('ConfigManager', None))
+
+ self.assertEqual(len(fake_session.message_queue), 0)
+ fake_session.group_sendmsg({'result': [ 0 ]}, "Spec2")
+ fake_session.group_sendmsg({'result': [ 0, {"Wrong": True} ]}, "Spec2")
+ self.assertRaises(ModuleCCSessionError, mccs.start)
+ self.assertEqual(len(fake_session.message_queue), 2)
+
+ self.assertEqual({'command': ['module_spec', mccs.specification._module_spec]},
+ fake_session.get_message('ConfigManager', None))
+ self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
+ fake_session.get_message('ConfigManager', None))
+
def test_get_socket(self):
fake_session = FakeModuleCCSession()
mccs = self.create_session("spec1.spec", None, None, fake_session)
@@ -234,7 +260,18 @@ class TestModuleCCSession(unittest.TestCase):
fake_session = FakeModuleCCSession()
mccs = self.create_session("spec1.spec", None, None, fake_session)
mccs.close()
- self.assertEqual("closed", fake_session._socket)
+ self.assertEqual(None, fake_session._socket)
+
+ def test_del_opened(self):
+ fake_session = FakeModuleCCSession()
+ mccs = self.create_session("spec1.spec", None, None, fake_session)
+ mccs.__del__() # with opened fake_session
+
+ def test_del_closed(self):
+ fake_session = FakeModuleCCSession()
+ mccs = self.create_session("spec1.spec", None, None, fake_session)
+ fake_session.close()
+ mccs.__del__() # with closed fake_session
def my_config_handler_ok(self, new_config):
return isc.config.ccsession.create_answer(0)
@@ -568,7 +605,43 @@ class TestModuleCCSession(unittest.TestCase):
self.assertEqual(len(fake_session.message_queue), 1)
mccs.check_command()
self.assertEqual(len(fake_session.message_queue), 0)
-
+
+ def test_logconfig_handler(self):
+ # test whether default_logconfig_handler reacts nicely to
+ # bad data. We assume the actual logger output is tested
+ # elsewhere
+ self.assertRaises(TypeError, default_logconfig_handler);
+ self.assertRaises(TypeError, default_logconfig_handler, 1);
+
+ spec = isc.config.module_spec_from_file(
+ path_search('logging.spec', bind10_config.PLUGIN_PATHS))
+ config_data = ConfigData(spec)
+
+ self.assertRaises(TypeError, default_logconfig_handler, 1, config_data)
+
+ default_logconfig_handler({}, config_data)
+
+ # Wrong data should not raise, but simply not be accepted
+ # This would log a lot of errors, so we may want to suppress that later
+ default_logconfig_handler({ "bad_data": "indeed" }, config_data)
+ default_logconfig_handler({ "bad_data": 1}, config_data)
+ default_logconfig_handler({ "bad_data": 1123 }, config_data)
+ default_logconfig_handler({ "bad_data": True }, config_data)
+ default_logconfig_handler({ "bad_data": False }, config_data)
+ default_logconfig_handler({ "bad_data": 1.1 }, config_data)
+ default_logconfig_handler({ "bad_data": [] }, config_data)
+ default_logconfig_handler({ "bad_data": [[],[],[[1, 3, False, "foo" ]]] },
+ config_data)
+ default_logconfig_handler({ "bad_data": [ 1, 2, { "b": { "c": "d" } } ] },
+ config_data)
+
+ # Try a correct config
+ log_conf = {"loggers":
+ [{"name": "b10-xfrout", "output_options":
+ [{"output": "/tmp/bind10.log",
+ "destination": "file",
+ "flush": True}]}]}
+ default_logconfig_handler(log_conf, config_data)
class fakeData:
def decode(self):
diff --git a/src/lib/python/isc/config/tests/cfgmgr_test.py b/src/lib/python/isc/config/tests/cfgmgr_test.py
index c992d0d..0a9e2d3 100644
--- a/src/lib/python/isc/config/tests/cfgmgr_test.py
+++ b/src/lib/python/isc/config/tests/cfgmgr_test.py
@@ -27,9 +27,20 @@ class TestConfigManagerData(unittest.TestCase):
def setUp(self):
self.data_path = os.environ['CONFIG_TESTDATA_PATH']
self.writable_data_path = os.environ['CONFIG_WR_TESTDATA_PATH']
- self.config_manager_data = ConfigManagerData(self.writable_data_path)
+ self.config_manager_data = ConfigManagerData(self.writable_data_path,
+ file_name="b10-config.db")
self.assert_(self.config_manager_data)
+ def test_abs_file(self):
+ """
+ Test what happens if we give the config manager an absolute path.
+ It shouldn't append the data path to it.
+ """
+ abs_path = self.data_path + os.sep + "b10-config-imaginary.db"
+ data = ConfigManagerData(os.getcwd(), abs_path)
+ self.assertEqual(abs_path, data.db_filename)
+ self.assertEqual(self.data_path, data.data_path)
+
def test_init(self):
self.assertEqual(self.config_manager_data.data['version'],
config_data.BIND10_CONFIG_DATA_VERSION)
@@ -39,10 +50,10 @@ class TestConfigManagerData(unittest.TestCase):
self.writable_data_path + os.sep + "b10-config.db")
def test_read_from_file(self):
- ConfigManagerData.read_from_file(self.writable_data_path)
+ ConfigManagerData.read_from_file(self.writable_data_path, "b10-config.db")
self.assertRaises(ConfigManagerDataEmpty,
ConfigManagerData.read_from_file,
- "doesnotexist")
+ "doesnotexist", "b10-config.db")
self.assertRaises(ConfigManagerDataReadError,
ConfigManagerData.read_from_file,
self.data_path, "b10-config-bad1.db")
@@ -68,8 +79,8 @@ class TestConfigManagerData(unittest.TestCase):
# by equality of the .data element. If data_path or db_filename
# are different, but the contents are the same, it's still
# considered equal
- cfd1 = ConfigManagerData(self.data_path)
- cfd2 = ConfigManagerData(self.data_path)
+ cfd1 = ConfigManagerData(self.data_path, file_name="b10-config.db")
+ cfd2 = ConfigManagerData(self.data_path, file_name="b10-config.db")
self.assertEqual(cfd1, cfd2)
cfd2.data_path = "some/unknown/path"
self.assertEqual(cfd1, cfd2)
@@ -85,10 +96,25 @@ class TestConfigManager(unittest.TestCase):
self.data_path = os.environ['CONFIG_TESTDATA_PATH']
self.writable_data_path = os.environ['CONFIG_WR_TESTDATA_PATH']
self.fake_session = FakeModuleCCSession()
- self.cm = ConfigManager(self.writable_data_path, self.fake_session)
+ self.cm = ConfigManager(self.writable_data_path,
+ database_filename="b10-config.db",
+ session=self.fake_session)
self.name = "TestModule"
self.spec = isc.config.module_spec_from_file(self.data_path + os.sep + "/spec2.spec")
-
+
+ def test_paths(self):
+ """
+ Test data_path and database filename is passed trough to
+ underlying ConfigManagerData.
+ """
+ cm = ConfigManager("datapath", "filename", self.fake_session)
+ self.assertEqual("datapath" + os.sep + "filename",
+ cm.config.db_filename)
+ # It should preserve it while reading
+ cm.read_config()
+ self.assertEqual("datapath" + os.sep + "filename",
+ cm.config.db_filename)
+
def test_init(self):
self.assert_(self.cm.module_specs == {})
self.assert_(self.cm.data_path == self.writable_data_path)
@@ -109,6 +135,8 @@ class TestConfigManager(unittest.TestCase):
self.assert_(module_spec.get_module_name() not in self.cm.module_specs)
self.cm.set_module_spec(module_spec)
self.assert_(module_spec.get_module_name() in self.cm.module_specs)
+ self.assert_(module_spec.get_module_name() not in
+ self.cm.virtual_modules)
def test_remove_module_spec(self):
module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
@@ -117,6 +145,30 @@ class TestConfigManager(unittest.TestCase):
self.assert_(module_spec.get_module_name() in self.cm.module_specs)
self.cm.remove_module_spec(module_spec.get_module_name())
self.assert_(module_spec.get_module_name() not in self.cm.module_specs)
+ self.assert_(module_spec.get_module_name() not in
+ self.cm.virtual_modules)
+
+ def test_add_remove_virtual_module(self):
+ module_spec = isc.config.module_spec.module_spec_from_file(
+ self.data_path + os.sep + "spec1.spec")
+ check_func = lambda: True
+ # Make sure it's not in there before
+ self.assert_(module_spec.get_module_name() not in self.cm.module_specs)
+ self.assert_(module_spec.get_module_name() not in
+ self.cm.virtual_modules)
+ # Add it there
+ self.cm.set_virtual_module(module_spec, check_func)
+ # Check it's in there
+ self.assert_(module_spec.get_module_name() in self.cm.module_specs)
+ self.assertEqual(self.cm.module_specs[module_spec.get_module_name()],
+ module_spec)
+ self.assertEqual(self.cm.virtual_modules[module_spec.get_module_name()],
+ check_func)
+ # Remove it again
+ self.cm.remove_module_spec(module_spec.get_module_name())
+ self.assert_(module_spec.get_module_name() not in self.cm.module_specs)
+ self.assert_(module_spec.get_module_name() not in
+ self.cm.virtual_modules)
def test_get_module_spec(self):
module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
@@ -124,7 +176,9 @@ class TestConfigManager(unittest.TestCase):
self.cm.set_module_spec(module_spec)
self.assert_(module_spec.get_module_name() in self.cm.module_specs)
module_spec2 = self.cm.get_module_spec(module_spec.get_module_name())
- self.assertEqual(module_spec, module_spec2)
+ self.assertEqual(module_spec.get_full_spec(), module_spec2)
+
+ self.assertEqual({}, self.cm.get_module_spec("nosuchmodule"))
def test_get_config_spec(self):
config_spec = self.cm.get_config_spec()
@@ -271,6 +325,9 @@ class TestConfigManager(unittest.TestCase):
self._handle_msg_helper({ "command": [ "module_spec", { 'foo': 1 } ] },
{'result': [1, 'Error in data definition: no module_name in module_spec']})
self._handle_msg_helper({ "command": [ "get_module_spec" ] }, { 'result': [ 0, { self.spec.get_module_name(): self.spec.get_full_spec() } ]})
+ self._handle_msg_helper({ "command": [ "get_module_spec",
+ { "module_name" : "Spec2" } ] },
+ { 'result': [ 0, self.spec.get_full_spec() ] })
self._handle_msg_helper({ "command": [ "get_commands_spec" ] }, { 'result': [ 0, { self.spec.get_module_name(): self.spec.get_commands_spec() } ]})
# re-add this once we have new way to propagate spec changes (1 instead of the current 2 messages)
#self.assertEqual(len(self.fake_session.message_queue), 2)
@@ -286,6 +343,51 @@ class TestConfigManager(unittest.TestCase):
},
{'result': [0]})
+ def test_set_config_virtual(self):
+ """Test that if the module is virtual, we don't send it over the
+ message bus, but call the checking function.
+ """
+ # We run the same three times, with different return values
+ def single_test(value, returnFunc, expectedResult):
+ # Because closures can't assign to closed-in variables, we pass
+ # it trough self
+ self.called_with = None
+ def check_test(new_data):
+ self.called_with = new_data
+ return returnFunc()
+
+ # Register our virtual module
+ self.cm.set_virtual_module(self.spec, check_test)
+ # The fake session will throw now if it tries to read a response.
+ # Handy, we don't need to find a complicated way to check for it.
+ result = self.cm._handle_set_config_module(self.spec.
+ get_module_name(),
+ {'item1': value})
+ # Check the correct result is passed and our function was called
+ # With correct data
+ self.assertEqual(self.called_with['item1'], value)
+ self.assertEqual(result, {'result': expectedResult})
+ if expectedResult[0] == 0:
+ # Check it provided the correct notification
+ self.assertEqual(len(self.fake_session.message_queue), 1)
+ self.assertEqual({'command': [ 'config_update',
+ {'item1': value}]},
+ self.fake_session.get_message('Spec2', None))
+ # and the queue should now be empty again
+ self.assertEqual(len(self.fake_session.message_queue), 0)
+ else:
+ # It shouldn't send anything on error
+ self.assertEqual(len(self.fake_session.message_queue), 0)
+
+ # Success
+ single_test(5, lambda: None, [0])
+ # Graceful error
+ single_test(6, lambda: "Just error", [1, "Just error"])
+ # Exception from the checker
+ def raiser():
+ raise Exception("Just exception")
+ single_test(7, raiser, [1, "Exception: Just exception"])
+
def test_set_config_all(self):
my_ok_answer = { 'result': [ 0 ] }
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 1aded94..fc1bffa 100644
--- a/src/lib/python/isc/config/tests/config_data_test.py
+++ b/src/lib/python/isc/config/tests/config_data_test.py
@@ -237,6 +237,27 @@ class TestConfigData(unittest.TestCase):
self.assertEqual(None, value)
self.assertEqual(False, default)
+ def test_get_default_value(self):
+ self.assertEqual(1, self.cd.get_default_value("item1"))
+ self.assertEqual('default', self.cd.get_default_value("item6/value1"))
+ self.assertEqual(None, self.cd.get_default_value("item6/value2"))
+
+ # set some local values to something else, and see if we
+ # still get the default
+ self.cd.set_local_config({"item1": 2, "item6": { "value1": "asdf" } })
+
+ self.assertEqual((2, False), self.cd.get_value("item1"))
+ self.assertEqual(1, self.cd.get_default_value("item1"))
+ self.assertEqual(('asdf', False), self.cd.get_value("item6/value1"))
+ self.assertEqual('default', self.cd.get_default_value("item6/value1"))
+
+ self.assertRaises(isc.cc.data.DataNotFoundError,
+ self.cd.get_default_value,
+ "does_not_exist/value1")
+ self.assertRaises(isc.cc.data.DataNotFoundError,
+ self.cd.get_default_value,
+ "item6/doesnotexist")
+
def test_set_local_config(self):
self.cd.set_local_config({"item1": 2})
value, default = self.cd.get_value("item1")
@@ -308,6 +329,35 @@ class TestMultiConfigData(unittest.TestCase):
spec_part = self.mcd.find_spec_part("Spec2/item1")
self.assertEqual({'item_name': 'item1', 'item_type': 'integer', 'item_optional': False, 'item_default': 1, }, spec_part)
+ def test_find_spec_part_nested(self):
+ module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec30.spec")
+ self.mcd.set_specification(module_spec)
+ spec_part = self.mcd.find_spec_part("/lists/first_list_items[0]/second_list_items[1]/final_element")
+ self.assertEqual({'item_name': 'final_element', 'item_type': 'string', 'item_default': 'hello', 'item_optional': False}, spec_part)
+ spec_part = self.mcd.find_spec_part("/BAD_NAME/first_list_items[0]/second_list_items[1]/final_element")
+ self.assertEqual(None, spec_part)
+
+ def test_find_spec_part_nested2(self):
+ module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec31.spec")
+ self.mcd.set_specification(module_spec)
+ spec_part = self.mcd.find_spec_part("/lists/first_list_items[0]/second_list_items[1]/map_element/list1[1]/list2[2]")
+ self.assertEqual({"item_name": "number", "item_type": "integer", "item_optional": False, "item_default": 1}, spec_part)
+
+ spec_part = self.mcd.find_spec_part("/DOESNOTEXIST")
+ self.assertEqual(None, spec_part)
+ spec_part = self.mcd.find_spec_part("/lists/DOESNOTEXIST")
+ self.assertEqual(None, spec_part)
+ spec_part = self.mcd.find_spec_part("/lists/first_list_items[0]/DOESNOTEXIST")
+ self.assertEqual(None, spec_part)
+ spec_part = self.mcd.find_spec_part("/lists/first_list_items[0]/second_list_items[1]/DOESNOTEXIST")
+ self.assertEqual(None, spec_part)
+ spec_part = self.mcd.find_spec_part("/lists/first_list_items[0]/second_list_items[1]/map_element/DOESNOTEXIST")
+ self.assertEqual(None, spec_part)
+ spec_part = self.mcd.find_spec_part("/lists/first_list_items[0]/second_list_items[1]/map_element/list1[1]/DOESNOTEXIST")
+ self.assertEqual(None, spec_part)
+ spec_part = self.mcd.find_spec_part("/lists/first_list_items[0]/second_list_items[1]/map_element/list1[1]/list2[1]/DOESNOTEXIST")
+ self.assertEqual(None, spec_part)
+
def test_get_current_config(self):
cf = { 'module1': { 'item1': 2, 'item2': True } }
self.mcd._set_current_config(cf);
@@ -465,8 +515,8 @@ class TestMultiConfigData(unittest.TestCase):
module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec24.spec")
self.mcd.set_specification(module_spec)
- maps = self.mcd.get_value_maps("/Spec24/item")
- self.assertEqual([], maps)
+ self.assertRaises(isc.cc.data.DataNotFoundError,
+ self.mcd.get_value_maps, "/Spec24/item", 4)
self.mcd._set_current_config({ "Spec24": { "item": [] } })
maps = self.mcd.get_value_maps("/Spec24/item")
self.assertEqual([{'default': False, 'modified': False, 'name': 'Spec24/item', 'type': 'list', 'value': []}], maps)
diff --git a/src/lib/python/isc/config/tests/unittest_fakesession.py b/src/lib/python/isc/config/tests/unittest_fakesession.py
index e31b436..1641ec0 100644
--- a/src/lib/python/isc/config/tests/unittest_fakesession.py
+++ b/src/lib/python/isc/config/tests/unittest_fakesession.py
@@ -35,6 +35,7 @@ class FakeModuleCCSession:
# the message_queue is empty when receive is called, throw
# a SessionTimeout
self._timeout = 0
+ self._closed = False
def group_subscribe(self, group_name, instance_name = None):
if not group_name in self.subscriptions:
@@ -43,6 +44,11 @@ class FakeModuleCCSession:
self.subscriptions[group_name].append(instance_name)
def group_unsubscribe(self, group_name, instance_name = None):
+
+ # raises SessionError if the session has been already closed.
+ if self._closed:
+ raise isc.cc.SessionError("Session has been closed.")
+
if group_name in self.subscriptions:
if instance_name:
if len(self.subscriptions[group_name]) > 1:
@@ -94,7 +100,8 @@ class FakeModuleCCSession:
def close(self):
# need to pass along somehow that this function has been called,
- self._socket = "closed"
+ self._socket = None
+ self._closed = True
def set_timeout(self, timeout):
self._timeout = timeout
diff --git a/src/lib/python/isc/datasrc/Makefile.am b/src/lib/python/isc/datasrc/Makefile.am
index 5b9dafb..46fb661 100644
--- a/src/lib/python/isc/datasrc/Makefile.am
+++ b/src/lib/python/isc/datasrc/Makefile.am
@@ -3,3 +3,8 @@ SUBDIRS = . tests
python_PYTHON = __init__.py master.py sqlite3_ds.py
pythondir = $(pyexecdir)/isc/datasrc
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/datasrc/sqlite3_ds.py b/src/lib/python/isc/datasrc/sqlite3_ds.py
index 77b0828..a77645a 100644
--- a/src/lib/python/isc/datasrc/sqlite3_ds.py
+++ b/src/lib/python/isc/datasrc/sqlite3_ds.py
@@ -235,13 +235,13 @@ def load(dbfile, zone, reader):
zone += '.'
conn, cur = open(dbfile)
- old_zone_id = get_zoneid(zone, cur)
+ try:
+ old_zone_id = get_zoneid(zone, cur)
- temp = str(random.randrange(100000))
- cur.execute("INSERT INTO zones (name, rdclass) VALUES (?, 'IN')", [temp])
- new_zone_id = cur.lastrowid
+ temp = str(random.randrange(100000))
+ cur.execute("INSERT INTO zones (name, rdclass) VALUES (?, 'IN')", [temp])
+ new_zone_id = cur.lastrowid
- try:
for name, ttl, rdclass, rdtype, rdata in reader():
sigtype = ''
if rdtype.lower() == 'rrsig':
@@ -266,20 +266,20 @@ def load(dbfile, zone, reader):
VALUES (?, ?, ?, ?, ?, ?)""",
[new_zone_id, name, reverse_name(name), ttl,
rdtype, rdata])
+
+ if old_zone_id:
+ cur.execute("DELETE FROM zones WHERE id=?", [old_zone_id])
+ cur.execute("UPDATE zones SET name=? WHERE id=?", [zone, new_zone_id])
+ conn.commit()
+ cur.execute("DELETE FROM records WHERE zone_id=?", [old_zone_id])
+ cur.execute("DELETE FROM nsec3 WHERE zone_id=?", [old_zone_id])
+ conn.commit()
+ else:
+ cur.execute("UPDATE zones SET name=? WHERE id=?", [zone, new_zone_id])
+ conn.commit()
except Exception as e:
fail = "Error while loading " + zone + ": " + e.args[0]
raise Sqlite3DSError(fail)
-
- if old_zone_id:
- cur.execute("DELETE FROM zones WHERE id=?", [old_zone_id])
- cur.execute("UPDATE zones SET name=? WHERE id=?", [zone, new_zone_id])
- conn.commit()
- cur.execute("DELETE FROM records WHERE zone_id=?", [old_zone_id])
- cur.execute("DELETE FROM nsec3 WHERE zone_id=?", [old_zone_id])
- conn.commit()
- else:
- cur.execute("UPDATE zones SET name=? WHERE id=?", [zone, new_zone_id])
- conn.commit()
-
- cur.close()
- conn.close()
+ finally:
+ cur.close()
+ conn.close()
diff --git a/src/lib/python/isc/datasrc/tests/Makefile.am b/src/lib/python/isc/datasrc/tests/Makefile.am
index 4f87cc9..6f6d157 100644
--- a/src/lib/python/isc/datasrc/tests/Makefile.am
+++ b/src/lib/python/isc/datasrc/tests/Makefile.am
@@ -4,6 +4,14 @@ EXTRA_DIST = $(PYTESTS)
EXTRA_DIST += testdata/brokendb.sqlite3
EXTRA_DIST += testdata/example.com.sqlite3
+CLEANFILES = $(abs_builddir)/example.com.out.sqlite3
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
@@ -14,7 +22,9 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
+ $(LIBRARY_PATH_PLACEHOLDER) \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/python/isc/log \
TESTDATA_PATH=$(abs_srcdir)/testdata \
+ TESTDATA_WRITE_PATH=$(abs_builddir) \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
diff --git a/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py b/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py
index 013c7d7..707994f 100644
--- a/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py
+++ b/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py
@@ -17,8 +17,31 @@ from isc.datasrc import sqlite3_ds
import os
import socket
import unittest
+import sqlite3
TESTDATA_PATH = os.environ['TESTDATA_PATH'] + os.sep
+TESTDATA_WRITE_PATH = os.environ['TESTDATA_WRITE_PATH'] + os.sep
+
+READ_ZONE_DB_FILE = TESTDATA_PATH + "example.com.sqlite3"
+WRITE_ZONE_DB_FILE = TESTDATA_WRITE_PATH + "example.com.out.sqlite3"
+BROKEN_DB_FILE = TESTDATA_PATH + "brokendb.sqlite3"
+
+def example_reader():
+ my_zone = [
+ ("example.com.", "3600", "IN", "SOA", "ns.example.com. admin.example.com. 1234 3600 1800 2419200 7200"),
+ ("example.com.", "3600", "IN", "NS", "ns.example.com."),
+ ("ns.example.com.", "3600", "IN", "A", "192.0.2.1")
+ ]
+ for rr in my_zone:
+ yield rr
+
+def example_reader_nested():
+ # this iterator is used in the 'locked' test; it will cause
+ # the load() method to try and write to the same database
+ sqlite3_ds.load(WRITE_ZONE_DB_FILE,
+ ".",
+ example_reader)
+ return example_reader()
class TestSqlite3_ds(unittest.TestCase):
def test_zone_exist(self):
@@ -33,11 +56,40 @@ class TestSqlite3_ds(unittest.TestCase):
# Open a broken database file
self.assertRaises(sqlite3_ds.Sqlite3DSError,
sqlite3_ds.zone_exist, "example.com",
- TESTDATA_PATH + "brokendb.sqlite3")
+ BROKEN_DB_FILE)
self.assertTrue(sqlite3_ds.zone_exist("example.com.",
- TESTDATA_PATH + "example.com.sqlite3"))
+ READ_ZONE_DB_FILE))
self.assertFalse(sqlite3_ds.zone_exist("example.org.",
- TESTDATA_PATH + "example.com.sqlite3"))
+ READ_ZONE_DB_FILE))
+
+ def test_load_db(self):
+ sqlite3_ds.load(WRITE_ZONE_DB_FILE, ".", example_reader)
+
+ def test_locked_db(self):
+ # load it first to make sure it exists
+ sqlite3_ds.load(WRITE_ZONE_DB_FILE, ".", example_reader)
+
+ # and manually create a writing session as well
+ con = sqlite3.connect(WRITE_ZONE_DB_FILE);
+ cur = con.cursor()
+ cur.execute("delete from records")
+
+ self.assertRaises(sqlite3_ds.Sqlite3DSError,
+ sqlite3_ds.load, WRITE_ZONE_DB_FILE, ".",
+ example_reader)
+
+ con.rollback()
+
+ # and make sure lock does not stay
+ sqlite3_ds.load(WRITE_ZONE_DB_FILE, ".", example_reader)
+
+ # force locked db by nested loads
+ self.assertRaises(sqlite3_ds.Sqlite3DSError,
+ sqlite3_ds.load, WRITE_ZONE_DB_FILE, ".",
+ example_reader_nested)
+
+ # and make sure lock does not stay
+ sqlite3_ds.load(WRITE_ZONE_DB_FILE, ".", example_reader)
if __name__ == '__main__':
unittest.main()
diff --git a/src/lib/python/isc/log/Makefile.am b/src/lib/python/isc/log/Makefile.am
index acd2acc..5ff2c28 100644
--- a/src/lib/python/isc/log/Makefile.am
+++ b/src/lib/python/isc/log/Makefile.am
@@ -1,8 +1,41 @@
SUBDIRS = . tests
-python_PYTHON = __init__.py log.py
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
-pythondir = $(pyexecdir)/isc/log
+pythondir = $(pyexecdir)/isc
+python_LTLIBRARIES = log.la
+log_la_SOURCES = log.cc
+
+log_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+# Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be
+# placed after -Wextra defined in AM_CXXFLAGS
+log_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
+log_la_LDFLAGS = $(PYTHON_LDFLAGS)
+log_la_LDFLAGS += -module
+log_la_LIBADD = $(top_builddir)/src/lib/log/liblog.la
+log_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la
+log_la_LIBADD += $(top_builddir)/src/lib/config/libcfgclient.la
+log_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+log_la_LIBADD += $(PYTHON_LIB)
+
+# This is not installed, it helps locate the module during tests
+EXTRA_DIST = __init__.py
+
+# We're going to abuse install-data-local for a pre-install check.
+# This is to be considered a short term hack and is expected to be removed
+# in a near future version.
+install-data-local:
+ if test -d @pyexecdir@/isc/log; then \
+ echo "@pyexecdir@/isc/log is deprecated, and will confuse newer versions. Please (re)move it by hand."; \
+ exit 1; \
+ fi
pytest:
$(SHELL) tests/log_test
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/log/__init__.py b/src/lib/python/isc/log/__init__.py
index a34e164..641cf79 100644
--- a/src/lib/python/isc/log/__init__.py
+++ b/src/lib/python/isc/log/__init__.py
@@ -1 +1,33 @@
-from isc.log.log import *
+# Copyright (C) 2011 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# This file is not installed. The log.so is installed into the right place.
+# It is only to find it in the .libs directory when we run as a test or
+# from the build directory.
+# But as nobody gives us the builddir explicitly (and we can't use generation
+# from .in file, as it would put us into the builddir and we wouldn't be found)
+# we guess from current directory. Any idea for something better? This should
+# be enough for the tests, but would it work for B10_FROM_SOURCE as well?
+# Should we look there? Or define something in bind10_config?
+
+import os
+import sys
+
+for base in sys.path[:]:
+ loglibdir = os.path.join(base, 'isc/log/.libs')
+ if os.path.exists(loglibdir):
+ sys.path.insert(0, loglibdir)
+
+from log import *
diff --git a/src/lib/python/isc/log/log.cc b/src/lib/python/isc/log/log.cc
new file mode 100644
index 0000000..484151f
--- /dev/null
+++ b/src/lib/python/isc/log/log.cc
@@ -0,0 +1,701 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+#include <structmember.h>
+
+#include <config.h>
+
+#include <log/message_dictionary.h>
+#include <log/logger_manager.h>
+#include <log/logger.h>
+
+#include <config/ccsession.h>
+
+#include <string>
+#include <boost/bind.hpp>
+
+using namespace isc::log;
+using std::string;
+using boost::bind;
+
+// We encountered a strange problem with Clang (clang version 2.8
+// (tags/RELEASE_28 115909)) on OSX, where unwinding the stack
+// segfaults the moment this exception was thrown and caught.
+//
+// Placing it in a named namespace instead of the original
+// unnamed namespace appears to solve this, so as a temporary
+// workaround, we create a local randomly named namespace here
+// to solve this issue.
+namespace clang_unnamed_namespace_workaround {
+ // To propagate python exceptions through our code
+ // This exception is used to signal to the calling function that a
+ // proper Python Exception has already been set, and the caller
+ // should now return NULL.
+ // Since it is only used internally, and should not pass any
+ // information itself, is is not derived from std::exception
+ class InternalError : public std::exception {};
+}
+using namespace clang_unnamed_namespace_workaround;
+
+namespace {
+
+// This is for testing only. The real module will have it always set as
+// NULL and will use the global dictionary.
+MessageDictionary* testDictionary = NULL;
+
+PyObject*
+setTestDictionary(PyObject*, PyObject* args) {
+ PyObject* enableO;
+ // The API doesn't seem to provide conversion to bool,
+ // so we do it little bit manually
+ if (!PyArg_ParseTuple(args, "O", &enableO)) {
+ return (NULL);
+ }
+ int enableI(PyObject_IsTrue(enableO));
+ if (enableI == -1) {
+ return (NULL);
+ }
+ bool enable(enableI != 0);
+
+ try {
+ delete testDictionary;
+ testDictionary = NULL;
+ if (enable) {
+ testDictionary = new MessageDictionary;
+ }
+ }
+ catch (const std::exception& e) {
+ PyErr_SetString(PyExc_RuntimeError, e.what());
+ return (NULL);
+ }
+ catch (...) {
+ PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+ return (NULL);
+ }
+ Py_RETURN_NONE;
+}
+
+PyObject*
+createMessage(PyObject*, PyObject* args) {
+ const char* mid;
+ const char* text;
+ // We parse the strings
+ if (!PyArg_ParseTuple(args, "ss", &mid, &text)) {
+ return (NULL);
+ }
+ PyObject* origMid;
+ // And extract the original representation of the message
+ // ID, so we can return it instead of creating another instance.
+ // This call shouldn't fail if the previous suceeded.
+ if (!PyArg_ParseTuple(args, "Os", &origMid, &text)) {
+ return (NULL);
+ }
+
+ try {
+ MessageDictionary* dict = testDictionary ? testDictionary :
+ &MessageDictionary::globalDictionary();
+
+ // We ignore the result, they will be in some kind of dupe list
+ // if there's a problem
+ dict->add(mid, text);
+ }
+ catch (const std::exception& e) {
+ PyErr_SetString(PyExc_RuntimeError, e.what());
+ return (NULL);
+ }
+ catch (...) {
+ PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+ return (NULL);
+ }
+
+ // Return the ID
+ Py_INCREF(origMid);
+ return (origMid);
+}
+
+PyObject*
+getMessage(PyObject*, PyObject* args) {
+ const char* mid;
+ if (!PyArg_ParseTuple(args, "s", &mid)) {
+ return (NULL);
+ }
+
+ try {
+ MessageDictionary* dict = testDictionary ? testDictionary :
+ &MessageDictionary::globalDictionary();
+
+ const std::string& result(dict->getText(mid));
+ if (result.empty()) {
+ Py_RETURN_NONE;
+ } else {
+ return (Py_BuildValue("s", result.c_str()));
+ }
+ }
+ catch (const std::exception& e) {
+ PyErr_SetString(PyExc_RuntimeError, e.what());
+ return (NULL);
+ }
+ catch (...) {
+ PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+ return (NULL);
+ }
+}
+
+PyObject*
+reset(PyObject*, PyObject*) {
+ LoggerManager::reset();
+ Py_RETURN_NONE;
+}
+
+PyObject*
+init(PyObject*, PyObject* args) {
+ const char* root;
+ const char* file(NULL);
+ const char* severity("INFO");
+ int dbglevel(0);
+ if (!PyArg_ParseTuple(args, "s|siz", &root, &severity, &dbglevel, &file)) {
+ return (NULL);
+ }
+
+ try {
+ LoggerManager::init(root, getSeverity(severity), dbglevel, file);
+ }
+ catch (const std::exception& e) {
+ PyErr_SetString(PyExc_RuntimeError, e.what());
+ return (NULL);
+ }
+ catch (...) {
+ PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+ return (NULL);
+ }
+ Py_RETURN_NONE;
+}
+
+PyObject*
+logConfigUpdate(PyObject*, PyObject* args) {
+ // we have no wrappers for ElementPtr and ConfigData,
+ // So we expect JSON strings and convert them.
+ // The new_config object is assumed to have been validated.
+
+ const char* new_config_json;
+ const char* mod_spec_json;
+ if (!PyArg_ParseTuple(args, "ss",
+ &new_config_json, &mod_spec_json)) {
+ return (NULL);
+ }
+
+ try {
+ isc::data::ConstElementPtr new_config =
+ isc::data::Element::fromJSON(new_config_json);
+ isc::data::ConstElementPtr mod_spec_e =
+ isc::data::Element::fromJSON(mod_spec_json);
+ isc::config::ModuleSpec mod_spec(mod_spec_e);
+ isc::config::ConfigData config_data(mod_spec);
+ isc::config::default_logconfig_handler("logging", new_config,
+ config_data);
+
+ Py_RETURN_NONE;
+ } catch (const isc::data::JSONError& je) {
+ std::string error_msg = std::string("JSON format error: ") + je.what();
+ PyErr_SetString(PyExc_TypeError, error_msg.c_str());
+ } catch (const isc::data::TypeError& de) {
+ PyErr_SetString(PyExc_TypeError, "argument 1 of log_config_update "
+ "is not a map of config data");
+ } catch (const isc::config::ModuleSpecError& mse) {
+ PyErr_SetString(PyExc_TypeError, "argument 2 of log_config_update "
+ "is not a correct module specification");
+ } catch (const std::exception& e) {
+ PyErr_SetString(PyExc_RuntimeError, e.what());
+ } catch (...) {
+ PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+ }
+ return (NULL);
+}
+
+PyMethodDef methods[] = {
+ {"set_test_dictionary", setTestDictionary, METH_VARARGS,
+ "Set or unset testing mode for message dictionary. In testing, "
+ "the create_message and get_message functions work on different "
+ "than the logger-global dictionary, not polluting it."},
+ {"create_message", createMessage, METH_VARARGS,
+ "Creates a new message in the dictionary. You shouldn't need to "
+ "call this directly, it should be called by the generated message "
+ "file. Returns the identifier to be used in logging. The text "
+ "shouldn't be empty."},
+ {"get_message", getMessage, METH_VARARGS,
+ "Get a message. This function is for testing purposes and you don't "
+ "need to call it. It returns None if the message does not exist."},
+ {"reset", reset, METH_NOARGS,
+ "Reset all logging. For testing purposes only, do not use."},
+ {"init", init, METH_VARARGS,
+ "Run-time initialization. You need to call this before you do any "
+ "logging, to configure the root logger name. You may also provide "
+ "logging severity (one of 'DEBUG', 'INFO', 'WARN', 'ERROR' or "
+ "'FATAL'), a debug level (integer in the range 0-99) and a file name "
+ "of a dictionary with message text translations."},
+ {"log_config_update", logConfigUpdate, METH_VARARGS,
+ "Update logger settings. This method is automatically used when "
+ "ModuleCCSession is initialized with handle_logging_config set "
+ "to True. When called, the first argument is the new logging "
+ "configuration (in JSON format). The second argument is "
+ "the raw specification (as returned from "
+ "ConfigData.get_module_spec().get_full_spec(), and converted to "
+ "JSON format).\n"
+ "Raises a TypeError if either argument is not a (correct) JSON "
+ "string, or if the spec is not a correct spec.\n"
+ "If this call succeeds, the global logger settings have "
+ "been updated."
+ },
+ {NULL, NULL, 0, NULL}
+};
+
+class LoggerWrapper : public PyObject {
+// Everything is public here, as it is accessible only inside this .cc file.
+public:
+ Logger *logger_;
+};
+
+extern PyTypeObject logger_type;
+
+int
+Logger_init(LoggerWrapper* self, PyObject* args) {
+ const char* name;
+ if (!PyArg_ParseTuple(args, "s", &name)) {
+ return (-1);
+ }
+ try {
+ self->logger_ = new Logger(name);
+ return (0);
+ }
+ catch (const std::exception& e) {
+ PyErr_SetString(PyExc_RuntimeError, e.what());
+ return (-1);
+ }
+ catch (...) {
+ PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+ return (-1);
+ }
+}
+
+void
+Logger_destroy(LoggerWrapper* const self) {
+ delete self->logger_;
+ self->logger_ = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+// The isc::log doesn't contain function to convert this way
+const char*
+severityToText(const Severity& severity) {
+ switch (severity) {
+ case DEFAULT:
+ return ("DEFAULT");
+ case DEBUG:
+ return ("DEBUG");
+ case INFO:
+ return ("INFO");
+ case WARN:
+ return ("WARN");
+ case ERROR:
+ return ("ERROR");
+ case FATAL:
+ return ("FATAL");
+ default:
+ return (NULL);
+ }
+}
+
+PyObject*
+Logger_getEffectiveSeverity(LoggerWrapper* self, PyObject*) {
+ try {
+ return (Py_BuildValue("s",
+ severityToText(
+ self->logger_->getEffectiveSeverity())));
+ }
+ catch (const std::exception& e) {
+ PyErr_SetString(PyExc_RuntimeError, e.what());
+ return (NULL);
+ }
+ catch (...) {
+ PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+ return (NULL);
+ }
+}
+
+PyObject*
+Logger_getEffectiveDebugLevel(LoggerWrapper* self, PyObject*) {
+ try {
+ return (Py_BuildValue("i", self->logger_->getEffectiveDebugLevel()));
+ }
+ catch (const std::exception& e) {
+ PyErr_SetString(PyExc_RuntimeError, e.what());
+ return (NULL);
+ }
+ catch (...) {
+ PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+ return (NULL);
+ }
+}
+
+PyObject*
+Logger_setSeverity(LoggerWrapper* self, PyObject* args) {
+ const char* severity;
+ int dbgLevel = 0;
+ if (!PyArg_ParseTuple(args, "z|i", &severity, &dbgLevel)) {
+ return (NULL);
+ }
+ try {
+ self->logger_->setSeverity((severity == NULL) ? DEFAULT :
+ getSeverity(severity), dbgLevel);
+ }
+ catch (const std::exception& e) {
+ PyErr_SetString(PyExc_RuntimeError, e.what());
+ return (NULL);
+ }
+ catch (...) {
+ PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+ return (NULL);
+ }
+ Py_RETURN_NONE;
+}
+
+template<class FPtr> // Who should remember the pointer-to-method syntax
+PyObject*
+Logger_isLevelEnabled(LoggerWrapper* self, FPtr function) {
+ try {
+ if ((self->logger_->*function)()) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+ }
+ catch (const std::exception& e) {
+ PyErr_SetString(PyExc_RuntimeError, e.what());
+ return (NULL);
+ }
+ catch (...) {
+ PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+ return (NULL);
+ }
+}
+
+PyObject*
+Logger_isInfoEnabled(LoggerWrapper* self, PyObject*) {
+ return (Logger_isLevelEnabled(self, &Logger::isInfoEnabled));
+}
+
+PyObject*
+Logger_isWarnEnabled(LoggerWrapper* self, PyObject*) {
+ return (Logger_isLevelEnabled(self, &Logger::isWarnEnabled));
+}
+
+PyObject*
+Logger_isErrorEnabled(LoggerWrapper* self, PyObject*) {
+ return (Logger_isLevelEnabled(self, &Logger::isErrorEnabled));
+}
+
+PyObject*
+Logger_isFatalEnabled(LoggerWrapper* self, PyObject*) {
+ return (Logger_isLevelEnabled(self, &Logger::isFatalEnabled));
+}
+
+PyObject*
+Logger_isDebugEnabled(LoggerWrapper* self, PyObject* args) {
+ int level = MIN_DEBUG_LEVEL;
+ if (!PyArg_ParseTuple(args, "|i", &level)) {
+ return (NULL);
+ }
+
+ try {
+ if (self->logger_->isDebugEnabled(level)) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+ }
+ catch (const std::exception& e) {
+ PyErr_SetString(PyExc_RuntimeError, e.what());
+ return (NULL);
+ }
+ catch (...) {
+ PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+ return (NULL);
+ }
+}
+
+string
+objectToStr(PyObject* object, bool convert) {
+ PyObject* cleanup(NULL);
+ if (convert) {
+ object = cleanup = PyObject_Str(object);
+ if (object == NULL) {
+ throw InternalError();
+ }
+ }
+ const char* value;
+ PyObject* tuple(Py_BuildValue("(O)", object));
+ if (tuple == NULL) {
+ if (cleanup != NULL) {
+ Py_DECREF(cleanup);
+ }
+ throw InternalError();
+ }
+
+ if (!PyArg_ParseTuple(tuple, "s", &value)) {
+ Py_DECREF(tuple);
+ if (cleanup != NULL) {
+ Py_DECREF(cleanup);
+ }
+ throw InternalError();
+ }
+ string result(value);
+ Py_DECREF(tuple);
+ if (cleanup != NULL) {
+ Py_DECREF(cleanup);
+ }
+ return (result);
+}
+
+// Generic function to output the logging message. Called by the real functions.
+template<class Function>
+PyObject*
+Logger_performOutput(Function function, PyObject* args, bool dbgLevel) {
+ try {
+ Py_ssize_t number(PyObject_Length(args));
+ if (number < 0) {
+ return (NULL);
+ }
+
+ // Which argument is the first to format?
+ size_t start(1);
+ if (dbgLevel) {
+ start ++;
+ }
+
+ if (number < start) {
+ return (PyErr_Format(PyExc_TypeError, "Too few arguments to "
+ "logging call, at least %zu needed and %zd "
+ "given", start, number));
+ }
+
+ // Extract the fixed arguments
+ PyObject *midO(PySequence_GetItem(args, start - 1));
+ if (midO == NULL) {
+ return (NULL);
+ }
+ string mid(objectToStr(midO, false));
+ long dbg(0);
+ if (dbgLevel) {
+ PyObject *dbgO(PySequence_GetItem(args, 0));
+ if (dbgO == NULL) {
+ return (NULL);
+ }
+ dbg = PyLong_AsLong(dbgO);
+ if (PyErr_Occurred()) {
+ return (NULL);
+ }
+ }
+
+ // We create the logging message right now. If we fail to convert a
+ // parameter to string, at least the part that we already did will
+ // be output
+ Logger::Formatter formatter(function(dbg, mid.c_str()));
+
+ // Now process the rest of parameters, convert each to string and put
+ // into the formatter. It will print itself in the end.
+ for (size_t i(start); i < number; ++ i) {
+ PyObject* param(PySequence_GetItem(args, i));
+ if (param == NULL) {
+ return (NULL);
+ }
+ formatter = formatter.arg(objectToStr(param, true));
+ }
+ Py_RETURN_NONE;
+ }
+ catch (const InternalError&) {
+ return (NULL);
+ }
+ catch (const std::exception& e) {
+ PyErr_SetString(PyExc_RuntimeError, e.what());
+ return (NULL);
+ }
+ catch (...) {
+ PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+ return (NULL);
+ }
+}
+
+// Now map the functions into the performOutput. I wish C++ could do
+// functional programming.
+PyObject*
+Logger_debug(LoggerWrapper* self, PyObject* args) {
+ return (Logger_performOutput(bind(&Logger::debug, self->logger_, _1, _2),
+ args, true));
+}
+
+PyObject*
+Logger_info(LoggerWrapper* self, PyObject* args) {
+ return (Logger_performOutput(bind(&Logger::info, self->logger_, _2),
+ args, false));
+}
+
+PyObject*
+Logger_warn(LoggerWrapper* self, PyObject* args) {
+ return (Logger_performOutput(bind(&Logger::warn, self->logger_, _2),
+ args, false));
+}
+
+PyObject*
+Logger_error(LoggerWrapper* self, PyObject* args) {
+ return (Logger_performOutput(bind(&Logger::error, self->logger_, _2),
+ args, false));
+}
+
+PyObject*
+Logger_fatal(LoggerWrapper* self, PyObject* args) {
+ return (Logger_performOutput(bind(&Logger::fatal, self->logger_, _2),
+ args, false));
+}
+
+PyMethodDef loggerMethods[] = {
+ { "get_effective_severity",
+ reinterpret_cast<PyCFunction>(Logger_getEffectiveSeverity),
+ METH_NOARGS, "Returns the effective logging severity as string" },
+ { "get_effective_debug_level",
+ reinterpret_cast<PyCFunction>(Logger_getEffectiveDebugLevel),
+ METH_NOARGS, "Returns the current debug level." },
+ { "set_severity",
+ reinterpret_cast<PyCFunction>(Logger_setSeverity), METH_VARARGS,
+ "Sets the severity of a logger. The parameters are severity as a "
+ "string and, optionally, a debug level (integer in range 0-99). "
+ "The severity may be NULL, in which case an inherited value is taken."
+ },
+ { "is_debug_enabled", reinterpret_cast<PyCFunction>(Logger_isDebugEnabled),
+ METH_VARARGS, "Returns if the logger would log debug message now. "
+ "You can provide a desired debug level." },
+ { "is_info_enabled", reinterpret_cast<PyCFunction>(Logger_isInfoEnabled),
+ METH_NOARGS, "Returns if the logger would log info message now." },
+ { "is_warn_enabled", reinterpret_cast<PyCFunction>(Logger_isWarnEnabled),
+ METH_NOARGS, "Returns if the logger would log warn message now." },
+ { "is_error_enabled", reinterpret_cast<PyCFunction>(Logger_isErrorEnabled),
+ METH_NOARGS, "Returns if the logger would log error message now." },
+ { "is_fatal_enabled", reinterpret_cast<PyCFunction>(Logger_isFatalEnabled),
+ METH_NOARGS, "Returns if the logger would log fatal message now." },
+ { "debug", reinterpret_cast<PyCFunction>(Logger_debug), METH_VARARGS,
+ "Logs a debug-severity message. It takes the debug level, message ID "
+ "and any number of stringifiable arguments to the message." },
+ { "info", reinterpret_cast<PyCFunction>(Logger_info), METH_VARARGS,
+ "Logs a info-severity message. It taskes the message ID and any "
+ "number of stringifiable arguments to the message." },
+ { "warn", reinterpret_cast<PyCFunction>(Logger_warn), METH_VARARGS,
+ "Logs a warn-severity message. It taskes the message ID and any "
+ "number of stringifiable arguments to the message." },
+ { "error", reinterpret_cast<PyCFunction>(Logger_error), METH_VARARGS,
+ "Logs a error-severity message. It taskes the message ID and any "
+ "number of stringifiable arguments to the message." },
+ { "fatal", reinterpret_cast<PyCFunction>(Logger_fatal), METH_VARARGS,
+ "Logs a fatal-severity message. It taskes the message ID and any "
+ "number of stringifiable arguments to the message." },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject logger_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "isc.log.Logger",
+ sizeof(LoggerWrapper), // tp_basicsize
+ 0, // tp_itemsize
+ reinterpret_cast<destructor>(Logger_destroy), // tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ NULL, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "Wrapper around the C++ isc::log::Logger class."
+ "It is not complete, but everything important should be here.",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ loggerMethods, // tp_methods
+ NULL, // tp_members
+ NULL, // tp_getset
+ NULL, // tp_base
+ NULL, // tp_dict
+ NULL, // tp_descr_get
+ NULL, // tp_descr_set
+ 0, // tp_dictoffset
+ reinterpret_cast<initproc>(Logger_init), // tp_init
+ NULL, // tp_alloc
+ PyType_GenericNew, // tp_new
+ NULL, // tp_free
+ NULL, // tp_is_gc
+ NULL, // tp_bases
+ NULL, // tp_mro
+ NULL, // tp_cache
+ NULL, // tp_subclasses
+ NULL, // tp_weaklist
+ NULL, // tp_del
+ 0 // tp_version_tag
+};
+
+PyModuleDef iscLog = {
+ { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
+ "log",
+ "Python bindings for the classes in the isc::log namespace.\n\n"
+ "These bindings are close match to the C++ API, but they are not complete "
+ "(some parts are not needed) and some are done in more python-like ways.",
+ -1,
+ methods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+} // end anonymous namespace
+
+PyMODINIT_FUNC
+PyInit_log(void) {
+ PyObject* mod = PyModule_Create(&iscLog);
+ if (mod == NULL) {
+ return (NULL);
+ }
+
+ if (PyType_Ready(&logger_type) < 0) {
+ return (NULL);
+ }
+
+ if (PyModule_AddObject(mod, "Logger",
+ static_cast<PyObject*>(static_cast<void*>(
+ &logger_type))) < 0) {
+ return (NULL);
+ }
+ Py_INCREF(&logger_type);
+
+ return (mod);
+}
diff --git a/src/lib/python/isc/log/log.py b/src/lib/python/isc/log/log.py
deleted file mode 100644
index 74261e2..0000000
--- a/src/lib/python/isc/log/log.py
+++ /dev/null
@@ -1,280 +0,0 @@
-# Copyright (C) 2010 Internet Systems Consortium.
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
-# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
-# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
-# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-"""This module is to convert python logging module over
-to log4python.
-Copyright (C) 2010 Internet Systems Consortium.
-To use, simply 'import isc.log.log' and log away!
-"""
-import os
-import sys
-import syslog
-import logging
-import logging.handlers
-
-"""LEVELS: logging levels mapping
-"""
-LEVELS = {'debug' : logging.DEBUG,
- 'info' : logging.INFO,
- 'warning' : logging.WARNING,
- 'error' : logging.ERROR,
- 'critical' : logging.CRITICAL}
-
-FORMATTER = logging.Formatter("%(name)s: %(levelname)s: %(message)s")
-TIME_FORMATTER = logging.Formatter("%(asctime)s.%(msecs)03d %(name)s: %(levelname)s: %(message)s",
- "%d-%b-%Y %H:%M:%S")
-
-def log_err(err_type, err_msg):
- sys.stderr.write(err_type + ": " + "%s.\n" % str(err_msg)[str(err_msg).find(']')+1:])
-
-
-class NSFileLogHandler(logging.handlers.RotatingFileHandler):
- """RotatingFileHandler: replace RotatingFileHandler with a custom handler"""
-
- def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=0):
- abs_file_name = self._get_abs_file_path(filename)
- """Create log directory beforehand, because the underlying logging framework won't
- create non-exsiting log directory on writing logs.
- """
- if not (os.path.exists(os.path.dirname(abs_file_name))):
- os.makedirs(os.path.dirname(abs_file_name))
- super(NSFileLogHandler, self).__init__(abs_file_name, mode, maxBytes,
- backupCount, encoding, delay)
-
- def handleError(self, record):
- """Overwrite handleError to provide more user-friendly error messages"""
- if logging.raiseExceptions:
- ei = sys.exc_info()
- if (ei[1]):
- sys.stderr.write("[b10-logging] : " + str(ei[1]))
-
- def _get_abs_file_path(self, file_name):
- """ Get absolute file path"""
- # For a bare filename, log_dir will be set the current directory.
- if not os.path.dirname(file_name):
- abs_file_dir = os.getcwd()
- else:
- abs_file_dir = os.path.abspath(os.path.dirname(file_name))
- abs_file_name = os.path.join(abs_file_dir, os.path.basename(file_name))
- return abs_file_name
-
- def shouldRollover(self, record):
- """Rewrite RotatingFileHandler.shouldRollover.
-
- If the log file is deleted at runtime, a new file will be created.
- """
- dfn = self.baseFilename
- if (self.stream) and (not os.path.exists(dfn)): #Does log file exist?
- self.stream = None
- """ Log directory may be deleted while bind10 running or updated with a
- non-existing directory. Need to create log directory beforehand, because
- the underlying logging framework won't create non-exsiting log directory
- on writing logs.
- """
- if not (os.path.exists(os.path.dirname(dfn))): #Does log subdirectory exist?
- os.makedirs(os.path.dirname(dfn))
- self.stream = self._open()
- return super(NSFileLogHandler, self).shouldRollover(record)
-
- def update_config(self, file_name, backup_count, max_bytes):
- """Update RotatingFileHandler configuration.
- Changes will be picked up in the next call to shouldRollover().
-
- input:
- log file name
- max backup count
- predetermined log file size
- """
- abs_file_name = self._get_abs_file_path(file_name)
- self.baseFilename = abs_file_name
- self.maxBytes = max_bytes
- self.backupCount = backup_count
-
-
-class NSSysLogHandler(logging.Handler):
- """Replace SysLogHandler with a custom handler
-
- A handler class which sends formatted logging records to a syslog
- server.
- """
- def __init__(self, ident, logopt=0, facility=syslog.LOG_USER):
- """Initialize a handler.
-
- If facility is not specified, LOG_USER is used.
- """
- super(NSSysLogHandler, self).__init__()
- self._ident = ident
- self._logopt = logopt
- self._facility = facility
- self._mappings = {
- logging.DEBUG: syslog.LOG_DEBUG,
- logging.INFO: syslog.LOG_INFO,
- logging.WARNING: syslog.LOG_WARNING,
- logging.ERROR: syslog.LOG_ERR,
- logging.CRITICAL: syslog.LOG_CRIT,
- }
-
- def _encodeLevel(self, level):
- """Encoding the priority."""
- return self._mappings.get(level, syslog.LOG_INFO)
-
- def emit(self, record):
- """Emit a record.
-
- The record is formatted, and then sent to the syslog server. If
- exception information is present, it is NOT sent to the server.
- """
- syslog.openlog(self._ident, self._logopt, self._facility)
- msg = self.format(record)
- prio = self._encodeLevel(record.levelno)
- syslog.syslog(prio, msg)
- syslog.closelog()
-
-
-class NSLogger(logging.getLoggerClass()):
- """Override logging.logger behaviour."""
- def __init__(self, log_name, log_file, severity='debug', versions=0,
- max_bytes=0, log_to_console=True):
- """Initializes the logger with some specific parameters
-
- If log_to_console is True, stream handler will be used;
- else syslog handler will be used.
-
- To disable file handler, set log_file = ''.
- """
- self._log_name = log_name
- self._log_file = log_file
- self._severity = severity
- self._versions = versions
- self._max_bytes = max_bytes
-
- super(NSLogger, self).__init__(self._log_name)
-
- # Set up a specific logger with our desired output level
- logLevel = LEVELS.get(self._severity, logging.NOTSET)
- self.setLevel(logLevel)
-
- self._file_handler = None
- self._stream_handler = None
- self._syslog_handler = None
-
- self._add_rotate_handler(self._log_file, self._versions, self._max_bytes)
- if log_to_console:
- self._add_stream_handler()
- else:
- self._add_syslog_handler()
-
- def _add_rotate_handler(self, log_file, backup_count, max_bytes):
- """Add a rotate file handler.
-
- input:
- log_file : the location of log file. Handler will not be created
- if log_file=''
- max_bytes : limit log growth
- backup_count : max backup count
- """
- if (log_file != 0 and log_file != ''):
- try:
- self._file_handler = NSFileLogHandler(filename = log_file,
- maxBytes = max_bytes, backupCount = backup_count)
- except (IOError, OSError) as e:
- self._file_handler = None
- log_err("[b10-logging] Add file handler fail", str(e))
- return
- self._file_handler.setFormatter(TIME_FORMATTER)
- self.addHandler(self._file_handler)
-
- def _add_stream_handler(self):
- """Add a stream handler.
-
- sys.stderr will be used for logging output.
- """
- self._stream_handler = logging.StreamHandler()
- self._stream_handler.setFormatter(TIME_FORMATTER)
- self.addHandler(self._stream_handler)
-
- def _add_syslog_handler(self, nsfacility=syslog.LOG_USER):
- """Add a syslog handler.
-
- If facility is not specified, LOG_USER is used.
- The default severity level is INFO.
- """
- self._syslog_handler = NSSysLogHandler('BIND10', facility = nsfacility)
- self._syslog_handler.setFormatter(FORMATTER)
- #set syslog handler severity level INFO
- self._syslog_handler.setLevel(logging.INFO)
- self.addHandler(self._syslog_handler)
-
- def _update_rotate_handler(self, log_file, backup_count, max_bytes):
- """If the rotate file handler has been added to the logger, update its
- configuration, or add it to the logger.
- """
- if (self._file_handler in self.handlers):
- if (log_file != 0 and log_file != ''):
- self._file_handler.update_config(log_file, backup_count, max_bytes)
- else:
- """If log file is empty, the handler will be removed."""
- self._file_handler.flush()
- self._file_handler.close()
- self.removeHandler(self._file_handler)
- else:
- self._add_rotate_handler(log_file, backup_count, max_bytes)
-
- def _get_config(self, config_data):
- """Get config data from module configuration"""
-
- log_file_str = config_data.get('log_file')
- if (log_file_str):
- self._log_file = log_file_str
-
- severity_str = config_data.get('log_severity')
- if (severity_str):
- self._severity = severity_str
-
- versions_str = config_data.get('log_versions')
- if (versions_str):
- self._versions = int(versions_str)
-
- max_bytes_str = config_data.get('log_max_bytes')
- if (max_bytes_str):
- self._max_bytes = int(max_bytes_str)
-
- def update_config(self, config_data):
- """Update logger's configuration.
-
- We can update logger's log level and its rotate file handler's configuration.
- """
- self._get_config(config_data)
-
- logLevel = LEVELS.get(self._severity, logging.NOTSET)
- if (logLevel != self.getEffectiveLevel()):
- self.setLevel(logLevel)
- self._update_rotate_handler(self._log_file, self._versions, self._max_bytes)
-
- def log_message(self, level, msg, *args, **kwargs):
- """Log 'msg % args' with the integer severity 'level'.
-
- To pass exception information, use the keyword argument exc_info with
- a true value, e.g.
-
- logger.log_message('info', "We have a %s", "mysterious problem").
- """
- logLevel = LEVELS.get(level, logging.NOTSET)
- try:
- self.log(logLevel, msg, *args, **kwargs)
- except (TypeError, KeyError) as e:
- sys.stderr.write("[b10-logging] Log message fail %s\n" % (str(e)))
-
-
diff --git a/src/lib/python/isc/log/tests/Makefile.am b/src/lib/python/isc/log/tests/Makefile.am
index 86b3e5d..6bb67de 100644
--- a/src/lib/python/isc/log/tests/Makefile.am
+++ b/src/lib/python/isc/log/tests/Makefile.am
@@ -1,16 +1,28 @@
PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
PYTESTS = log_test.py
-EXTRA_DIST = $(PYTESTS)
+EXTRA_DIST = $(PYTESTS) log_console.py.in console.out check_output.sh
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
+ $(LIBRARY_PATH_PLACEHOLDER) \
+ env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/python/isc/log \
+ $(abs_srcdir)/check_output.sh $(abs_builddir)/log_console.py $(abs_srcdir)/console.out
if ENABLE_PYTHON_COVERAGE
- touch $(abs_top_srcdir)/.coverage
+ touch $(abs_top_srcdir)/.coverage
rm -f .coverage
${LN_S} $(abs_top_srcdir)/.coverage .coverage
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
- env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/python/isc/log \
+ $(LIBRARY_PATH_PLACEHOLDER) \
+ env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/python/isc/log:$(abs_top_builddir)/src/lib/log/python/.libs \
+ B10_TEST_PLUGIN_DIR=$(abs_top_srcdir)/src/bin/cfgmgr/plugins \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
diff --git a/src/lib/python/isc/log/tests/check_output.sh b/src/lib/python/isc/log/tests/check_output.sh
new file mode 100755
index 0000000..32146af
--- /dev/null
+++ b/src/lib/python/isc/log/tests/check_output.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+"$1" 2>&1 | cut -d\ -f3- | diff - "$2" 1>&2
diff --git a/src/lib/python/isc/log/tests/console.out b/src/lib/python/isc/log/tests/console.out
new file mode 100644
index 0000000..fbb1bb9
--- /dev/null
+++ b/src/lib/python/isc/log/tests/console.out
@@ -0,0 +1,4 @@
+INFO [test.output] MSG_ID Message with list [1, 2, 3, 4]
+WARN [test.output] DIFFERENT Different message
+FATAL [test.output] MSG_ID Message with 2 1
+DEBUG [test.output] MSG_ID Message with 3 2
diff --git a/src/lib/python/isc/log/tests/log_console.py.in b/src/lib/python/isc/log/tests/log_console.py.in
new file mode 100755
index 0000000..af05f61
--- /dev/null
+++ b/src/lib/python/isc/log/tests/log_console.py.in
@@ -0,0 +1,15 @@
+#!@PYTHON@
+
+import isc.log
+# This would come from a dictionary in real life
+MSG_ID = isc.log.create_message("MSG_ID", "Message with %2 %1")
+DIFFERENT = isc.log.create_message("DIFFERENT", "Different message")
+isc.log.init("test")
+logger = isc.log.Logger("output")
+
+logger.debug(20, MSG_ID, "test", "no output")
+logger.info(MSG_ID, [1, 2, 3, 4], "list")
+logger.warn(DIFFERENT)
+logger.fatal(MSG_ID, 1, 2)
+logger.set_severity("DEBUG", 99)
+logger.debug(1, MSG_ID, 2, 3)
diff --git a/src/lib/python/isc/log/tests/log_test.in b/src/lib/python/isc/log/tests/log_test.in
deleted file mode 100644
index 60e5e3f..0000000
--- a/src/lib/python/isc/log/tests/log_test.in
+++ /dev/null
@@ -1,26 +0,0 @@
-#! /bin/sh
-
-# Copyright (C) 2010 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.
-
-PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
-export PYTHON_EXEC
-
-TEST_PATH=@abs_top_srcdir@/src/lib/python/isc/log/tests
-PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python
-export PYTHONPATH
-
-cd ${TEST_PATH}
-exec ${PYTHON_EXEC} -O log_test.py $*
diff --git a/src/lib/python/isc/log/tests/log_test.py b/src/lib/python/isc/log/tests/log_test.py
index 026dee1..4292b6c 100644
--- a/src/lib/python/isc/log/tests/log_test.py
+++ b/src/lib/python/isc/log/tests/log_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2010 Internet Systems Consortium.
+# Copyright (C) 2011 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
@@ -13,225 +13,151 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-#
-# Tests for the python logging module
-#
-
-from isc.log.log import *
+# This tests it can be loaded, nothing more yet
+import isc.log
import unittest
-import os
-import sys
-import tempfile
-
-
-class TestRotateFileHandler(unittest.TestCase):
+import json
+import bind10_config
+from isc.config.ccsession import path_search
+class LogDict(unittest.TestCase):
def setUp(self):
- self.FILE_LOG1 = tempfile.NamedTemporaryFile(mode='w',
- prefix="b10",
- delete=True)
- self.FILE_LOG2 = tempfile.NamedTemporaryFile(mode='w',
- prefix="b10",
- delete=True)
- self.FILE_LOG3 = tempfile.NamedTemporaryFile(mode='w',
- prefix="b10",
- delete=True)
- self.handler = NSFileLogHandler(filename = self.FILE_LOG1.name,
- maxBytes = 1024,
- backupCount = 5)
-
- def test_shouldRollover(self):
- if(os.path.exists(self.FILE_LOG1.name)):
- os.remove(self.FILE_LOG1.name)
- record = logging.LogRecord(None, None, "", 0, "rotate file handler", (), None, None)
- self.handler.shouldRollover(record)
- self.assertTrue(os.path.exists(self.FILE_LOG1.name))
-
- def test_get_absolute_file_path(self):
- abs_file_name = self.handler._get_abs_file_path(self.FILE_LOG1.name)
- self.assertEqual(abs_file_name, self.FILE_LOG1.name)
- # test bare filename
- file_name1 = "bind10.py"
- abs_file_name = self.handler._get_abs_file_path(file_name1)
- self.assertEqual(abs_file_name, os.path.join(os.getcwd(), file_name1))
- # test relative path
- file_name2 = "./bind10.py"
- abs_file_name = self.handler._get_abs_file_path(file_name2)
- self.assertEqual(abs_file_name, os.path.join(os.getcwd(), os.path.basename(file_name2)))
-
- def test_update_config(self):
- self.handler.update_config(self.FILE_LOG2.name, 3, 512)
- self.assertEqual(self.handler.baseFilename, self.FILE_LOG2.name)
- self.assertEqual(self.handler.maxBytes, 512)
- self.assertEqual(self.handler.backupCount, 3)
-
- # check the existence of new log file.
- # emit() will call shouldRollover() to update the log file
- if(os.path.exists(self.FILE_LOG2.name)):
- os.remove(self.FILE_LOG2.name)
- record = logging.LogRecord(None, None, "", 0, "rotate file handler", (), None, None)
- self.handler.emit(record)
- self.assertTrue(os.path.exists(self.FILE_LOG2.name))
-
- def test_handle_Error(self):
- if(os.path.exists(self.FILE_LOG3.name)):
- os.remove(self.FILE_LOG3.name)
- # redirect error message to file
- savederr = sys.stderr
- errfd = open(self.FILE_LOG3.name, 'w+')
- sys.stderr = errfd
- record = logging.LogRecord(None, None, "", 0, "record message", (), None, None)
- try:
- raise ValueError("ValueError")
- except ValueError:
- self.handler.handleError(record)
-
- self.assertEqual("[b10-logging] : ValueError", errfd.read())
- sys.stderr = savederr
- errfd.close()
-
+ # We work on a test dictionary now.
+ isc.log.set_test_dictionary(True)
def tearDown(self):
- self.handler.flush()
- self.handler.close()
- self.FILE_LOG1.close()
- self.FILE_LOG2.close()
- self.FILE_LOG3.close()
+ # Return to the global dictionary
+ isc.log.set_test_dictionary(False)
-class TestSysLogHandler(unittest.TestCase):
- def setUp(self):
- self.handler = NSSysLogHandler("BIND10")
-
- def test_encodeLevel(self):
- sysLevel = self.handler._encodeLevel(logging.ERROR)
- self.assertEqual(sysLevel, syslog.LOG_ERR)
+ def test_load_msgs(self):
+ # Try loading a message and see it's there, but nothing more
+ self.assertEqual(isc.log.create_message("ID", "Text"), "ID")
+ self.assertEqual(isc.log.get_message("ID"), "Text")
+ self.assertEqual(isc.log.get_message("no-ID"), None)
- def test_emit(self):
- syslog_message = "bind10 syslog testing"
- record = logging.LogRecord(None, None, "", 0, syslog_message, (), None, None)
- self.handler.emit(record)
+class Manager(unittest.TestCase):
+ def tearDown(self):
+ isc.log.reset()
+
+ def test_init_debug(self):
+ # We try calling it now only, as we don't have any other functions
+ # to check the outcome by it. Once we add the logger class, we may
+ # check more.
+ isc.log.init("root", "DEBUG", 50, None)
+
+ def test_init_defaults(self):
+ # We try calling it now only, as we don't have any other functions
+ # to check the outcome by it. Once we add the logger class, we may
+ # check more.
+ isc.log.init("root")
+
+ def test_init_notfound(self):
+ # This should not throw, because the C++ one doesn't. Should we really
+ # ignore errors like missing file?
+ isc.log.init("root", "INFO", 0, "/no/such/file");
+
+ def test_log_config_update(self):
+ log_spec = json.dumps(isc.config.module_spec_from_file(path_search('logging.spec', bind10_config.PLUGIN_PATHS)).get_full_spec())
+
+ self.assertRaises(TypeError, isc.log.log_config_update)
+ self.assertRaises(TypeError, isc.log.log_config_update, 1)
+ self.assertRaises(TypeError, isc.log.log_config_update, 1, 1)
+ self.assertRaises(TypeError, isc.log.log_config_update, 1, 1, 1)
+
+ self.assertRaises(TypeError, isc.log.log_config_update, 1, log_spec)
+ self.assertRaises(TypeError, isc.log.log_config_update, [], log_spec)
+ self.assertRaises(TypeError, isc.log.log_config_update, "foo", log_spec)
+ self.assertRaises(TypeError, isc.log.log_config_update, "{ '", log_spec)
+
+ # empty should pass
+ isc.log.log_config_update("{}", log_spec)
+
+ # bad spec
+ self.assertRaises(TypeError, isc.log.log_config_update, "{}", json.dumps({"foo": "bar"}))
+
+ # Try a correct one
+ log_conf = json.dumps({"loggers":
+ [{"name": "b10-xfrout", "output_options":
+ [{"output": "/tmp/bind10.log",
+ "destination": "file",
+ "flush": True}]}]})
+ isc.log.log_config_update(log_conf, log_spec)
+
+class Logger(unittest.TestCase):
+ def tearDown(self):
+ isc.log.reset()
-class TestLogging(unittest.TestCase):
-
def setUp(self):
- self.FILE_STREAM_LOG1 = tempfile.NamedTemporaryFile(mode='w',
- prefix="b10",
- delete=True)
- self.FILE_STREAM_LOG2 = tempfile.NamedTemporaryFile(mode='w',
- prefix="b10",
- delete=True)
- self.FILE_STREAM_LOG3 = tempfile.NamedTemporaryFile(mode='w',
- prefix="b10",
- delete=True)
- self.file_stream_logger = NSLogger('File_Stream_Logger',
- self.FILE_STREAM_LOG1.name,
- 'debug', 5, 1024, True)
- self.syslog_logger = NSLogger('SysLogger', '', 'info', 5, 1024, False)
- self.stderr_bak = sys.stderr
- sys.stderr = open(os.devnull, 'w')
-
- def test_logging_init(self):
- self.assertNotEqual(self.file_stream_logger._file_handler, None)
- self.assertNotEqual(self.file_stream_logger._stream_handler, None)
- self.assertEqual(self.file_stream_logger._syslog_handler, None)
-
- self.assertIn(self.file_stream_logger._file_handler, self.file_stream_logger.handlers)
- self.assertIn(self.file_stream_logger._stream_handler, self.file_stream_logger.handlers)
- self.assertNotIn(self.file_stream_logger._syslog_handler, self.file_stream_logger.handlers)
- logLevel = LEVELS.get('debug', logging.NOTSET)
- self.assertEqual(self.file_stream_logger.getEffectiveLevel(), logLevel)
-
- self.assertEqual(self.syslog_logger._file_handler, None)
- self.assertEqual(self.syslog_logger._stream_handler, None)
- self.assertNotEqual(self.syslog_logger._syslog_handler, None)
- self.assertNotIn(self.syslog_logger._file_handler, self.syslog_logger.handlers)
- self.assertNotIn(self.syslog_logger._stream_handler, self.syslog_logger.handlers)
- self.assertIn(self.syslog_logger._syslog_handler, self.syslog_logger.handlers)
-
- logLevel = LEVELS.get('info', logging.NOTSET)
- self.assertEqual(self.syslog_logger.getEffectiveLevel(), logLevel)
-
- def test_add_rotate_handler(self):
- if(self.syslog_logger._file_handler in self.syslog_logger.handlers):
- self.syslog_logger.removeHandler(self.syslog_logger._file_handler)
-
- self.syslog_logger._add_rotate_handler('', 5, 1024)
- self.assertNotIn(self.syslog_logger._file_handler, self.syslog_logger.handlers)
-
- self.syslog_logger._add_rotate_handler(self.FILE_STREAM_LOG1.name, 5, 1024)
- self.assertIn(self.syslog_logger._file_handler, self.syslog_logger.handlers)
-
- # test IOError exception
- self.syslog_logger.removeHandler(self.syslog_logger._file_handler)
- log_file = self.FILE_STREAM_LOG1.name + '/logfile'
- self.syslog_logger._add_rotate_handler(log_file, 5, 1024)
- self.assertNotIn(self.syslog_logger._file_handler, self.syslog_logger.handlers)
-
- def test_add_stream_handler(self):
- if(self.file_stream_logger._stream_handler in self.file_stream_logger.handlers):
- self.file_stream_logger.removeHandler(self.file_stream_logger._stream_handler)
-
- self.file_stream_logger._add_stream_handler()
- self.assertIn(self.file_stream_logger._stream_handler, self.file_stream_logger.handlers)
-
- def test_add_syslog_handler(self):
- if(self.syslog_logger._syslog_handler in self.syslog_logger.handlers):
- self.syslog_logger.removeHandler(self.syslog_logger._syslog_handler)
-
- self.syslog_logger._add_syslog_handler()
- self.assertIn(self.syslog_logger._syslog_handler, self.syslog_logger.handlers)
-
- def test_update_rotate_handler(self):
- self.file_stream_logger._update_rotate_handler(self.FILE_STREAM_LOG2.name, 4, 1024)
- self.assertIn(self.file_stream_logger._file_handler, self.file_stream_logger.handlers)
-
- self.file_stream_logger._update_rotate_handler('', 5, 1024)
- self.assertNotIn(self.file_stream_logger._file_handler, self.file_stream_logger.handlers)
-
- self.file_stream_logger._update_rotate_handler(self.FILE_STREAM_LOG1.name, 4, 1024)
- self.assertIn(self.file_stream_logger._file_handler, self.file_stream_logger.handlers)
-
- def test_get_config(self):
- config_data = {'log_file' : self.FILE_STREAM_LOG1.name,
- 'log_severity' : 'critical',
- 'log_versions' : 4,
- 'log_max_bytes' : 1024}
- self.file_stream_logger._get_config(config_data)
- self.assertEqual(self.file_stream_logger._log_file, self.FILE_STREAM_LOG1.name)
- self.assertEqual(self.file_stream_logger._severity, 'critical')
- self.assertEqual(self.file_stream_logger._versions, 4)
- self.assertEqual(self.file_stream_logger._max_bytes, 1024)
-
-
- def test_update_config(self):
- update_config = {'log_file' : self.FILE_STREAM_LOG1.name,
- 'log_severity' : 'error',
- 'log_versions' : 4,
- 'log_max_bytes' : 1024}
- self.file_stream_logger.update_config(update_config)
- logLevel = LEVELS.get('error', logging.NOTSET)
- self.assertEqual(self.file_stream_logger.getEffectiveLevel(), logLevel)
-
- def test_log_message(self):
- update_config = {'log_file' : self.FILE_STREAM_LOG3.name,
- 'log_severity' : 'critical',
- 'log_versions' : 4,
- 'log_max_bytes' : 1024}
- self.file_stream_logger.update_config(update_config)
- self.file_stream_logger.log_message('debug', 'debug message')
- self.file_stream_logger.log_message('warning', 'warning message')
- self.file_stream_logger.log_message('error', 'error message')
- #test non-exist log level
- self.assertRaises(None, self.file_stream_logger.log_message('not-exist', 'not exist message'))
- #test log_message KeyError exception
- self.assertRaises(None, self.file_stream_logger.log_message('critical', 'critical message', extra=['message', 'asctime']))
- self.assertTrue(os.path.exists(self.FILE_STREAM_LOG3.name))
-
- def tearDown(self):
- self.FILE_STREAM_LOG1.close()
- self.FILE_STREAM_LOG2.close()
- self.FILE_STREAM_LOG3.close()
- sys.stderr.flush();
- sys.stderr = self.stderr_bak
+ isc.log.init("root", "DEBUG", 50)
+ self.sevs = ['INFO', 'WARN', 'ERROR', 'FATAL']
+
+ # Checks defaults of the logger
+ def defaults(self, logger):
+ self.assertEqual(logger.get_effective_severity(), "DEBUG")
+ self.assertEqual(logger.get_effective_debug_level(), 50)
+
+ def test_default_severity(self):
+ logger = isc.log.Logger("child")
+ self.defaults(logger)
+
+ # Try changing the severities little bit
+ def test_severity(self):
+ logger = isc.log.Logger("child")
+ logger.set_severity('DEBUG', 25)
+ self.assertEqual(logger.get_effective_severity(), "DEBUG")
+ self.assertEqual(logger.get_effective_debug_level(), 25)
+ for sev in self.sevs:
+ logger.set_severity(sev)
+ self.assertEqual(logger.get_effective_severity(), sev)
+ self.assertEqual(logger.get_effective_debug_level(), 0)
+ # Return to default
+ logger.set_severity(None)
+ self.defaults(logger)
+
+ def test_enabled(self):
+ logger = isc.log.Logger("child")
+ self.sevs.insert(0, 'DEBUG')
+ methods = {
+ 'DEBUG': logger.is_debug_enabled,
+ 'INFO': logger.is_info_enabled,
+ 'WARN': logger.is_warn_enabled,
+ 'ERROR': logger.is_error_enabled,
+ 'FATAL': logger.is_fatal_enabled
+ }
+ for sev in self.sevs:
+ logger.set_severity(sev)
+ enabled = False
+ for tested in self.sevs:
+ if tested == sev:
+ enabled = True
+ self.assertEqual(methods[tested](), enabled)
+ logger.set_severity('DEBUG', 50)
+ self.assertTrue(logger.is_debug_enabled())
+ self.assertTrue(logger.is_debug_enabled(0))
+ self.assertTrue(logger.is_debug_enabled(50))
+ self.assertFalse(logger.is_debug_enabled(99))
+
+ def test_invalid_params(self):
+ """
+ Tests invalid arguments for logging functions. The output is tested
+ in check_output.sh.
+ """
+ logger = isc.log.Logger("child")
+ methods = [
+ logger.info,
+ logger.warn,
+ logger.error,
+ logger.fatal
+ ]
+ for meth in methods:
+ # Not enough arguments
+ self.assertRaises(TypeError, meth)
+ # Bad type
+ self.assertRaises(TypeError, meth, 1)
+ # Too few arguments
+ self.assertRaises(TypeError, logger.debug, 42)
+ self.assertRaises(TypeError, logger.debug)
+ # Bad type
+ self.assertRaises(TypeError, logger.debug, "42", "hello")
if __name__ == '__main__':
unittest.main()
diff --git a/src/lib/python/isc/net/Makefile.am b/src/lib/python/isc/net/Makefile.am
index bb6057c..1b97614 100644
--- a/src/lib/python/isc/net/Makefile.am
+++ b/src/lib/python/isc/net/Makefile.am
@@ -3,3 +3,8 @@ SUBDIRS = tests
python_PYTHON = __init__.py addr.py parse.py
pythondir = $(pyexecdir)/isc/net
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/net/parse.py b/src/lib/python/isc/net/parse.py
index 86d95aa..30edadc 100644
--- a/src/lib/python/isc/net/parse.py
+++ b/src/lib/python/isc/net/parse.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2010 CZ NIC
+# Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
diff --git a/src/lib/python/isc/net/tests/Makefile.am b/src/lib/python/isc/net/tests/Makefile.am
index 73528d2..3a04f17 100644
--- a/src/lib/python/isc/net/tests/Makefile.am
+++ b/src/lib/python/isc/net/tests/Makefile.am
@@ -2,6 +2,13 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
PYTESTS = addr_test.py parse_test.py
EXTRA_DIST = $(PYTESTS)
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
if ENABLE_PYTHON_COVERAGE
@@ -11,6 +18,7 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
+ $(LIBRARY_PATH_PLACEHOLDER) \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
diff --git a/src/lib/python/isc/net/tests/parse_test.py b/src/lib/python/isc/net/tests/parse_test.py
index 53fca16..ba97da6 100644
--- a/src/lib/python/isc/net/tests/parse_test.py
+++ b/src/lib/python/isc/net/tests/parse_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2010 CZ NIC
+# Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
diff --git a/src/lib/python/isc/notify/Makefile.am b/src/lib/python/isc/notify/Makefile.am
index f4a94fa..4081a17 100644
--- a/src/lib/python/isc/notify/Makefile.am
+++ b/src/lib/python/isc/notify/Makefile.am
@@ -3,3 +3,8 @@ SUBDIRS = . tests
python_PYTHON = __init__.py notify_out.py
pythondir = $(pyexecdir)/isc/notify
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/notify/notify_out.py b/src/lib/python/isc/notify/notify_out.py
index 43dc7af..4b25463 100644
--- a/src/lib/python/isc/notify/notify_out.py
+++ b/src/lib/python/isc/notify/notify_out.py
@@ -21,12 +21,13 @@ import threading
import time
import errno
from isc.datasrc import sqlite3_ds
+from isc.net import addr
import isc
-try:
- from pydnspp import *
-except ImportError as e:
- # C++ loadable module may not be installed;
- sys.stderr.write('[b10-xfrout] failed to import DNS or XFR module: %s\n' % str(e))
+try:
+ from pydnspp import *
+except ImportError as e:
+ # C++ loadable module may not be installed;
+ sys.stderr.write('[b10-xfrout] failed to import DNS or XFR module: %s\n' % str(e))
ZONE_NEW_DATA_READY_CMD = 'zone_new_data_ready'
_MAX_NOTIFY_NUM = 30
@@ -35,7 +36,6 @@ _EVENT_NONE = 0
_EVENT_READ = 1
_EVENT_TIMEOUT = 2
_NOTIFY_TIMEOUT = 1
-_IDLE_SLEEP_TIME = 0.5
# define the rcode for parsing notify reply message
_REPLY_OK = 0
@@ -54,10 +54,6 @@ class ZoneNotifyInfo:
'''This class keeps track of notify-out information for one zone.'''
def __init__(self, zone_name_, class_):
- '''notify_timeout_: absolute time for next notify reply. when the zone
- is preparing for sending notify message, notify_timeout_ is set to now,
- that means the first sending is triggered by the 'Timeout' mechanism.
- '''
self._notify_current = None
self._slave_index = 0
self._sock = None
@@ -66,9 +62,12 @@ class ZoneNotifyInfo:
self.zone_name = zone_name_
self.zone_class = class_
self.notify_msg_id = 0
- self.notify_timeout = 0
- self.notify_try_num = 0 #Notify times sending to one target.
-
+ # Absolute time for next notify reply. When the zone is preparing for
+ # sending notify message, notify_timeout_ is set to now, that means
+ # the first sending is triggered by the 'Timeout' mechanism.
+ self.notify_timeout = None
+ self.notify_try_num = 0 # Notify times sending to one target.
+
def set_next_notify_target(self):
if self._slave_index < (len(self.notify_slaves) - 1):
self._slave_index += 1
@@ -77,8 +76,7 @@ class ZoneNotifyInfo:
self._notify_current = None
def prepare_notify_out(self):
- '''Create the socket and set notify timeout time to now'''
- self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #TODO support IPv6?
+ '''Set notify timeout time to now'''
self.notify_timeout = time.time()
self.notify_try_num = 0
self._slave_index = 0
@@ -89,6 +87,12 @@ class ZoneNotifyInfo:
if self._sock:
self._sock.close()
self._sock = None
+ self.notify_timeout = None
+
+ def create_socket(self, dest_addr):
+ self._sock = socket.socket(addr.IPAddr(dest_addr).family,
+ socket.SOCK_DGRAM)
+ return self._sock
def get_socket(self):
return self._sock
@@ -98,9 +102,9 @@ class ZoneNotifyInfo:
class NotifyOut:
'''This class is used to handle notify logic for all zones(sending
- notify message to its slaves). notify service can be started by
+ notify message to its slaves). notify service can be started by
calling dispatcher(), and it can be stoped by calling shutdown()
- in another thread. '''
+ in another thread. '''
def __init__(self, datasrc_file, log=None, verbose=True):
self._notify_infos = {} # key is (zone_name, zone_class)
self._waiting_zones = []
@@ -114,12 +118,15 @@ class NotifyOut:
self._lock = threading.Lock()
self._db_file = datasrc_file
self._init_notify_out(datasrc_file)
+ # Use nonblock event to eliminate busy loop
+ # If there are no notifying zones, clear the event bit and wait.
+ self._nonblock_event = threading.Event()
def _init_notify_out(self, datasrc_file):
'''Get all the zones name and its notify target's address
- TODO, currently the zones are got by going through the zone
- table in database. There should be a better way to get them
- and also the setting 'also_notify', and there should be one
+ TODO, currently the zones are got by going through the zone
+ table in database. There should be a better way to get them
+ and also the setting 'also_notify', and there should be one
mechanism to cover the changed datasrc.'''
self._db_file = datasrc_file
for zone_name, zone_class in sqlite3_ds.get_zones_info(datasrc_file):
@@ -130,7 +137,7 @@ class NotifyOut:
self._notify_infos[zone_id].notify_slaves.append((item, 53))
def send_notify(self, zone_name, zone_class='IN'):
- '''Send notify to one zone's slaves, this function is
+ '''Send notify to one zone's slaves, this function is
the only interface for class NotifyOut which can be called
by other object.
Internally, the function only set the zone's notify-reply
@@ -142,14 +149,20 @@ class NotifyOut:
if zone_id not in self._notify_infos:
return
+ # Has no slave servers, skip it.
+ if (len(self._notify_infos[zone_id].notify_slaves) <= 0):
+ return
+
with self._lock:
if (self.notify_num >= _MAX_NOTIFY_NUM) or (zone_id in self._notifying_zones):
if zone_id not in self._waiting_zones:
self._waiting_zones.append(zone_id)
else:
self._notify_infos[zone_id].prepare_notify_out()
- self.notify_num += 1
+ self.notify_num += 1
self._notifying_zones.append(zone_id)
+ if not self._nonblock_event.isSet():
+ self._nonblock_event.set()
def _dispatcher(self, started_event):
started_event.set() # Let the master know we are alive already
@@ -168,8 +181,8 @@ class NotifyOut:
If one zone get the notify reply before timeout, call the
handle to process the reply. If one zone can't get the notify
- before timeout, call the handler to resend notify or notify
- next slave.
+ before timeout, call the handler to resend notify or notify
+ next slave.
The thread can be stopped by calling shutdown().
@@ -205,6 +218,9 @@ class NotifyOut:
# Ask it to stop
self._serving = False
+ if not self._nonblock_event.isSet():
+ # set self._nonblock_event to stop waiting for new notifying zones.
+ self._nonblock_event.set()
self._write_sock.send(SOCK_DATA) # make self._read_sock be readable.
# Wait for it
@@ -223,7 +239,7 @@ class NotifyOut:
then use the name in NS record rdata part to get the a/aaaa records
in the same zone. the targets listed in a/aaaa record rdata are treated
as the notify slaves.
- Note: this is the simplest way to get the address of slaves,
+ Note: this is the simplest way to get the address of slaves,
but not correct, it can't handle the delegation slaves, or the CNAME
and DNAME logic.
TODO. the function should be provided by one library.'''
@@ -231,8 +247,8 @@ class NotifyOut:
soa_rrset = sqlite3_ds.get_zone_rrset(zone_name, zone_name, 'SOA', self._db_file)
ns_rr_name = []
for ns in ns_rrset:
- ns_rr_name.append(self._get_rdata_data(ns))
-
+ ns_rr_name.append(self._get_rdata_data(ns))
+
if len(soa_rrset) > 0:
sname = (soa_rrset[0][sqlite3_ds.RR_RDATA_INDEX].split(' '))[0].strip() #TODO, bad hardcode to get rdata part
if sname in ns_rr_name:
@@ -266,15 +282,22 @@ class NotifyOut:
sock = self._notify_infos[info].get_socket()
if sock:
valid_socks.append(sock)
+
+ # If a non null timeout is specified notify has been scheduled
+ # (in which case socket is still None) or sent (with a valid
+ # socket). In either case we need add the zone to notifying_zones
+ # so that we can invoke the appropriate event for the zone after
+ # select.
+ tmp_timeout = self._notify_infos[info].notify_timeout
+ if tmp_timeout is not None:
notifying_zones[info] = self._notify_infos[info]
- tmp_timeout = self._notify_infos[info].notify_timeout
if min_timeout is not None:
if tmp_timeout < min_timeout:
min_timeout = tmp_timeout
else:
min_timeout = tmp_timeout
- block_timeout = _IDLE_SLEEP_TIME
+ block_timeout = None
if min_timeout is not None:
block_timeout = min_timeout - time.time()
if block_timeout < 0:
@@ -297,10 +320,18 @@ class NotifyOut:
# This is None only during some tests
if self._read_sock is not None:
valid_socks.append(self._read_sock)
+
+ # Currently, there is no notifying zones, waiting for zones to send notify
+ if block_timeout is None:
+ self._nonblock_event.clear()
+ self._nonblock_event.wait()
+ # has new notifying zone, check immediately
+ block_timeout = 0
+
try:
r_fds, w, e = select.select(valid_socks, [], [], block_timeout)
except select.error as err:
- if err.args[0] != EINTR:
+ if err.args[0] != errno.EINTR:
return {}, {}
if self._read_sock in r_fds: # user has called shutdown()
@@ -323,10 +354,10 @@ class NotifyOut:
return replied_zones, not_replied_zones
def _zone_notify_handler(self, zone_notify_info, event_type):
- '''Notify handler for one zone. The first notify message is
- always triggered by the event "_EVENT_TIMEOUT" since when
- one zone prepares to notify its slaves, its notify_timeout
- is set to now, which is used to trigger sending notify
+ '''Notify handler for one zone. The first notify message is
+ always triggered by the event "_EVENT_TIMEOUT" since when
+ one zone prepares to notify its slaves, its notify_timeout
+ is set to now, which is used to trigger sending notify
message when dispatcher() scanning zones. '''
tgt = zone_notify_info.get_current_notify_target()
if event_type == _EVENT_READ:
@@ -345,13 +376,13 @@ class NotifyOut:
self._log_msg('info', 'notify to %s: retried exceeded' % addr_to_str(tgt))
self._notify_next_target(zone_notify_info)
else:
- retry_timeout = _NOTIFY_TIMEOUT * pow(2, zone_notify_info.notify_try_num)
# set exponential backoff according rfc1996 section 3.6
+ retry_timeout = _NOTIFY_TIMEOUT * pow(2, zone_notify_info.notify_try_num)
zone_notify_info.notify_timeout = time.time() + retry_timeout
self._send_notify_message_udp(zone_notify_info, tgt)
def _notify_next_target(self, zone_notify_info):
- '''Notify next address for the same zone. If all the targets
+ '''Notify next address for the same zone. If all the targets
has been notified, notify the first zone in waiting list. '''
zone_notify_info.notify_try_num = 0
zone_notify_info.set_next_notify_target()
@@ -359,35 +390,38 @@ class NotifyOut:
if not tgt:
zone_notify_info.finish_notify_out()
with self._lock:
- self.notify_num -= 1
- self._notifying_zones.remove((zone_notify_info.zone_name,
- zone_notify_info.zone_class))
+ self.notify_num -= 1
+ self._notifying_zones.remove((zone_notify_info.zone_name,
+ zone_notify_info.zone_class))
# trigger notify out for waiting zones
if len(self._waiting_zones) > 0:
- zone_id = self._waiting_zones.pop(0)
+ zone_id = self._waiting_zones.pop(0)
self._notify_infos[zone_id].prepare_notify_out()
- self.notify_num += 1
+ self.notify_num += 1
self._notifying_zones.append(zone_id)
+ if not self._nonblock_event.isSet():
+ self._nonblock_event.set()
def _send_notify_message_udp(self, zone_notify_info, addrinfo):
- msg, qid = self._create_notify_message(zone_notify_info.zone_name,
+ msg, qid = self._create_notify_message(zone_notify_info.zone_name,
zone_notify_info.zone_class)
render = MessageRenderer()
- render.set_length_limit(512)
+ render.set_length_limit(512)
msg.to_wire(render)
zone_notify_info.notify_msg_id = qid
- sock = zone_notify_info.get_socket()
try:
+ sock = zone_notify_info.create_socket(addrinfo[0])
sock.sendto(render.get_data(), 0, addrinfo)
self._log_msg('info', 'sending notify to %s' % addr_to_str(addrinfo))
- except socket.error as err:
- self._log_msg('error', 'send notify to %s failed: %s' % (addr_to_str(addrinfo), str(err)))
+ except (socket.error, addr.InvalidAddress) as err:
+ self._log_msg('error', 'send notify to %s failed: %s' %
+ (addr_to_str(addrinfo), str(err)))
return False
return True
def _create_rrset_from_db_record(self, record, zone_class):
- '''Create one rrset from one record of datasource, if the schema of record is changed,
+ '''Create one rrset from one record of datasource, if the schema of record is changed,
This function should be updated first. TODO, the function is copied from xfrout, there
should be library for creating one rrset. '''
rrtype_ = RRType(record[sqlite3_ds.RR_TYPE_INDEX])
@@ -407,7 +441,7 @@ class NotifyOut:
question = Question(Name(zone_name), RRClass(zone_class), RRType('SOA'))
msg.add_question(question)
# Add soa record to answer section
- soa_record = sqlite3_ds.get_zone_rrset(zone_name, zone_name, 'SOA', self._db_file)
+ soa_record = sqlite3_ds.get_zone_rrset(zone_name, zone_name, 'SOA', self._db_file)
rrset_soa = self._create_rrset_from_db_record(soa_record[0], zone_class)
msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
return msg, qid
@@ -425,10 +459,10 @@ class NotifyOut:
self._log_msg('error', errstr + 'bad flags')
return _BAD_QR
- if msg.get_qid() != zone_notify_info.notify_msg_id:
+ if msg.get_qid() != zone_notify_info.notify_msg_id:
self._log_msg('error', errstr + 'bad query ID')
return _BAD_QUERY_ID
-
+
question = msg.get_question()[0]
if question.get_name() != Name(zone_notify_info.zone_name):
self._log_msg('error', errstr + 'bad query name')
@@ -438,7 +472,7 @@ class NotifyOut:
self._log_msg('error', errstr + 'bad opcode')
return _BAD_OPCODE
except Exception as err:
- # We don't care what exception, just report it?
+ # We don't care what exception, just report it?
self._log_msg('error', errstr + str(err))
return _BAD_REPLY_PACKET
diff --git a/src/lib/python/isc/notify/tests/Makefile.am b/src/lib/python/isc/notify/tests/Makefile.am
index c5f0165..1427d93 100644
--- a/src/lib/python/isc/notify/tests/Makefile.am
+++ b/src/lib/python/isc/notify/tests/Makefile.am
@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
# required by loadable python modules.
LIBRARY_PATH_PLACEHOLDER =
if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
endif
# test using command-line arguments, so use check-local target instead of TESTS
diff --git a/src/lib/python/isc/notify/tests/notify_out_test.py b/src/lib/python/isc/notify/tests/notify_out_test.py
index c4c149c..0eb77a3 100644
--- a/src/lib/python/isc/notify/tests/notify_out_test.py
+++ b/src/lib/python/isc/notify/tests/notify_out_test.py
@@ -24,9 +24,7 @@ from isc.notify import notify_out, SOCK_DATA
# our fake socket, where we can read and insert messages
class MockSocket():
- def __init__(self, family, type):
- self.family = family
- self.type = type
+ def __init__(self):
self._local_sock, self._remote_sock = socket.socketpair()
def connect(self, to):
@@ -51,12 +49,16 @@ class MockSocket():
return self._remote_sock
# We subclass the ZoneNotifyInfo class we're testing here, only
-# to override the prepare_notify_out() method.
+# to override the create_socket() method.
class MockZoneNotifyInfo(notify_out.ZoneNotifyInfo):
- def prepare_notify_out(self):
- super().prepare_notify_out();
+ def create_socket(self, addrinfo):
+ super().create_socket(addrinfo)
+ # before replacing the underlying socket, remember the address family
+ # of the original socket so that tests can check that.
+ self.sock_family = self._sock.family
self._sock.close()
- self._sock = MockSocket(socket.AF_INET, socket.SOCK_DGRAM)
+ self._sock = MockSocket()
+ return self._sock
class TestZoneNotifyInfo(unittest.TestCase):
def setUp(self):
@@ -64,11 +66,12 @@ class TestZoneNotifyInfo(unittest.TestCase):
def test_prepare_finish_notify_out(self):
self.info.prepare_notify_out()
- self.assertNotEqual(self.info._sock, None)
+ self.assertNotEqual(self.info.notify_timeout, None)
self.assertIsNone(self.info._notify_current)
self.info.finish_notify_out()
self.assertEqual(self.info._sock, None)
+ self.assertEqual(self.info.notify_timeout, None)
def test_set_next_notify_target(self):
self.info.notify_slaves.append(('127.0.0.1', 53))
@@ -99,36 +102,56 @@ class TestNotifyOut(unittest.TestCase):
self._notify._notify_infos[('example.org.', 'IN')] = MockZoneNotifyInfo('example.org.', 'IN')
self._notify._notify_infos[('example.org.', 'CH')] = MockZoneNotifyInfo('example.org.', 'CH')
- info = self._notify._notify_infos[('example.net.', 'IN')]
- info.notify_slaves.append(('127.0.0.1', 53))
- info.notify_slaves.append(('1.1.1.1', 5353))
+ net_info = self._notify._notify_infos[('example.net.', 'IN')]
+ net_info.notify_slaves.append(('127.0.0.1', 53))
+ net_info.notify_slaves.append(('1.1.1.1', 5353))
+ com_info = self._notify._notify_infos[('example.com.', 'IN')]
+ com_info.notify_slaves.append(('1.1.1.1', 5353))
+ com_ch_info = self._notify._notify_infos[('example.com.', 'CH')]
+ com_ch_info.notify_slaves.append(('1.1.1.1', 5353))
def tearDown(self):
self._db_file.close()
os.unlink(self._db_file.name)
def test_send_notify(self):
+ notify_out._MAX_NOTIFY_NUM = 2
+
+ self._notify._nonblock_event.clear()
self._notify.send_notify('example.net')
+ self.assertTrue(self._notify._nonblock_event.isSet())
self.assertEqual(self._notify.notify_num, 1)
- self.assertEqual(self._notify._notifying_zones[0], ('example.net.','IN'))
+ self.assertEqual(self._notify._notifying_zones[0], ('example.net.', 'IN'))
self._notify.send_notify('example.com')
self.assertEqual(self._notify.notify_num, 2)
- self.assertEqual(self._notify._notifying_zones[1], ('example.com.','IN'))
+ self.assertEqual(self._notify._notifying_zones[1], ('example.com.', 'IN'))
- notify_out._MAX_NOTIFY_NUM = 3
+ # notify_num is equal to MAX_NOTIFY_NUM, append it to waiting_zones list.
+ self._notify._nonblock_event.clear()
self._notify.send_notify('example.com', 'CH')
- self.assertEqual(self._notify.notify_num, 3)
- self.assertEqual(self._notify._notifying_zones[2], ('example.com.','CH'))
-
- self._notify.send_notify('example.org.')
- self.assertEqual(self._notify._waiting_zones[0], ('example.org.', 'IN'))
- self._notify.send_notify('example.org.')
+ # add waiting zones won't set nonblock_event.
+ self.assertFalse(self._notify._nonblock_event.isSet())
+ self.assertEqual(self._notify.notify_num, 2)
self.assertEqual(1, len(self._notify._waiting_zones))
+ # zone_id is already in notifying_zones list, append it to waiting_zones list.
+ self._notify.send_notify('example.net')
+ self.assertEqual(2, len(self._notify._waiting_zones))
+ self.assertEqual(self._notify._waiting_zones[1], ('example.net.', 'IN'))
+
+ # zone_id is already in waiting_zones list, skip it.
+ self._notify.send_notify('example.net')
+ self.assertEqual(2, len(self._notify._waiting_zones))
+
+ # has no slave masters, skip it.
self._notify.send_notify('example.org.', 'CH')
+ self.assertEqual(self._notify.notify_num, 2)
+ self.assertEqual(2, len(self._notify._waiting_zones))
+
+ self._notify.send_notify('example.org.')
+ self.assertEqual(self._notify.notify_num, 2)
self.assertEqual(2, len(self._notify._waiting_zones))
- self.assertEqual(self._notify._waiting_zones[1], ('example.org.', 'CH'))
def test_wait_for_notify_reply(self):
self._notify.send_notify('example.net.')
@@ -140,6 +163,11 @@ class TestNotifyOut(unittest.TestCase):
self.assertEqual(len(replied_zones), 0)
self.assertEqual(len(timeout_zones), 2)
+ # Trigger timeout events to "send" notifies via a mock socket
+ for zone in timeout_zones:
+ self._notify._zone_notify_handler(timeout_zones[zone],
+ notify_out._EVENT_TIMEOUT)
+
# Now make one socket be readable
self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() + 10
self._notify._notify_infos[('example.com.', 'IN')].notify_timeout = time.time() + 10
@@ -171,6 +199,7 @@ class TestNotifyOut(unittest.TestCase):
self._notify.send_notify('example.net.')
self._notify.send_notify('example.com.')
notify_out._MAX_NOTIFY_NUM = 2
+ # zone example.org. has no slave servers.
self._notify.send_notify('example.org.')
self._notify.send_notify('example.com.', 'CH')
@@ -179,17 +208,19 @@ class TestNotifyOut(unittest.TestCase):
self.assertEqual(0, info.notify_try_num)
self.assertEqual(info.get_current_notify_target(), ('1.1.1.1', 5353))
self.assertEqual(2, self._notify.notify_num)
+ self.assertEqual(1, len(self._notify._waiting_zones))
self._notify._notify_next_target(info)
self.assertEqual(0, info.notify_try_num)
self.assertIsNone(info.get_current_notify_target())
self.assertEqual(2, self._notify.notify_num)
- self.assertEqual(1, len(self._notify._waiting_zones))
+ self.assertEqual(0, len(self._notify._waiting_zones))
example_com_info = self._notify._notify_infos[('example.com.', 'IN')]
self._notify._notify_next_target(example_com_info)
- self.assertEqual(2, self._notify.notify_num)
- self.assertEqual(2, len(self._notify._notifying_zones))
+ self.assertEqual(1, self._notify.notify_num)
+ self.assertEqual(1, len(self._notify._notifying_zones))
+ self.assertEqual(0, len(self._notify._waiting_zones))
def test_handle_notify_reply(self):
self.assertEqual(notify_out._BAD_REPLY_PACKET, self._notify._handle_notify_reply(None, b'badmsg'))
@@ -216,11 +247,31 @@ class TestNotifyOut(unittest.TestCase):
data = b'\x2f\x18\x10\x10\x00\x01\x00\x00\x00\x00\x00\x00\x07example\03com\x00\x00\x06\x00\x01'
self.assertEqual(notify_out._BAD_QR, self._notify._handle_notify_reply(example_com_info, data))
- def test_send_notify_message_udp(self):
+ def test_send_notify_message_udp_ipv4(self):
example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
example_com_info.prepare_notify_out()
- ret = self._notify._send_notify_message_udp(example_com_info, ('1.1.1.1', 53))
+ ret = self._notify._send_notify_message_udp(example_com_info,
+ ('192.0.2.1', 53))
self.assertTrue(ret)
+ self.assertEqual(socket.AF_INET, example_com_info.sock_family)
+
+ def test_send_notify_message_udp_ipv6(self):
+ example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
+ ret = self._notify._send_notify_message_udp(example_com_info,
+ ('2001:db8::53', 53))
+ self.assertTrue(ret)
+ self.assertEqual(socket.AF_INET6, example_com_info.sock_family)
+
+ def test_send_notify_message_with_bogus_address(self):
+ example_com_info = self._notify._notify_infos[('example.net.', 'IN')]
+
+ # As long as the underlying data source validates RDATA this shouldn't
+ # happen, but right now it's not actually the case. Even if the
+ # data source does its job, it's prudent to confirm the behavior for
+ # an unexpected case.
+ ret = self._notify._send_notify_message_udp(example_com_info,
+ ('invalid', 53))
+ self.assertFalse(ret)
def test_zone_notify_handler(self):
old_send_msg = self._notify._send_notify_message_udp
@@ -302,7 +353,7 @@ class TestNotifyOut(unittest.TestCase):
def test_prepare_select_info(self):
timeout, valid_fds, notifying_zones = self._notify._prepare_select_info()
- self.assertEqual(notify_out._IDLE_SLEEP_TIME, timeout)
+ self.assertEqual(None, timeout)
self.assertListEqual([], valid_fds)
self._notify._notify_infos[('example.net.', 'IN')]._sock = 1
@@ -326,7 +377,32 @@ class TestNotifyOut(unittest.TestCase):
def test_shutdown(self):
thread = self._notify.dispatcher()
self.assertTrue(thread.is_alive())
+ # nonblock_event won't be setted since there are no notifying zones.
+ self.assertFalse(self._notify._nonblock_event.isSet())
+
+ # set nonblock_event manually
+ self._notify._nonblock_event.set()
+ # nonblock_event will be cleared soon since there are no notifying zones.
+ while (self._notify._nonblock_event.isSet()):
+ pass
+
+ # send notify
+ example_net_info = self._notify._notify_infos[('example.net.', 'IN')]
+ example_net_info.notify_slaves = [('127.0.0.1', 53)]
+ example_net_info.create_socket('127.0.0.1')
+ self._notify.send_notify('example.net')
+ self.assertTrue(self._notify._nonblock_event.isSet())
+ # set notify_try_num to _MAX_NOTIFY_TRY_NUM, zone 'example.net' will be removed
+ # from notifying zones soon and nonblock_event will be cleared since there is no
+ # notifying zone left.
+ example_net_info.notify_try_num = notify_out._MAX_NOTIFY_TRY_NUM
+ while (self._notify._nonblock_event.isSet()):
+ pass
+
+ self.assertFalse(self._notify._nonblock_event.isSet())
self._notify.shutdown()
+ # nonblock_event should have been setted to stop waiting.
+ self.assertTrue(self._notify._nonblock_event.isSet())
self.assertFalse(thread.is_alive())
if __name__== "__main__":
diff --git a/src/lib/python/isc/testutils/Makefile.am b/src/lib/python/isc/testutils/Makefile.am
new file mode 100644
index 0000000..0b08257
--- /dev/null
+++ b/src/lib/python/isc/testutils/Makefile.am
@@ -0,0 +1,6 @@
+EXTRA_DIST = __init__.py parse_args.py tsigctx_mock.py
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/testutils/README b/src/lib/python/isc/testutils/README
new file mode 100644
index 0000000..c0389a8
--- /dev/null
+++ b/src/lib/python/isc/testutils/README
@@ -0,0 +1,3 @@
+This contains some shared test code for other modules and python processes.
+That's why it doesn't have its own test subdirectory and why it isn't
+installed.
diff --git a/src/lib/python/isc/testutils/__init__.py b/src/lib/python/isc/testutils/__init__.py
new file mode 100644
index 0000000..afcccf4
--- /dev/null
+++ b/src/lib/python/isc/testutils/__init__.py
@@ -0,0 +1,17 @@
+# Copyright (C) 2011 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.
+
+# Nothing here, really, it's just to tell python this directory is in
+# module hierarchy
diff --git a/src/lib/python/isc/testutils/parse_args.py b/src/lib/python/isc/testutils/parse_args.py
new file mode 100644
index 0000000..5d79137
--- /dev/null
+++ b/src/lib/python/isc/testutils/parse_args.py
@@ -0,0 +1,30 @@
+# Copyright (C) 2011 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.
+
+from optparse import OptionParser
+
+class OptsError(Exception):
+ """To know when OptionParser would exit"""
+ pass
+
+class TestOptParser(OptionParser):
+ """
+ We define our own option parser to push into the parsing routine.
+ This one does not exit the whole application on error, it just raises
+ exception. It doesn't change anything else. The application uses the
+ stock one.
+ """
+ def error(self, message):
+ raise OptsError(message)
diff --git a/src/lib/python/isc/testutils/tsigctx_mock.py b/src/lib/python/isc/testutils/tsigctx_mock.py
new file mode 100644
index 0000000..a9af9b9
--- /dev/null
+++ b/src/lib/python/isc/testutils/tsigctx_mock.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2011 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.
+
+from pydnspp import *
+
+class MockTSIGContext(TSIGContext):
+ """Tthis is a mock of TSIGContext class for testing.
+ Via its "error" attribute, you can fake the result of verify(), thereby
+ you can test many of TSIG related tests without requiring actual crypto
+ setups. "error" should be either a TSIGError type value or a callable
+ object (typically a function). In the latter case, the callable object
+ will take the context as a parameter, and is expected to return a
+ TSIGError object.
+ """
+
+ def __init__(self, tsig_key):
+ super().__init__(tsig_key)
+ self.error = None
+ self.verify_called = 0 # number of verify() called
+
+ def sign(self, qid, data):
+ """Transparently delegate the processing to the super class.
+ It doesn't matter much anyway because normal applications that would
+ be implemented in Python normally won't call TSIGContext.sign()
+ directly.
+ """
+ return super().sign(qid, data)
+
+ def verify(self, tsig_record, data):
+ self.verify_called += 1
+ # call real "verify" so that we can notice any misue (which would
+ # result in exception.
+ super().verify(tsig_record, data)
+ return self.get_error()
+
+ def get_error(self):
+ if self.error is None:
+ return super().get_error()
+ if hasattr(self.error, '__call__'):
+ return self.error(self)
+ return self.error
diff --git a/src/lib/python/isc/util/Makefile.am b/src/lib/python/isc/util/Makefile.am
index 7ab8048..140e221 100644
--- a/src/lib/python/isc/util/Makefile.am
+++ b/src/lib/python/isc/util/Makefile.am
@@ -1,5 +1,10 @@
SUBDIRS = . tests
-python_PYTHON = __init__.py process.py socketserver_mixin.py
+python_PYTHON = __init__.py process.py socketserver_mixin.py file.py
pythondir = $(pyexecdir)/isc/util
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/util/file.py b/src/lib/python/isc/util/file.py
new file mode 100644
index 0000000..faef9a8
--- /dev/null
+++ b/src/lib/python/isc/util/file.py
@@ -0,0 +1,29 @@
+# Copyright (C) 2011 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.
+
+"""Various functions for working with files and directories."""
+
+from os.path import exists, join
+
+def path_search(filename, paths):
+ """
+ Searches list of paths to find filename in one of them. The found one will
+ be returned or IOError will be returned if it isn't found.
+ """
+ for p in paths:
+ f = join(p, filename)
+ if exists(f):
+ return f
+ raise IOError("'" + filename + "' not found in " + str(paths))
diff --git a/src/lib/python/isc/util/process.py b/src/lib/python/isc/util/process.py
index 25775af..84a2259 100644
--- a/src/lib/python/isc/util/process.py
+++ b/src/lib/python/isc/util/process.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2010 CZ NIC
+# Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
diff --git a/src/lib/python/isc/util/tests/Makefile.am b/src/lib/python/isc/util/tests/Makefile.am
index f32fda0..c3d35c2 100644
--- a/src/lib/python/isc/util/tests/Makefile.am
+++ b/src/lib/python/isc/util/tests/Makefile.am
@@ -1,7 +1,14 @@
PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-PYTESTS = process_test.py socketserver_mixin_test.py
+PYTESTS = process_test.py socketserver_mixin_test.py file_test.py
EXTRA_DIST = $(PYTESTS)
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
if ENABLE_PYTHON_COVERAGE
@@ -11,6 +18,7 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
+ $(LIBRARY_PATH_PLACEHOLDER) \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
diff --git a/src/lib/python/isc/util/tests/file_test.py b/src/lib/python/isc/util/tests/file_test.py
new file mode 100644
index 0000000..fb765d7
--- /dev/null
+++ b/src/lib/python/isc/util/tests/file_test.py
@@ -0,0 +1,32 @@
+# Copyright (C) 2011 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.
+
+import isc.util.file
+import unittest
+
+class FileTest(unittest.TestCase):
+ def test_search_path_find(self):
+ """Test it returns the first occurence of the file"""
+ self.assertEqual('./Makefile',
+ isc.util.file.path_search('Makefile',
+ ['/no/such/directory/', '.',
+ '../tests/']))
+
+ def test_search_path_notfound(self):
+ """Test it throws an exception when the file can't be found"""
+ self.assertRaises(IOError, isc.util.file.path_search, 'no file', ['/no/such/directory'])
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/src/lib/python/isc/util/tests/process_test.py b/src/lib/python/isc/util/tests/process_test.py
index ffe8371..5005aa0 100644
--- a/src/lib/python/isc/util/tests/process_test.py
+++ b/src/lib/python/isc/util/tests/process_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2010 CZ NIC
+# Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
diff --git a/src/lib/rbmsgq/lib/cc.rb b/src/lib/rbmsgq/lib/cc.rb
deleted file mode 100644
index b16f1ac..0000000
--- a/src/lib/rbmsgq/lib/cc.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-# Copyright (C) 2009 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.
-
-unless respond_to?('relative_feature') # nodoc
- def require_relative(relative_feature)
- c = caller.first
- fail "Can't parse #{c}" unless c.rindex(/:\d+(:in `.*')?$/)
- file = $`
- if /\A\((.*)\)/ =~ file # eval, etc.
- raise LoadError, "require_relative is called in #{$1}"
- end
- absolute = File.expand_path(relative_feature, File.dirname(file))
- require absolute
- end
-end
-
-class CC
- def self.set_utf8(str) #nodoc
- if str.respond_to?('force_encoding')
- str.force_encoding(Encoding::UTF_8)
- end
- end
-
- def self.set_binary(str) #nodoc
- if str.respond_to?('force_encoding')
- str.force_encoding(Encoding::BINARY)
- end
- end
-end
-
-require_relative 'cc/message'
-require_relative 'cc/session'
-
-if $0 == __FILE__
- cc = CC::Session.new
-
- puts "Our local name: #{cc.lname}"
-
- cc.group_subscribe("test")
-
- counter = 0
-
- while counter < 10000 do
- cc.group_sendmsg({ :counter => counter }, "test", "foo")
- routing, data = cc.group_recvmsg(false)
- counter += 1
- end
-end
diff --git a/src/lib/rbmsgq/lib/cc/message.rb b/src/lib/rbmsgq/lib/cc/message.rb
deleted file mode 100644
index 254caf0..0000000
--- a/src/lib/rbmsgq/lib/cc/message.rb
+++ /dev/null
@@ -1,324 +0,0 @@
-# Copyright (C) 2009 Internet Systems Consortium.
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
-# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
-# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
-# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-class CC
- class DecodeError < Exception ; end
-end
-
-class CC
-class Message
- PROTOCOL_VERSION = 0x536b616e
-
- ITEM_BLOB = 0x01
- ITEM_HASH = 0x02
- ITEM_LIST = 0x03
- ITEM_NULL = 0x04
- ITEM_BOOL = 0x05
- ITEM_INT = 0x06
- ITEM_UTF8 = 0x08
- ITEM_MASK = 0x0f
-
- ITEM_LENGTH_32 = 0x00
- ITEM_LENGTH_16 = 0x10
- ITEM_LENGTH_8 = 0x20
- ITEM_LENGTH_MASK = 0x30
-
- def initialize(msg = nil)
- @data = [PROTOCOL_VERSION].pack("N")
- if msg.is_a?(Hash)
- @data += CC::Message::encode_hash(msg)
- elsif msg.is_a?(String)
- @data = msg
- else
- raise ArgumentError, "initializer is not a Hash or String"
- end
- end
-
- def to_wire
- CC::set_binary(@data)
- @data
- end
-
- #
- # Encode a message. The item passed in should be a hash, and can contain
- # any number of items of any type afterwards. All keys in the hash must
- # be of type String or Symbol, and the values may be of any type. If
- # the value is a Hash or Array, it will be encoded as a message type
- # HASH or LIST. If it is nil, it will be encoded as NULL, and if it is
- # any other type, its to_s method will be called on it and it will be
- # encoded as a UTF8 item.
- #
- def self.to_wire(msg)
- encoded = [PROTOCOL_VERSION].pack("N")
- encoded += encode_hash(msg)
- encoded.force_encoding('binary')
- encoded
- end
-
- #
- # Decode a wire format message.
- #
- def self.from_wire(msg)
- if msg.length < 4
- raise CC::DecodeError, "Data is too short to decode"
- end
- msg.force_encoding('binary')
- version, msg = msg.unpack("N a*")
- unless version == PROTOCOL_VERSION
- raise CC::DecodeError, "Incorrect protocol version"
- end
- decode_hash(msg)
- end
-
- private
- # encode a simple string with a length prefix
- def self.encode_tag(tag)
- tag = tag.to_s
- [tag.length, tag].pack("C/a*")
- end
-
- def self.encode_length_and_type(data, type)
- if data.nil?
- [ITEM_NULL].pack("C")
- else
- len = data.length
- if len < 0x00000100
- [type | ITEM_LENGTH_8, len, data].pack("C C/a*")
- elsif len < 0x00010000
- [type | ITEM_LENGTH_16, len, data].pack("C n/a*")
- else
- [type | ITEM_LENGTH_32, len, data].pack("C N/a*")
- end
- end
- end
-
- # pack a string, including its type and length.
- def self.pack_utf8(str)
- encode_length_and_type(str.to_s.encode('binary'), ITEM_UTF8)
- end
-
- def self.pack_bool(bool)
- encode_length_and_type(encode_bool(bool), ITEM_BOOL)
- end
-
- def self.pack_int(int)
- encode_length_and_type(encode_int(int), ITEM_INT)
- end
-
- def self.pack_blob(str)
- encode_length_and_type(str.to_s, ITEM_BLOB)
- end
-
- def self.pack_array(arr)
- encode_length_and_type(encode_array(arr), ITEM_LIST)
- end
-
- def self.pack_hash(hash)
- encode_length_and_type(encode_hash(hash), ITEM_HASH)
- end
-
- def self.encode_data(data)
- str.to_s
- end
-
- def self.encode_utf8(str)
- str.to_s.encode('binary')
- end
-
- def self.pack_nil
- encode_length_and_type(nil, ITEM_NULL)
- end
-
- def self.encode_item(item)
- case item
- when nil
- ret = pack_nil
- when Hash
- ret = pack_hash(item)
- when Array
- ret = pack_array(item)
- when String
- if item.encoding == 'utf-8'
- ret = pack_utf8(item)
- else
- ret = pack_blob(item)
- end
- when FalseClass
- ret = pack_bool(item)
- when TrueClass
- ret = pack_bool(item)
- when Integer
- ret = pack_int(item)
- else
- ret = pack_blob(item.to_s)
- end
-
- ret
- end
-
- def self.encode_hash(msg)
- unless msg.is_a?Hash
- raise ArgumentError, "Should be a hash"
- end
- buffer = ""
- msg.each do |key, value|
- buffer += encode_tag(key)
- buffer += encode_item(value)
- end
- buffer
- end
-
- def self.encode_bool(msg)
- unless msg.class == FalseClass or msg.class == TrueClass
- raise ArgumentError, "Should be true or false"
- end
- if msg
- [0x01].pack("C")
- else
- [0x00].pack("C")
- end
- end
-
- def self.encode_int(int)
- int.to_s.encode('binary')
- end
-
- def self.encode_array(msg)
- unless msg.is_a?Array
- raise ArgumentError, "Should be an array"
- end
- buffer = ""
- msg.each do |value|
- buffer += encode_item(value)
- end
- buffer
- end
-
- def self.decode_tag(str)
- if str.length < 1
- raise CC::DecodeError, "Data underrun while decoding"
- end
- length = str.unpack("C")[0]
- if str.length - 1 < length
- raise CC::DecodeError, "Data underrun while decoding"
- end
- tag, remainder = str.unpack("x a#{length} a*")
- [ tag.encode('utf-8'), remainder ]
- end
-
- def self.decode_item(msg)
- if msg.length < 1
- raise CC::DecodeError, "Data underrun while decoding"
- end
- type_and_length_format = msg.unpack("C")[0]
- type = type_and_length_format & ITEM_MASK
- length_format = type_and_length_format & ITEM_LENGTH_MASK
-
- if type == ITEM_NULL
- msg = msg.unpack("x a*")[0]
- else
- if length_format == ITEM_LENGTH_8
- if msg.length - 1 < 1
- raise CC::DecodeError, "Data underrun while decoding"
- end
- length, msg = msg.unpack("x C a*")
- elsif length_format == ITEM_LENGTH_16
- if msg.length - 1 < 2
- raise CC::DecodeError, "Data underrun while decoding"
- end
- length, msg = msg.unpack("x n a*")
- elsif length_format == ITEM_LENGTH_32
- if msg.length - 1 < 4
- raise CC::DecodeError, "Data underrun while decoding"
- end
- length, msg = msg.unpack("x N a*")
- end
- if msg.length < length
- raise CC::DecodeError, "Data underrun while decoding"
- end
- item, msg = msg.unpack("a#{length} a*")
- end
-
- # unpack item based on type
- case type
- when ITEM_BLOB
- value = item
- when ITEM_UTF8
- value = item.encode('utf-8')
- when ITEM_BOOL
- value = decode_bool(item)
- when ITEM_INT
- value = decode_int(item)
- when ITEM_HASH
- value = decode_hash(item)
- when ITEM_LIST
- value = decode_array(item)
- when ITEM_NULL
- value = nil
- else
- raise CC::DecodeError, "Unknown item type in decode: #{type}"
- end
-
- [value, msg]
- end
-
- def self.decode_bool(msg)
- return msg == [0x01].pack("C")
- end
-
- def self.decode_int(msg)
- return Integer(msg.encode('utf-8'))
- end
-
- def self.decode_hash(msg)
- ret = {}
- while msg.length > 0
- tag, msg = decode_tag(msg)
- value, msg = decode_item(msg)
- ret[tag] = value
- end
-
- ret
- end
-
- def self.decode_array(msg)
- ret = []
- while msg.length > 0
- value, msg = decode_item(msg)
- ret << value
- end
-
- ret
- end
-
-end # class Message
-end # class CC
-
-if $0 == __FILE__
- target = {
- "from" => "sender at host",
- "to" => "recipient at host",
- "seq" => 1234,
- "data" => {
- "list" => [ 1, 2, nil, true, false, "this" ],
- "description" => "Fun for all",
- },
- }
-
- wire = CC::Message.to_wire(target)
- puts wire.inspect
-
- puts CC::Message.from_wire(wire).inspect
-end
diff --git a/src/lib/rbmsgq/lib/cc/session.rb b/src/lib/rbmsgq/lib/cc/session.rb
deleted file mode 100644
index 1d9cef5..0000000
--- a/src/lib/rbmsgq/lib/cc/session.rb
+++ /dev/null
@@ -1,214 +0,0 @@
-# Copyright (C) 2009 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.
-
-require 'socket'
-
-class CC
-class ProtocolError < Exception ; end
-end
-
-class CC
-class Session
- attr_reader :socket
- attr_reader :lname
-
- #
- # :host => host to connect to (defaults to "127.0.0.1")
- # :port => port to connect to (defaults to 9913)
- #
- def initialize(args = {})
- @socket = nil # TCP socket.
- @lname = nil # local name, or nil if not connected.
- @recvbuffer = "" # data buffer for partial reads.
- @recvlength = nil # if non-nil, we have a length to fill buffer to.
- @sendbuffer = "" # pending output data.
- @sequence = "a" # per message sequence id, always unique
-
- options = {
- :host => "127.0.0.1",
- :port => 9912
- }.merge(args)
-
- @socket = TCPSocket.new(options[:host], options[:port])
-
- #
- # Get our local name.
- #
- sendmsg({ :type => :getlname })
- msg = recvmsg(false)
- @lname = msg["lname"]
- if @lname.nil?
- raise CC::ProtocolError, "Could not get local name"
- end
- CC::set_utf8(@lname)
- end
-
- #
- # Send a message to the controller. The item to send can either be a
- # CC::Message object, or a Hash. If a Hash, it will be internally
- # converted to a CC::Message before transmitting.
- #
- # A return value of true means the entire message was not
- # transmitted, and a call to send_pending will have to be
- # made to send remaining data. This should only happen when
- # the socket is in non-blocking mode.
- #
- def sendmsg(msg)
- if msg.is_a?(Hash)
- msg = CC::Message.new(msg)
- end
-
- unless msg.is_a?(CC::Message)
- raise ArgumentError, "msg is not a CC::Message or a Hash"
- end
-
- wire = msg.to_wire
- @sendbuffer << [wire.length].pack("N")
- @sendbuffer << wire
-
- send_pending
- end
-
- #
- # Send as much data as we can.
- def send_pending
- return false if @sendbuffer.length == 0
- sent = @socket.send(@sendbuffer, 0)
- @sendbuffer = @sendbuffer[sent .. -1]
- @sendbuffer.length == 0 ? true : false
- end
-
- def recvmsg(nonblock = true)
- data = receive_full_buffer(nonblock)
- if data
- CC::Message::from_wire(data)
- else
- nil
- end
- end
-
- def group_subscribe(group, instance = "*", subtype = "normal")
- sendmsg({ :type => "subscribe",
- :group => group,
- :instance => instance,
- :subtype => subtype,
- })
- end
-
- def group_unsubscribe(group, instance = "*")
- sendmsg({ :type => "unsubscribe",
- :group => group,
- :instance => instance,
- })
- end
-
- def group_sendmsg(msg, group, instance = "*", to = "*")
- seq = next_sequence
- sendmsg({ :type => "send",
- :from => @lname,
- :to => to,
- :group => group,
- :instance => instance,
- :seq => seq,
- :msg => CC::Message.to_wire(msg),
- })
- seq
- end
-
- def group_replymsg(routing, msg)
- seq = next_sequence
- sendmsg({ :type => "send",
- :from => @lname,
- :to => routing["from"],
- :group => routing["group"],
- :instance => routing["instance"],
- :seq => seq,
- :reply => routing["seq"],
- :msg => CC::Message.to_wire(msg),
- })
- seq
- end
-
- def group_recvmsg(nonblock = true)
- msg = recvmsg(nonblock)
- return nil if msg.nil?
- data = CC::Message::from_wire(msg["msg"])
- msg.delete("msg")
- return [data, msg]
- end
-
- private
-
- def next_sequence
- @sequence.next!
- end
-
- #
- # A rather tricky function. If we have something waiting in our buffer,
- # and it will satisfy the current request, we will read it all in. If
- # not, we will read only what we need to satisfy a single message.
- #
- def receive_full_buffer(nonblock)
- # read the length prefix if we need it still.
- if @recvlength.nil?
- length = 4
- length -= @recvbuffer.length
- data = nil
- begin
- if nonblock
- data = @socket.recv_nonblock(length)
- else
- data = @socket.recv(length)
- end
- rescue Errno::EINPROGRESS
- rescue Errno::EAGAIN
- end
- return nil if data == nil
- @recvbuffer += data
- return nil if @recvbuffer.length < 4
- @recvlength = @recvbuffer.unpack('N')[0]
- @recvbuffer = ""
- CC::set_binary(@recvbuffer)
- end
-
- #
- # we have a length target. Loop reading data until we get enough to
- # fill our buffer.
- #
- length = @recvlength - @recvbuffer.length
- while (length > 0) do
- data = nil
- begin
- if nonblock
- data = @socket.recv_nonblock(length)
- else
- data = @socket.recv(length)
- end
- rescue Errno::EINPROGRESS
- rescue Errno::EAGAIN
- end
- return nil if data == 0 # blocking I/O
- @recvbuffer += data
- length -= data.length
- end
-
- data = @recvbuffer
- @recvbuffer = ""
- @recvlength = nil
- data
- end
-
-end # class Session
-end # class CC
diff --git a/src/lib/resolve/Makefile.am b/src/lib/resolve/Makefile.am
index 0b29da4..ceccce8 100644
--- a/src/lib/resolve/Makefile.am
+++ b/src/lib/resolve/Makefile.am
@@ -7,16 +7,36 @@ AM_CPPFLAGS += $(SQLITE_CFLAGS)
AM_CXXFLAGS = $(B10_CXXFLAGS)
-CLEANFILES = *.gcno *.gcda
+# Define rule to build logging source files from message file
+resolve_messages.h resolve_messages.cc: resolve_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/resolve/resolve_messages.mes
+
+# Tell Automake that the nsasdef.{cc,h} source files are created in the build
+# process, so it must create these before doing anything else. Although they
+# are a dependency of the library (so will be created from the message file
+# anyway), there is no guarantee as to exactly _when_ in the build they will be
+# created. As the .h file is included in other sources file (so must be
+# present when they are compiled), the safest option is to create it first.
+BUILT_SOURCES = resolve_messages.h resolve_messages.cc
+
+CLEANFILES = *.gcno *.gcda resolve_messages.cc resolve_messages.h
lib_LTLIBRARIES = libresolve.la
libresolve_la_SOURCES = resolve.h resolve.cc
+libresolve_la_SOURCES += resolve_log.h resolve_log.cc
libresolve_la_SOURCES += resolver_interface.h
libresolve_la_SOURCES += resolver_callback.h resolver_callback.cc
libresolve_la_SOURCES += response_classifier.cc response_classifier.h
libresolve_la_SOURCES += recursive_query.cc recursive_query.h
+
+nodist_libresolve_la_SOURCES = resolve_messages.h resolve_messages.cc
+
libresolve_la_LIBADD = $(top_builddir)/src/lib/dns/libdns++.la
libresolve_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+libresolve_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la
+
+# The message file should be in the distribution.
+EXTRA_DIST = resolve_messages.mes
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
diff --git a/src/lib/resolve/recursive_query.cc b/src/lib/resolve/recursive_query.cc
index eee98ca..d692dc1 100644
--- a/src/lib/resolve/recursive_query.cc
+++ b/src/lib/resolve/recursive_query.cc
@@ -16,33 +16,109 @@
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h> // for some IPC/network system calls
+#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>
#include <config.h>
-#include <log/dummylog.h>
-
#include <dns/question.h>
#include <dns/message.h>
#include <dns/opcode.h>
+#include <dns/exceptions.h>
+#include <dns/rdataclass.h>
#include <resolve/resolve.h>
+#include <resolve/resolve_log.h>
#include <cache/resolver_cache.h>
#include <nsas/address_request_callback.h>
#include <nsas/nameserver_address.h>
#include <asio.hpp>
-#include <asiolink/dns_service.h>
-#include <asiolink/io_fetch.h>
+#include <asiodns/dns_service.h>
+#include <asiodns/io_fetch.h>
#include <asiolink/io_service.h>
#include <resolve/recursive_query.h>
-using isc::log::dlog;
using namespace isc::dns;
+using namespace isc::util;
+using namespace isc::asiolink;
+using namespace isc::resolve;
+
+namespace isc {
+namespace asiodns {
+
+namespace {
+// Function to check if the given name/class has any address in the cache
+bool
+hasAddress(const Name& name, const RRClass& rrClass,
+ const isc::cache::ResolverCache& cache)
+{
+ // FIXME: If we are single-stack and we get only the other type of
+ // address, what should we do? In that case, it will be considered
+ // unreachable, which is most probably true, because A and AAAA will
+ // usually have the same RTT, so we should have both or none from the
+ // glue.
+ return (cache.lookup(name, RRType::A(), rrClass) != RRsetPtr() ||
+ cache.lookup(name, RRType::AAAA(), rrClass) != RRsetPtr());
+}
+
+// Convenience function for debug messages. Question::toText() includes
+// a trailing newline in its output, which makes it awkward to embed in a
+// message. This just strips that newline from it.
+std::string
+questionText(const isc::dns::Question& question) {
+ std::string text = question.toText();
+ if (!text.empty()) {
+ text.erase(text.size() - 1);
+ }
+ return (text);
+}
-namespace asiolink {
+} // anonymous namespace
+
+/// \brief Find deepest usable delegation in the cache
+///
+/// This finds the deepest delegation we have in cache and is safe to use.
+/// It is not public function, therefore it's not in header. But it's not
+/// in anonymous namespace, so we can call it from unittests.
+/// \param name The name we want to delegate to.
+/// \param cache The place too look for known delegations.
+std::string
+deepestDelegation(Name name, RRClass rrclass,
+ isc::cache::ResolverCache& cache)
+{
+ RRsetPtr cachedNS;
+ // Look for delegation point from bottom, until we find one with
+ // IP address or get to root.
+ //
+ // We need delegation with IP address so we can ask it right away.
+ // If we don't have the IP address, we would need to ask above it
+ // anyway in the best case, and the NS could be inside the zone,
+ // and we could get all loopy with the NSAS in the worst case.
+ while (name.getLabelCount() > 1 &&
+ (cachedNS = cache.lookupDeepestNS(name, rrclass)) != RRsetPtr()) {
+ // Look if we have an IP address for the NS
+ for (RdataIteratorPtr ns(cachedNS->getRdataIterator());
+ !ns->isLast(); ns->next()) {
+ // Do we have IP for this specific NS?
+ if (hasAddress(dynamic_cast<const rdata::generic::NS&>(
+ ns->getCurrent()).getNSName(), rrclass,
+ cache)) {
+ // Found one, stop checking and use this zone
+ // (there may be more addresses, that's only better)
+ return (cachedNS->getName().toText());
+ }
+ }
+ // We don't have anything for this one, so try something higher
+ if (name.getLabelCount() > 1) {
+ name = name.split(1);
+ }
+ }
+ // Fallback, nothing found, start at root
+ return (".");
+}
typedef std::vector<std::pair<std::string, uint16_t> > AddressVector;
@@ -56,27 +132,31 @@ RecursiveQuery::RecursiveQuery(DNSService& dns_service,
const std::vector<std::pair<std::string, uint16_t> >& upstream,
const std::vector<std::pair<std::string, uint16_t> >& upstream_root,
int query_timeout, int client_timeout, int lookup_timeout,
- unsigned retries) :
+ unsigned retries)
+ :
dns_service_(dns_service),
nsas_(nsas), cache_(cache),
upstream_(new AddressVector(upstream)),
upstream_root_(new AddressVector(upstream_root)),
test_server_("", 0),
query_timeout_(query_timeout), client_timeout_(client_timeout),
- lookup_timeout_(lookup_timeout), retries_(retries)
+ lookup_timeout_(lookup_timeout), retries_(retries), rtt_recorder_()
{
}
// Set the test server - only used for unit testing.
-
void
RecursiveQuery::setTestServer(const std::string& address, uint16_t port) {
- dlog("Setting test server to " + address + "(" +
- boost::lexical_cast<std::string>(port) + ")");
+ LOG_WARN(isc::resolve::logger, RESLIB_TEST_SERVER).arg(address).arg(port);
test_server_.first = address;
test_server_.second = port;
}
+// Set the RTT recorder - only used for testing
+void
+RecursiveQuery::setRttRecorder(boost::shared_ptr<RttRecorder>& recorder) {
+ rtt_recorder_ = recorder;
+}
namespace {
@@ -96,14 +176,16 @@ public:
ResolverNSASCallback(RunningQuery* rq) : rq_(rq) {}
void success(const isc::nsas::NameserverAddress& address) {
- dlog("Found a nameserver, sending query to " + address.getAddress().toText());
+ // Success callback, send query to found namesever
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQ_SUCCESS)
+ .arg(address.getAddress().toText());
rq_->nsasCallbackCalled();
rq_->sendTo(address);
}
void unreachable() {
- dlog("Nameservers unreachable");
- // Drop query or send servfail?
+ // Nameservers unreachable: drop query or send servfail?
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQ_FAIL);
rq_->nsasCallbackCalled();
rq_->makeSERVFAIL();
rq_->callCallback(true);
@@ -122,13 +204,12 @@ private:
// Info for (re)sending the query (the question and destination)
Question question_;
+ // This is the query message got from client
+ ConstMessagePtr query_message_;
+
// This is where we build and store our final answer
MessagePtr answer_message_;
- // currently we use upstream as the current list of NS records
- // we should differentiate between forwarding and resolving
- boost::shared_ptr<AddressVector> upstream_;
-
// Test server - only used for testing. This takes precedence over all
// other servers if the port is non-zero.
std::pair<std::string, uint16_t> test_server_;
@@ -222,16 +303,24 @@ private:
// event; we can cancel the NSAS callback safely.
size_t outstanding_events_;
+ // RTT Recorder. Used for testing, the RTTs of queries are
+ // sent to this object as well as being used to update the NSAS.
+ boost::shared_ptr<RttRecorder> rtt_recorder_;
+
// perform a single lookup; first we check the cache to see
// if we have a response for our query stored already. if
// so, call handlerecursiveresponse(), if not, we call send()
void doLookup() {
- dlog("doLookup: try cache");
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RUNQ_CACHE_LOOKUP)
+ .arg(questionText(question_));
+
Message cached_message(Message::RENDER);
isc::resolve::initResponseMessage(question_, cached_message);
if (cache_.lookup(question_.getName(), question_.getType(),
question_.getClass(), cached_message)) {
- dlog("Message found in cache, continuing with that");
+
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RUNQ_CACHE_FIND)
+ .arg(questionText(question_));
// Should these be set by the cache too?
cached_message.setOpcode(Opcode::QUERY());
cached_message.setRcode(Rcode::NOERROR());
@@ -241,10 +330,13 @@ private:
stop();
}
} else {
- cur_zone_ = ".";
+ cur_zone_ = deepestDelegation(question_.getName(),
+ question_.getClass(), cache_);
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_DEEPEST)
+ .arg(questionText(question_)).arg(cur_zone_);
send();
}
-
+
}
// Send the current question to the given nameserver address
@@ -254,45 +346,42 @@ private:
current_ns_address = address;
gettimeofday(¤t_ns_qsent_time, NULL);
++outstanding_events_;
- IOFetch query(protocol_, io_, question_,
- current_ns_address.getAddress(),
- 53, buffer_, this,
- query_timeout_);
- io_.get_io_service().post(query);
+ if (test_server_.second != 0) {
+ IOFetch query(protocol_, io_, question_,
+ test_server_.first,
+ test_server_.second, buffer_, this,
+ query_timeout_);
+ io_.get_io_service().post(query);
+ } else {
+ IOFetch query(protocol_, io_, question_,
+ current_ns_address.getAddress(),
+ 53, buffer_, this,
+ query_timeout_);
+ io_.get_io_service().post(query);
+ }
}
- // 'general' send; if we are in forwarder mode, send a query to
- // a random nameserver in our forwarders list. If we are in
- // recursive mode, ask the NSAS to give us an address.
+ // 'general' send, ask the NSAS to give us an address.
void send(IOFetch::Protocol protocol = IOFetch::UDP) {
- // If are in forwarder mode, send it to a random
- // forwarder. If not, ask the NSAS for an address
- const int uc = upstream_->size();
protocol_ = protocol; // Store protocol being used for this
if (test_server_.second != 0) {
- dlog("Sending upstream query (" + question_.toText() +
- ") to test server at " + test_server_.first);
+ // Send query to test server
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_TEST_UPSTREAM)
+ .arg(questionText(question_)).arg(test_server_.first);
+ gettimeofday(¤t_ns_qsent_time, NULL);
++outstanding_events_;
IOFetch query(protocol, io_, question_,
test_server_.first,
test_server_.second, buffer_, this,
query_timeout_);
io_.get_io_service().post(query);
- } else if (uc > 0) {
- // TODO: use boost, or rand()-utility function we provide
- int serverIndex = rand() % uc;
- dlog("Sending upstream query (" + question_.toText() +
- ") to " + upstream_->at(serverIndex).first);
- ++outstanding_events_;
- IOFetch query(protocol, io_, question_,
- upstream_->at(serverIndex).first,
- upstream_->at(serverIndex).second, buffer_, this,
- query_timeout_);
- io_.get_io_service().post(query);
+
} else {
// Ask the NSAS for an address for the current zone,
// the callback will call the actual sendTo()
- dlog("Look up nameserver for " + cur_zone_ + " in NSAS");
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_NSAS_LOOKUP)
+ .arg(cur_zone_);
+
// Can we have multiple calls to nsas_out? Let's assume not
// for now
assert(!nsas_callback_out_);
@@ -320,7 +409,7 @@ private:
// error message)
// returns false if we are not done
bool handleRecursiveAnswer(const Message& incoming) {
- dlog("Handle response");
+
// In case we get a CNAME, we store the target
// here (classify() will set it when it walks through
// the cname chain to verify it).
@@ -330,84 +419,112 @@ private:
isc::resolve::ResponseClassifier::classify(
question_, incoming, cname_target, cname_count_);
- bool found_ns_address = false;
+ bool found_ns = false;
switch (category) {
case isc::resolve::ResponseClassifier::ANSWER:
case isc::resolve::ResponseClassifier::ANSWERCNAME:
- // Done. copy and return.
- dlog("Response is an answer");
+ // Answer received - copy and return.
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_ANSWER)
+ .arg(questionText(question_));
isc::resolve::copyResponseMessage(incoming, answer_message_);
cache_.update(*answer_message_);
return true;
break;
+
case isc::resolve::ResponseClassifier::CNAME:
- dlog("Response is CNAME!");
+ // CNAME received.
+
// (unfinished) CNAME. We set our question_ to the CNAME
// target, then start over at the beginning (for now, that
// is, we reset our 'current servers' to the root servers).
if (cname_count_ >= RESOLVER_MAX_CNAME_CHAIN) {
- // just give up
- dlog("CNAME chain too long");
+ // CNAME chain too long - just give up
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_LONG_CHAIN)
+ .arg(questionText(question_));
makeSERVFAIL();
return true;
}
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_CNAME)
+ .arg(questionText(question_));
+
answer_message_->appendSection(Message::SECTION_ANSWER,
incoming);
question_ = Question(cname_target, question_.getClass(),
question_.getType());
- dlog("Following CNAME chain to " + question_.toText());
+ // Follow CNAME chain.
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_FOLLOW_CNAME)
+ .arg(questionText(question_));
doLookup();
return false;
break;
+
case isc::resolve::ResponseClassifier::NXDOMAIN:
case isc::resolve::ResponseClassifier::NXRRSET:
- dlog("Response is NXDOMAIN or NXRRSET");
- // NXDOMAIN, just copy and return.
- dlog(incoming.toText());
+ // Received NXDOMAIN or NXRRSET, just copy and return
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_NXDOM_NXRR)
+ .arg(questionText(question_));
isc::resolve::copyResponseMessage(incoming, answer_message_);
// no negcache yet
//cache_.update(*answer_message_);
return true;
break;
+
case isc::resolve::ResponseClassifier::REFERRAL:
- dlog("Response is referral");
+ // Response is a referral
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_REFERRAL)
+ .arg(questionText(question_));
+
cache_.update(incoming);
// Referral. For now we just take the first glue address
// we find and continue with that
// auth section should have at least one RRset
// and one of them should be an NS (otherwise
- // classifier should have error'd)
- // TODO: should we check if it really is subzone?
+ // classifier should have error'd) to a subdomain
for (RRsetIterator rrsi = incoming.beginSection(Message::SECTION_AUTHORITY);
- rrsi != incoming.endSection(Message::SECTION_AUTHORITY) && !found_ns_address;
+ rrsi != incoming.endSection(Message::SECTION_AUTHORITY) && !found_ns;
++rrsi) {
ConstRRsetPtr rrs = *rrsi;
if (rrs->getType() == RRType::NS()) {
- // TODO: make cur_zone_ a Name instead of a string
- // (this requires a few API changes in related
- // libraries, so as not to need many conversions)
- cur_zone_ = rrs->getName().toText();
- dlog("Referred to zone " + cur_zone_);
- found_ns_address = true;
- break;
+ NameComparisonResult compare(Name(cur_zone_).compare(rrs->getName()));
+ if (compare.getRelation() == NameComparisonResult::SUPERDOMAIN) {
+ // TODO: make cur_zone_ a Name instead of a string
+ // (this requires a few API changes in related
+ // libraries, so as not to need many conversions)
+ cur_zone_ = rrs->getName().toText();
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_REFER_ZONE)
+ .arg(cur_zone_);
+ found_ns = true;
+ break;
+ }
}
}
- if (found_ns_address) {
+ if (found_ns) {
// next resolver round
// we do NOT use doLookup() here, but send() (i.e. we
// skip the cache), since if we had the final answer
// instead of a delegation cached, we would have been
// there by now.
- send();
+ GlueHints glue_hints(cur_zone_, incoming);
+
+ // Ask the NSAS for an address, or glue.
+ // This will eventually result in either sendTo()
+ // or stop() being called by nsas_callback_
+ assert(!nsas_callback_out_);
+ nsas_callback_out_ = true;
+ nsas_.lookup(cur_zone_, question_.getClass(),
+ nsas_callback_, ANY_OK, glue_hints);
return false;
} else {
- dlog("No NS RRset in referral?");
+ // Referral was received but did not contain an NS RRset.
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_NO_NS_RRSET)
+ .arg(questionText(question_));
+
// TODO this will result in answering with the delegation. oh well
isc::resolve::copyResponseMessage(incoming, answer_message_);
return true;
@@ -417,7 +534,8 @@ private:
// Truncated packet. If the protocol we used for the last one is
// UDP, re-query using TCP. Otherwise regard it as an error.
if (protocol_ == IOFetch::UDP) {
- dlog("Response truncated, re-querying over TCP");
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_TRUNCATED)
+ .arg(questionText(question_));
send(IOFetch::TCP);
return false;
}
@@ -436,6 +554,8 @@ private:
case isc::resolve::ResponseClassifier::NOTSINGLE:
case isc::resolve::ResponseClassifier::OPCODE:
case isc::resolve::ResponseClassifier::RCODE:
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_RCODE_ERR)
+ .arg(questionText(question_));
// Should we try a different server rather than SERVFAIL?
makeSERVFAIL();
return true;
@@ -453,18 +573,19 @@ public:
RunningQuery(IOService& io,
const Question& question,
MessagePtr answer_message,
- boost::shared_ptr<AddressVector> upstream,
std::pair<std::string, uint16_t>& test_server,
OutputBufferPtr buffer,
isc::resolve::ResolverInterface::CallbackPtr cb,
int query_timeout, int client_timeout, int lookup_timeout,
unsigned retries,
isc::nsas::NameserverAddressStore& nsas,
- isc::cache::ResolverCache& cache) :
+ isc::cache::ResolverCache& cache,
+ boost::shared_ptr<RttRecorder>& recorder)
+ :
io_(io),
question_(question),
+ query_message_(),
answer_message_(answer_message),
- upstream_(upstream),
test_server_(test_server),
buffer_(buffer),
resolvercallback_(cb),
@@ -478,9 +599,11 @@ public:
callback_called_(false),
nsas_(nsas),
cache_(cache),
+ cur_zone_("."),
nsas_callback_(new ResolverNSASCallback(this)),
nsas_callback_out_(false),
- outstanding_events_(0)
+ outstanding_events_(0),
+ rtt_recorder_(recorder)
{
// Setup the timer to stop trying (lookup_timeout)
if (lookup_timeout >= 0) {
@@ -588,46 +711,75 @@ public:
// Update the NSAS with the time it took
struct timeval cur_time;
gettimeofday(&cur_time, NULL);
- uint32_t rtt;
- if (cur_time.tv_sec >= current_ns_qsent_time.tv_sec ||
- cur_time.tv_usec > current_ns_qsent_time.tv_usec) {
+ uint32_t rtt = 0;
+
+ // Only calculate RTT if it is positive
+ if (cur_time.tv_sec > current_ns_qsent_time.tv_sec ||
+ (cur_time.tv_sec == current_ns_qsent_time.tv_sec &&
+ cur_time.tv_usec > current_ns_qsent_time.tv_usec)) {
rtt = 1000 * (cur_time.tv_sec - current_ns_qsent_time.tv_sec);
rtt += (cur_time.tv_usec - current_ns_qsent_time.tv_usec) / 1000;
- } else {
- rtt = 1;
}
-
- dlog("RTT: " + boost::lexical_cast<std::string>(rtt));
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_RTT).arg(rtt);
current_ns_address.updateRTT(rtt);
-
- Message incoming(Message::PARSE);
- InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
- incoming.fromWire(ibuf);
-
- buffer_->clear();
- if (recursive_mode() &&
- incoming.getRcode() == Rcode::NOERROR()) {
- done_ = handleRecursiveAnswer(incoming);
- } else {
- isc::resolve::copyResponseMessage(incoming, answer_message_);
- done_ = true;
+ if (rtt_recorder_) {
+ rtt_recorder_->addRtt(rtt);
}
-
- if (done_) {
- callCallback(true);
- stop();
+
+ try {
+ Message incoming(Message::PARSE);
+ InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
+
+ incoming.fromWire(ibuf);
+
+ buffer_->clear();
+ if (incoming.getRcode() == Rcode::NOERROR()) {
+ done_ = handleRecursiveAnswer(incoming);
+ } else {
+ isc::resolve::copyResponseMessage(incoming, answer_message_);
+ done_ = true;
+ }
+ if (done_) {
+ callCallback(true);
+ stop();
+ }
+ } catch (const isc::dns::DNSProtocolError& dpe) {
+ // Right now, we treat this similar to timeouts
+ // (except we don't store RTT)
+ // We probably want to make this an integral part
+ // of the fetch data process. (TODO)
+ if (retries_--) {
+ // Retry
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS,
+ RESLIB_PROTOCOL_RETRY)
+ .arg(questionText(question_)).arg(dpe.what())
+ .arg(retries_);
+ send();
+ } else {
+ // Give up
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS,
+ RESLIB_PROTOCOL)
+ .arg(questionText(question_)).arg(dpe.what());
+ if (!callback_called_) {
+ makeSERVFAIL();
+ callCallback(true);
+ }
+ stop();
+ }
}
} else if (!done_ && retries_--) {
// Query timed out, but we have some retries, so send again
- dlog("Timeout for " + question_.toText() + " to " + current_ns_address.getAddress().toText() + ", resending query");
- if (recursive_mode()) {
- current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
- }
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_TIMEOUT_RETRY)
+ .arg(questionText(question_))
+ .arg(current_ns_address.getAddress().toText()).arg(retries_);
+ current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
send();
} else {
// We are either already done, or out of retries
- if (recursive_mode() && result == IOFetch::TIME_OUT) {
- dlog("Timeout for " + question_.toText() + " to " + current_ns_address.getAddress().toText() + ", giving up");
+ if (result == IOFetch::TIME_OUT) {
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_TIMEOUT)
+ .arg(questionText(question_))
+ .arg(current_ns_address.getAddress().toText());
current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
}
if (!callback_called_) {
@@ -643,12 +795,181 @@ public:
void makeSERVFAIL() {
isc::resolve::makeErrorMessage(answer_message_, Rcode::SERVFAIL());
}
-
- // Returns true if we are in 'recursive' mode
- // Returns false if we are in 'forwarding' mode
- // (i.e. if we have anything in upstream_)
- bool recursive_mode() const {
- return upstream_->empty();
+};
+
+class ForwardQuery : public IOFetch::Callback {
+private:
+ // The io service to handle async calls
+ IOService& io_;
+
+ // This is the query message got from client
+ ConstMessagePtr query_message_;
+
+ // This is where we build and store our final answer
+ MessagePtr answer_message_;
+
+ // List of nameservers to forward to
+ boost::shared_ptr<AddressVector> upstream_;
+
+ // Buffer to store the result.
+ OutputBufferPtr buffer_;
+
+ // This will be notified when we succeed or fail
+ isc::resolve::ResolverInterface::CallbackPtr resolvercallback_;
+
+ /*
+ * TODO Do something more clever with timeouts. In the long term, some
+ * computation of average RTT, increase with each retry, etc.
+ */
+ // Timeout information
+ int query_timeout_;
+
+ // TODO: replace by our wrapper
+ asio::deadline_timer client_timer;
+ asio::deadline_timer lookup_timer;
+
+ // Make FowardQuery deletes itself safely. for more information see
+ // the comments of outstanding_events in RunningQuery.
+ size_t outstanding_events_;
+
+ // If we have a client timeout, we call back with a failure message,
+ // but we do not stop yet. We use this variable to make sure we
+ // don't call back a second time later
+ bool callback_called_;
+
+ // send the query to the server.
+ void send(IOFetch::Protocol protocol = IOFetch::UDP) {
+ const int uc = upstream_->size();
+ buffer_->clear();
+ int serverIndex = rand() % uc;
+ ConstQuestionPtr question = *(query_message_->beginQuestion());
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_UPSTREAM)
+ .arg(questionText(*question))
+ .arg(upstream_->at(serverIndex).first);
+
+ ++outstanding_events_;
+ // Forward the query, create the IOFetch with
+ // query message, so that query flags can be forwarded
+ // together.
+ IOFetch query(protocol, io_, query_message_,
+ upstream_->at(serverIndex).first,
+ upstream_->at(serverIndex).second,
+ buffer_, this, query_timeout_);
+
+ io_.get_io_service().post(query);
+ }
+
+public:
+ ForwardQuery(IOService& io,
+ ConstMessagePtr query_message,
+ MessagePtr answer_message,
+ boost::shared_ptr<AddressVector> upstream,
+ OutputBufferPtr buffer,
+ isc::resolve::ResolverInterface::CallbackPtr cb,
+ int query_timeout, int client_timeout, int lookup_timeout) :
+ io_(io),
+ query_message_(query_message),
+ answer_message_(answer_message),
+ upstream_(upstream),
+ buffer_(buffer),
+ resolvercallback_(cb),
+ query_timeout_(query_timeout),
+ client_timer(io.get_io_service()),
+ lookup_timer(io.get_io_service()),
+ outstanding_events_(0),
+ callback_called_(false)
+ {
+ // Setup the timer to stop trying (lookup_timeout)
+ if (lookup_timeout >= 0) {
+ lookup_timer.expires_from_now(
+ boost::posix_time::milliseconds(lookup_timeout));
+ ++outstanding_events_;
+ lookup_timer.async_wait(boost::bind(&ForwardQuery::lookupTimeout, this));
+ }
+
+ // Setup the timer to send an answer (client_timeout)
+ if (client_timeout >= 0) {
+ client_timer.expires_from_now(
+ boost::posix_time::milliseconds(client_timeout));
+ ++outstanding_events_;
+ client_timer.async_wait(boost::bind(&ForwardQuery::clientTimeout, this));
+ }
+
+ send();
+ }
+
+ virtual void lookupTimeout() {
+ if (!callback_called_) {
+ makeSERVFAIL();
+ callCallback(false);
+ }
+ assert(outstanding_events_ > 0);
+ --outstanding_events_;
+ stop();
+ }
+
+ virtual void clientTimeout() {
+ if (!callback_called_) {
+ makeSERVFAIL();
+ callCallback(false);
+ }
+ assert(outstanding_events_ > 0);
+ --outstanding_events_;
+ stop();
+ }
+
+ // If the callback has not been called yet, call it now
+ // If success is true, we call 'success' with our answer_message
+ // If it is false, we call failure()
+ void callCallback(bool success) {
+ if (!callback_called_) {
+ callback_called_ = true;
+ if (success) {
+ resolvercallback_->success(answer_message_);
+ } else {
+ resolvercallback_->failure();
+ }
+ }
+ }
+
+ virtual void stop() {
+ // if we cancel our timers, we will still get an event for
+ // that, so we cannot delete ourselves just yet (those events
+ // would be bound to a deleted object)
+ // cancel them one by one, both cancels should get us back
+ // here again.
+ // same goes if we have an outstanding query (can't delete
+ // until that one comes back to us)
+ lookup_timer.cancel();
+ client_timer.cancel();
+ if (outstanding_events_ > 0) {
+ return;
+ } else {
+ delete this;
+ }
+ }
+
+ // This function is used as callback from DNSQuery.
+ virtual void operator()(IOFetch::Result result) {
+ // XXX is this the place for TCP retry?
+ assert(outstanding_events_ > 0);
+ --outstanding_events_;
+ if (result != IOFetch::TIME_OUT) {
+ // we got an answer
+ Message incoming(Message::PARSE);
+ InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
+ incoming.fromWire(ibuf);
+ isc::resolve::copyResponseMessage(incoming, answer_message_);
+ callCallback(true);
+ }
+
+ stop();
+ }
+
+ // Clear the answer parts of answer_message, and set the rcode
+ // to servfail
+ void makeSERVFAIL() {
+ isc::resolve::makeErrorMessage(answer_message_, Rcode::SERVFAIL());
}
};
@@ -665,14 +986,16 @@ RecursiveQuery::resolve(const QuestionPtr& question,
OutputBufferPtr buffer(new OutputBuffer(0));
- dlog("Asked to resolve: " + question->toText());
-
- dlog("Try out cache first (direct call to resolve)");
// First try to see if we have something cached in the messagecache
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESOLVE)
+ .arg(questionText(*question)).arg(1);
if (cache_.lookup(question->getName(), question->getType(),
question->getClass(), *answer_message) &&
answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
- dlog("Message found in cache, returning that");
+ // Message found, return that
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RECQ_CACHE_FIND)
+ .arg(questionText(*question)).arg(1);
+
// TODO: err, should cache set rcode as well?
answer_message->setRcode(Rcode::NOERROR());
callback->success(answer_message);
@@ -683,18 +1006,23 @@ RecursiveQuery::resolve(const QuestionPtr& question,
question->getType(),
question->getClass());
if (cached_rrset) {
- dlog("Found single RRset in cache");
+ // Found single RRset in cache
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RRSET_FOUND)
+ .arg(questionText(*question)).arg(1);
answer_message->addRRset(Message::SECTION_ANSWER,
cached_rrset);
answer_message->setRcode(Rcode::NOERROR());
callback->success(answer_message);
} else {
- dlog("Message not found in cache, starting recursive query");
- // It will delete itself when it is done
- new RunningQuery(io, *question, answer_message, upstream_,
+ // Message not found in cache, start recursive query. It will
+ // delete itself when it is done
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RECQ_CACHE_NO_FIND)
+ .arg(questionText(*question)).arg(1);
+ new RunningQuery(io, *question, answer_message,
test_server_, buffer, callback,
query_timeout_, client_timeout_,
- lookup_timeout_, retries_, nsas_, cache_);
+ lookup_timeout_, retries_, nsas_,
+ cache_, rtt_recorder_);
}
}
}
@@ -718,14 +1046,17 @@ RecursiveQuery::resolve(const Question& question,
answer_message->setOpcode(isc::dns::Opcode::QUERY());
answer_message->addQuestion(question);
- dlog("Asked to resolve: " + question.toText());
-
// First try to see if we have something cached in the messagecache
- dlog("Try out cache first (started by incoming event)");
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESOLVE)
+ .arg(questionText(question)).arg(2);
+
if (cache_.lookup(question.getName(), question.getType(),
question.getClass(), *answer_message) &&
answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
- dlog("Message found in cache, returning that");
+
+ // Message found, return that
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RECQ_CACHE_FIND)
+ .arg(questionText(question)).arg(2);
// TODO: err, should cache set rcode as well?
answer_message->setRcode(Rcode::NOERROR());
crs->success(answer_message);
@@ -736,22 +1067,57 @@ RecursiveQuery::resolve(const Question& question,
question.getType(),
question.getClass());
if (cached_rrset) {
- dlog("Found single RRset in cache");
+ // Found single RRset in cache
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RRSET_FOUND)
+ .arg(questionText(question)).arg(2);
answer_message->addRRset(Message::SECTION_ANSWER,
cached_rrset);
answer_message->setRcode(Rcode::NOERROR());
crs->success(answer_message);
+
} else {
- dlog("Message not found in cache, starting recursive query");
- // It will delete itself when it is done
- new RunningQuery(io, question, answer_message, upstream_,
+ // Message not found in cache, start recursive query. It will
+ // delete itself when it is done
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RECQ_CACHE_NO_FIND)
+ .arg(questionText(question)).arg(2);
+ new RunningQuery(io, question, answer_message,
test_server_, buffer, crs, query_timeout_,
client_timeout_, lookup_timeout_, retries_,
- nsas_, cache_);
+ nsas_, cache_, rtt_recorder_);
}
}
}
+void
+RecursiveQuery::forward(ConstMessagePtr query_message,
+ MessagePtr answer_message,
+ OutputBufferPtr buffer,
+ DNSServer* server,
+ isc::resolve::ResolverInterface::CallbackPtr callback)
+{
+ // XXX: eventually we will need to be able to determine whether
+ // the message should be sent via TCP or UDP, or sent initially via
+ // UDP and then fall back to TCP on failure, but for the moment
+ // we're only going to handle UDP.
+ IOService& io = dns_service_.getIOService();
+
+ if (!callback) {
+ callback.reset(new isc::resolve::ResolverCallbackServer(server));
+ }
+ // TODO: general 'prepareinitialanswer'
+ answer_message->setOpcode(isc::dns::Opcode::QUERY());
+ ConstQuestionPtr question = *query_message->beginQuestion();
+ answer_message->addQuestion(*question);
+
+ // implement the simplest forwarder, which will pass
+ // everything throught without interpretation, except
+ // QID, port number. The response will not be cached.
+ // It will delete itself when it is done
+ new ForwardQuery(io, query_message, answer_message,
+ upstream_, buffer, callback, query_timeout_,
+ client_timeout_, lookup_timeout_);
+}
-} // namespace asiolink
+} // namespace asiodns
+} // namespace isc
diff --git a/src/lib/resolve/recursive_query.h b/src/lib/resolve/recursive_query.h
index 1180d55..b9fb80d 100644
--- a/src/lib/resolve/recursive_query.h
+++ b/src/lib/resolve/recursive_query.h
@@ -15,18 +15,49 @@
#ifndef __RECURSIVE_QUERY_H
#define __RECURSIVE_QUERY_H 1
-#include <asiolink/dns_service.h>
-#include <asiolink/dns_server.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
+#include <asiodns/dns_service.h>
+#include <asiodns/dns_server.h>
#include <nsas/nameserver_address_store.h>
#include <cache/resolver_cache.h>
-namespace asiolink {
-/// \brief The \c RecursiveQuery class provides a layer of abstraction around
-/// the ASIO code that carries out an upstream query.
+namespace isc {
+namespace asiodns {
+
+/// \brief RTT Recorder
+///
+/// Used for testing, this class will hold the set of round-trip times to
+/// nameservers for the current recursive query.
+///
+/// A pointer to an object of this class is passed to RecursiveQuery which in
+/// turn passes it to the created RunningQuery class. When a running query
+/// completes, its RTT is passed to the RTT Recorder object.
+class RttRecorder {
+public:
+ /// \brief Record Time
+ ///
+ /// Adds a round-trip time to the internal vector of times.
+ ///
+ /// \param RTT to record.
+ void addRtt(uint32_t rtt) {
+ rtt_.push_back(rtt);
+ }
+
+ /// \brief Return RTT Vector
+ std::vector<uint32_t> getRtt() const {
+ return rtt_;
+ }
+
+private:
+ std::vector<uint32_t> rtt_; ///< Stored round-trip times
+};
+
+
+/// \brief Recursive Query
///
-/// This design is very preliminary; currently it is only capable of
-/// handling simple forward requests to a single resolver.
+/// The \c RecursiveQuery class provides a layer of abstraction around
+/// the ASIO code that carries out an upstream query.
+
class RecursiveQuery {
///
/// \name Constructors
@@ -56,17 +87,25 @@ public:
isc::nsas::NameserverAddressStore& nsas,
isc::cache::ResolverCache& cache,
const std::vector<std::pair<std::string, uint16_t> >&
- upstream,
+ upstream,
const std::vector<std::pair<std::string, uint16_t> >&
- upstream_root,
+ upstream_root,
int query_timeout = 2000,
int client_timeout = 4000,
int lookup_timeout = 30000,
unsigned retries = 3);
//@}
+ /// \brief Set Round-Trip Time Recorder
+ ///
+ /// Sets the RTT recorder object. This is not accessed directly, instead
+ /// it is passed to created RunningQuery objects.
+ ///
+ /// \param recorder Pointer to the RTT recorder object used to hold RTTs.
+ void setRttRecorder(boost::shared_ptr<RttRecorder>& recorder);
+
/// \brief Initiate resolving
- ///
+ ///
/// When sendQuery() is called, a (set of) message(s) is sent
/// asynchronously. If upstream servers are set, one is chosen
/// and the response (if any) from that server will be returned.
@@ -99,9 +138,23 @@ public:
/// \param server A pointer to the \c DNSServer object handling the client
void resolve(const isc::dns::Question& question,
isc::dns::MessagePtr answer_message,
- isc::dns::OutputBufferPtr buffer,
+ isc::util::OutputBufferPtr buffer,
DNSServer* server);
+ /// \brief Initiates forwarding for the given query.
+ ///
+ /// Others parameters are same with the parameters of
+ /// function resolve().
+ ///
+ /// \param query_message the full query got from client.
+ /// \param callback callback object
+ void forward(isc::dns::ConstMessagePtr query_message,
+ isc::dns::MessagePtr answer_message,
+ isc::util::OutputBufferPtr buffer,
+ DNSServer* server,
+ isc::resolve::ResolverInterface::CallbackPtr callback =
+ isc::resolve::ResolverInterface::CallbackPtr());
+
/// \brief Set Test Server
///
/// This method is *only* for unit testing the class. If set, it enables
@@ -113,7 +166,7 @@ public:
/// \param address IP address of the test server.
/// \param port Port number of the test server
void setTestServer(const std::string& address, uint16_t port);
-
+
private:
DNSService& dns_service_;
isc::nsas::NameserverAddressStore& nsas_;
@@ -127,7 +180,9 @@ private:
int client_timeout_;
int lookup_timeout_;
unsigned retries_;
+ boost::shared_ptr<RttRecorder> rtt_recorder_; ///< Round-trip time recorder
};
-} // namespace asiolink
+} // namespace asiodns
+} // namespace isc
#endif // __RECURSIVE_QUERY_H
diff --git a/src/lib/resolve/resolve_log.cc b/src/lib/resolve/resolve_log.cc
new file mode 100644
index 0000000..e41d8d2
--- /dev/null
+++ b/src/lib/resolve/resolve_log.cc
@@ -0,0 +1,26 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// Defines the logger used by the NSAS
+
+#include <resolve/resolve_log.h>
+
+namespace isc {
+namespace resolve {
+
+isc::log::Logger logger("reslib"); // Distinct from "resolver"
+
+} // namespace resolve
+} // namespace isc
+
diff --git a/src/lib/resolve/resolve_log.h b/src/lib/resolve/resolve_log.h
new file mode 100644
index 0000000..1f2869e
--- /dev/null
+++ b/src/lib/resolve/resolve_log.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __RESOLVE_LOG__H
+#define __RESOLVE_LOG__H
+
+#include <log/macros.h>
+#include "resolve_messages.h"
+
+namespace isc {
+namespace resolve {
+
+/// \brief Resolver Library Logging
+///
+/// Defines the levels used to output debug messages in the resolver library.
+/// Note that higher numbers equate to more verbose (and detailed) output.
+
+// The first level traces normal operations
+const int RESLIB_DBG_TRACE = 10;
+
+// The next level extends the normal operations and records the results of the
+// lookups.
+const int RESLIB_DBG_RESULTS = 20;
+
+// Report cache lookups and results
+const int RESLIB_DBG_CACHE = 40;
+
+// Indicate when callbacks are called
+const int RESLIB_DBG_CB = 50;
+
+
+/// \brief Resolver Library Logger
+///
+/// Define the logger used to log messages. We could define it in multiple
+/// modules, but defining in a single module and linking to it saves time and
+/// space.
+extern isc::log::Logger logger;
+
+} // namespace resolve
+} // namespace isc
+
+#endif // __RESOLVE_LOG__H
diff --git a/src/lib/resolve/resolve_messages.mes b/src/lib/resolve/resolve_messages.mes
new file mode 100644
index 0000000..97c4d90
--- /dev/null
+++ b/src/lib/resolve/resolve_messages.mes
@@ -0,0 +1,154 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+$NAMESPACE isc::resolve
+
+% RESLIB_ANSWER answer received in response to query for <%1>
+A debug message recording that an answer has been received to an upstream
+query for the specified question. Previous debug messages will have indicated
+the server to which the question was sent.
+
+% RESLIB_CNAME CNAME received in response to query for <%1>
+A debug message recording that CNAME response has been received to an upstream
+query for the specified question. Previous debug messages will have indicated
+the server to which the question was sent.
+
+% RESLIB_DEEPEST did not find <%1> in cache, deepest delegation found is %2
+A debug message, a cache lookup did not find the specified <name, class,
+type> tuple in the cache; instead, the deepest delegation found is indicated.
+
+% RESLIB_FOLLOW_CNAME following CNAME chain to <%1>
+A debug message, a CNAME response was received and another query is being issued
+for the <name, class, type> tuple.
+
+% RESLIB_LONG_CHAIN CNAME received in response to query for <%1>: CNAME chain length exceeded
+A debug message recording that a CNAME response has been received to an upstream
+query for the specified question (Previous debug messages will have indicated
+the server to which the question was sent). However, receipt of this CNAME
+has meant that the resolver has exceeded the CNAME chain limit (a CNAME chain
+is where on CNAME points to another) and so an error is being returned.
+
+% RESLIB_NO_NS_RRSET no NS RRSet in referral response received to query for <%1>
+A debug message, this indicates that a response was received for the specified
+query and was categorized as a referral. However, the received message did
+not contain any NS RRsets. This may indicate a programming error in the
+response classification code.
+
+% RESLIB_NSAS_LOOKUP looking up nameserver for zone %1 in the NSAS
+A debug message, the RunningQuery object is querying the NSAS for the
+nameservers for the specified zone.
+
+% RESLIB_NXDOM_NXRR NXDOMAIN/NXRRSET received in response to query for <%1>
+A debug message recording that either a NXDOMAIN or an NXRRSET response has
+been received to an upstream query for the specified question. Previous debug
+messages will have indicated the server to which the question was sent.
+
+% RESLIB_PROTOCOL protocol error in answer for %1: %3
+A debug message indicating that a protocol error was received. As there
+are no retries left, an error will be reported.
+
+% RESLIB_PROTOCOL_RETRY protocol error in answer for %1: %2 (retries left: %3)
+A debug message indicating that a protocol error was received and that
+the resolver is repeating the query to the same nameserver. After this
+repeated query, there will be the indicated number of retries left.
+
+% RESLIB_RCODE_ERR RCODE indicates error in response to query for <%1>
+A debug message, the response to the specified query indicated an error
+that is not covered by a specific code path. A SERVFAIL will be returned.
+
+% RESLIB_RECQ_CACHE_FIND found <%1> in the cache (resolve() instance %2)
+This is a debug message and indicates that a RecursiveQuery object found the
+the specified <name, class, type> tuple in the cache. The instance number
+at the end of the message indicates which of the two resolve() methods has
+been called.
+
+% RESLIB_RECQ_CACHE_NO_FIND did not find <%1> in the cache, starting RunningQuery (resolve() instance %2)
+This is a debug message and indicates that the look in the cache made by the
+RecursiveQuery::resolve() method did not find an answer, so a new RunningQuery
+object has been created to resolve the question. The instance number at
+the end of the message indicates which of the two resolve() methods has
+been called.
+
+% RESLIB_REFERRAL referral received in response to query for <%1>
+A debug message recording that a referral response has been received to an
+upstream query for the specified question. Previous debug messages will
+have indicated the server to which the question was sent.
+
+% RESLIB_REFER_ZONE referred to zone %1
+A debug message indicating that the last referral message was to the specified
+zone.
+
+% RESLIB_RESOLVE asked to resolve <%1> (resolve() instance %2)
+A debug message, the RecursiveQuery::resolve method has been called to resolve
+the specified <name, class, type> tuple. The first action will be to lookup
+the specified tuple in the cache. The instance number at the end of the
+message indicates which of the two resolve() methods has been called.
+
+% RESLIB_RRSET_FOUND found single RRset in the cache when querying for <%1> (resolve() instance %2)
+A debug message, indicating that when RecursiveQuery::resolve queried the
+cache, a single RRset was found which was put in the answer. The instance
+number at the end of the message indicates which of the two resolve()
+methods has been called.
+
+% RESLIB_RTT round-trip time of last query calculated as %1 ms
+A debug message giving the round-trip time of the last query and response.
+
+% RESLIB_RUNQ_CACHE_FIND found <%1> in the cache
+This is a debug message and indicates that a RunningQuery object found
+the specified <name, class, type> tuple in the cache.
+
+% RESLIB_RUNQ_CACHE_LOOKUP looking up up <%1> in the cache
+This is a debug message and indicates that a RunningQuery object has made
+a call to its doLookup() method to look up the specified <name, class, type>
+tuple, the first action of which will be to examine the cache.
+
+% RESLIB_RUNQ_FAIL failure callback - nameservers are unreachable
+A debug message indicating that a RunningQuery's failure callback has been
+called because all nameservers for the zone in question are unreachable.
+
+% RESLIB_RUNQ_SUCCESS success callback - sending query to %1
+A debug message indicating that a RunningQuery's success callback has been
+called because a nameserver has been found, and that a query is being sent
+to the specified nameserver.
+
+% RESLIB_TEST_SERVER setting test server to %1(%2)
+This is an internal debugging message and is only generated in unit tests.
+It indicates that all upstream queries from the resolver are being routed to
+the specified server, regardless of the address of the nameserver to which
+the query would normally be routed. As it should never be seen in normal
+operation, it is a warning message instead of a debug message.
+
+% RESLIB_TEST_UPSTREAM sending upstream query for <%1> to test server at %2
+This is a debug message and should only be seen in unit tests. A query for
+the specified <name, class, type> tuple is being sent to a test nameserver
+whose address is given in the message.
+
+% RESLIB_TIMEOUT query <%1> to %2 timed out
+A debug message indicating that the specified query has timed out and as
+there are no retries left, an error will be reported.
+
+% RESLIB_TIMEOUT_RETRY query <%1> to %2 timed out, re-trying (retries left: %3)
+A debug message indicating that the specified query has timed out and that
+the resolver is repeating the query to the same nameserver. After this
+repeated query, there will be the indicated number of retries left.
+
+% RESLIB_TRUNCATED response to query for <%1> was truncated, re-querying over TCP
+A debug message, this indicates that the response to the specified query was
+truncated and that the resolver will be re-querying over TCP. There are
+various reasons why responses may be truncated, so this message is normal and
+gives no cause for concern.
+
+% RESLIB_UPSTREAM sending upstream query for <%1> to %2
+A debug message indicating that a query for the specified <name, class, type>
+tuple is being sent to a nameserver whose address is given in the message.
diff --git a/src/lib/resolve/resolver_callback.h b/src/lib/resolve/resolver_callback.h
index f69d8a7..79138e8 100644
--- a/src/lib/resolve/resolver_callback.h
+++ b/src/lib/resolve/resolver_callback.h
@@ -15,9 +15,11 @@
#ifndef _ISC_RESOLVER_CALLBACK_H
#define _ISC_RESOLVER_CALLBACK_H 1
-#include <asiolink/asiolink.h>
+#include <asiodns/dns_server.h>
#include <dns/message.h>
+#include <resolve/resolver_interface.h>
+
namespace isc {
namespace resolve {
@@ -31,7 +33,7 @@ namespace resolve {
/// as the server itself should also have a reference.
class ResolverCallbackServer : public ResolverInterface::Callback {
public:
- ResolverCallbackServer(asiolink::DNSServer* server) :
+ ResolverCallbackServer(asiodns::DNSServer* server) :
server_(server->clone()) {}
~ResolverCallbackServer() { delete server_; };
@@ -39,7 +41,7 @@ public:
void failure();
private:
- asiolink::DNSServer* server_;
+ asiodns::DNSServer* server_;
};
} //namespace resolve
diff --git a/src/lib/resolve/resolver_interface.h b/src/lib/resolve/resolver_interface.h
index e08bb64..1d01e90 100644
--- a/src/lib/resolve/resolver_interface.h
+++ b/src/lib/resolve/resolver_interface.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 CZ NIC
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
diff --git a/src/lib/resolve/tests/Makefile.am b/src/lib/resolve/tests/Makefile.am
index da28f78..ee311a6 100644
--- a/src/lib/resolve/tests/Makefile.am
+++ b/src/lib/resolve/tests/Makefile.am
@@ -11,9 +11,11 @@ CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
TESTS += run_unittests
+
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_SOURCES = run_unittests.cc
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += resolve_unittest.cc
@@ -23,12 +25,15 @@ run_unittests_SOURCES += recursive_query_unittest.cc
run_unittests_SOURCES += recursive_query_unittest_2.cc
run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/resolve/tests/recursive_query_unittest.cc b/src/lib/resolve/tests/recursive_query_unittest.cc
index ab1ffa3..4e939fa 100644
--- a/src/lib/resolve/tests/recursive_query_unittest.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest.cc
@@ -30,11 +30,14 @@
#include <dns/tests/unittest_util.h>
#include <dns/rcode.h>
-#include <dns/buffer.h>
+#include <util/buffer.h>
+#include <util/unittests/resolver.h>
#include <dns/message.h>
+#include <dns/rdataclass.h>
#include <nsas/nameserver_address_store.h>
#include <cache/resolver_cache.h>
+#include <resolve/resolve.h>
// IMPORTANT: We shouldn't directly use ASIO definitions in this test.
// In particular, we must not include asio.hpp in this file.
@@ -45,17 +48,31 @@
// If we need to test something at the level of underlying ASIO and need
// their definition, that test should go to asiolink/internal/tests.
#include <resolve/recursive_query.h>
+#include <asiodns/dns_lookup.h>
#include <asiolink/io_socket.h>
#include <asiolink/io_service.h>
#include <asiolink/io_message.h>
#include <asiolink/io_error.h>
-#include <asiolink/dns_lookup.h>
#include <asiolink/simple_callback.h>
using isc::UnitTestUtil;
using namespace std;
-using namespace asiolink;
+using namespace isc::asiodns;
+using namespace isc::asiolink;
using namespace isc::dns;
+using namespace isc::util;
+
+namespace isc {
+namespace asiodns {
+
+// This is defined in recursive_query.cc, but not in header (it's not public
+// function). So bring it in to be tested.
+std::string
+deepestDelegation(Name name, RRClass rrclass,
+ isc::cache::ResolverCache& cache);
+
+}
+}
namespace {
const char* const TEST_SERVER_PORT = "53535";
@@ -108,6 +125,9 @@ class RecursiveQueryTest : public ::testing::Test {
protected:
RecursiveQueryTest();
~RecursiveQueryTest() {
+ // It would delete itself, but after the io_service_, which could
+ // segfailt in case there were unhandled requests
+ resolver_.reset();
if (res_ != NULL) {
freeaddrinfo(res_);
}
@@ -316,10 +336,10 @@ protected:
private:
// Currently unused; these will be used for testing
// asynchronous lookup calls via the asyncLookup() method
- boost::shared_ptr<asiolink::IOMessage> io_message_;
+ boost::shared_ptr<isc::asiolink::IOMessage> io_message_;
isc::dns::MessagePtr message_;
isc::dns::MessagePtr answer_message_;
- isc::dns::OutputBufferPtr respbuf_;
+ isc::util::OutputBufferPtr respbuf_;
// Callback functions provided by the caller
const SimpleCallback* checkin_;
@@ -346,12 +366,6 @@ protected:
private:
bool* done_;
};
-
- class MockResolver : public isc::resolve::ResolverInterface {
- void resolve(const QuestionPtr& question,
- const ResolverInterface::CallbackPtr& callback) {
- }
- };
// This version of mock server just stops the io_service when it is resumed
// the second time. (Used in the clientTimeout test, where resume
@@ -421,16 +435,17 @@ protected:
vector<uint8_t> callback_data_;
int sock_;
struct addrinfo* res_;
+ boost::shared_ptr<isc::util::unittests::TestResolver> resolver_;
};
RecursiveQueryTest::RecursiveQueryTest() :
dns_service_(NULL), callback_(NULL), callback_protocol_(0),
- callback_native_(-1), sock_(-1), res_(NULL)
+ callback_native_(-1), sock_(-1), res_(NULL),
+ resolver_(new isc::util::unittests::TestResolver())
{
io_service_ = new IOService();
setDNSService(true, true);
- boost::shared_ptr<MockResolver>mock_resolver(new MockResolver());
- nsas_ = new isc::nsas::NameserverAddressStore(mock_resolver);
+ nsas_ = new isc::nsas::NameserverAddressStore(resolver_);
}
TEST_F(RecursiveQueryTest, v6UDPSend) {
@@ -565,9 +580,12 @@ TEST_F(RecursiveQueryTest, forwarderSend) {
singleAddress(TEST_IPV4_ADDR, port));
Question q(Name("example.com"), RRClass::IN(), RRType::TXT());
+ Message query_message(Message::RENDER);
+ isc::resolve::initResponseMessage(q, query_message);
+
OutputBufferPtr buffer(new OutputBuffer(0));
MessagePtr answer(new Message(Message::RENDER));
- rq.resolve(q, answer, buffer, &server);
+ rq.forward(ConstMessagePtr(&query_message), answer, buffer, &server);
char data[4096];
size_t size = sizeof(data);
@@ -632,8 +650,41 @@ bool tryRead(int sock_, int recv_options, size_t max, int* num) {
return true;
}
+// Mock resolver callback for testing forward query.
+class MockResolverCallback : public isc::resolve::ResolverInterface::Callback {
+public:
+ enum ResultValue {
+ DEFAULT = 0,
+ SUCCESS = 1,
+ FAILURE = 2
+ };
+
+ MockResolverCallback(DNSServer* server):
+ result(DEFAULT),
+ server_(server->clone())
+ {}
-// Test it tries the correct amount of times before giving up
+ ~MockResolverCallback() {
+ delete server_;
+ }
+
+ void success(const isc::dns::MessagePtr response) {
+ result = SUCCESS;
+ server_->resume(true);
+ }
+
+ void failure() {
+ result = FAILURE;
+ server_->resume(false);
+ }
+
+ uint32_t result;
+private:
+ DNSServer* server_;
+};
+
+// Test query timeout, set query timeout is lower than client timeout
+// and lookup timeout.
TEST_F(RecursiveQueryTest, forwardQueryTimeout) {
// Prepare the service (we do not use the common setup, we do not answer
setDNSService();
@@ -655,26 +706,20 @@ TEST_F(RecursiveQueryTest, forwardQueryTimeout) {
Question question(Name("example.net"), RRClass::IN(), RRType::A());
OutputBufferPtr buffer(new OutputBuffer(0));
MessagePtr answer(new Message(Message::RENDER));
- query.resolve(question, answer, buffer, &server);
+ Message query_message(Message::RENDER);
+ isc::resolve::initResponseMessage(question, query_message);
+ boost::shared_ptr<MockResolverCallback> callback(new MockResolverCallback(&server));
+ query.forward(ConstMessagePtr(&query_message), answer, buffer, &server, callback);
// Run the test
io_service_->run();
-
- // Read up to 3 packets. Use some ad hoc timeout to prevent an infinite
- // block (see also recvUDP()).
- int recv_options = setSocketTimeout(sock_, 10, 0);
- int num = 0;
- bool read_success = tryRead(sock_, recv_options, 3, &num);
-
- // The query should 'succeed' with an error response
- EXPECT_TRUE(done);
- EXPECT_EQ(3, num);
- EXPECT_TRUE(read_success);
+ EXPECT_EQ(callback->result, MockResolverCallback::FAILURE);
}
// If we set client timeout to lower than querytimeout, we should
-// get a failure answer, but still see retries
-// (no actual answer is given here yet)
+// get a failure answer
+// (no actual answer is given here yet. TODO the returned error message
+// should be tested)
TEST_F(RecursiveQueryTest, forwardClientTimeout) {
// Prepare the service (we do not use the common setup, we do not answer
setDNSService();
@@ -689,36 +734,25 @@ TEST_F(RecursiveQueryTest, forwardClientTimeout) {
// Do the answer
const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
- // Set it up to retry twice before client timeout fires
- // Since the lookup timer has not fired, it should retry
- // four times
RecursiveQuery query(*dns_service_,
*nsas_, cache_,
singleAddress(TEST_IPV4_ADDR, port),
singleAddress(TEST_IPV4_ADDR, port),
- 200, 480, 4000, 4);
- Question question(Name("example.net"), RRClass::IN(), RRType::A());
+ 1000, 10, 4000, 4);
+ Question q(Name("example.net"), RRClass::IN(), RRType::A());
OutputBufferPtr buffer(new OutputBuffer(0));
- query.resolve(question, answer, buffer, &server);
+ Message query_message(Message::RENDER);
+ isc::resolve::initResponseMessage(q, query_message);
+ boost::shared_ptr<MockResolverCallback> callback(new MockResolverCallback(&server));
+ query.forward(ConstMessagePtr(&query_message), answer, buffer, &server, callback);
// Run the test
io_service_->run();
-
- // we know it'll fail, so make it a shorter timeout
- int recv_options = setSocketTimeout(sock_, 1, 0);
-
- // Try to read 4 times
- int num = 0;
- bool read_success = tryRead(sock_, recv_options, 4, &num);
-
- // The query should fail
- EXPECT_TRUE(done1);
- EXPECT_EQ(3, num);
- EXPECT_FALSE(read_success);
+ EXPECT_EQ(callback->result, MockResolverCallback::FAILURE);
}
-// If we set lookup timeout to lower than querytimeout*retries, we should
-// fail before the full amount of retries
+// If we set lookup timeout to lower than querytimeout, the lookup
+// will fail.
TEST_F(RecursiveQueryTest, forwardLookupTimeout) {
// Prepare the service (we do not use the common setup, we do not answer
setDNSService();
@@ -734,30 +768,22 @@ TEST_F(RecursiveQueryTest, forwardLookupTimeout) {
// Do the answer
const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
- // Set up the test so that it will retry 5 times, but the lookup
- // timeout will fire after only 3 normal timeouts
RecursiveQuery query(*dns_service_,
*nsas_, cache_,
singleAddress(TEST_IPV4_ADDR, port),
singleAddress(TEST_IPV4_ADDR, port),
- 200, 4000, 480, 5);
+ 1000, 4000, 10, 5);
Question question(Name("example.net"), RRClass::IN(), RRType::A());
OutputBufferPtr buffer(new OutputBuffer(0));
- query.resolve(question, answer, buffer, &server);
+ Message query_message(Message::RENDER);
+ isc::resolve::initResponseMessage(question, query_message);
+
+ boost::shared_ptr<MockResolverCallback> callback(new MockResolverCallback(&server));
+ query.forward(ConstMessagePtr(&query_message), answer, buffer, &server, callback);
// Run the test
io_service_->run();
-
- int recv_options = setSocketTimeout(sock_, 1, 0);
-
- // Try to read 5 times, should stop after 3 reads
- int num = 0;
- bool read_success = tryRead(sock_, recv_options, 5, &num);
-
- // The query should fail and respond with an error
- EXPECT_TRUE(done);
- EXPECT_EQ(3, num);
- EXPECT_FALSE(read_success);
+ EXPECT_EQ(callback->result, MockResolverCallback::FAILURE);
}
// Set everything very low and see if this doesn't cause weird
@@ -777,8 +803,6 @@ TEST_F(RecursiveQueryTest, lowtimeouts) {
// Do the answer
const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
- // Set up the test so that it will retry 5 times, but the lookup
- // timeout will fire after only 3 normal timeouts
RecursiveQuery query(*dns_service_,
*nsas_, cache_,
singleAddress(TEST_IPV4_ADDR, port),
@@ -786,21 +810,15 @@ TEST_F(RecursiveQueryTest, lowtimeouts) {
1, 1, 1, 1);
Question question(Name("example.net"), RRClass::IN(), RRType::A());
OutputBufferPtr buffer(new OutputBuffer(0));
- query.resolve(question, answer, buffer, &server);
+ Message query_message(Message::RENDER);
+ isc::resolve::initResponseMessage(question, query_message);
+
+ boost::shared_ptr<MockResolverCallback> callback(new MockResolverCallback(&server));
+ query.forward(ConstMessagePtr(&query_message), answer, buffer, &server, callback);
// Run the test
io_service_->run();
-
- int recv_options = setSocketTimeout(sock_, 1, 0);
-
- // Try to read 5 times, should stop after 3 reads
- int num = 0;
- bool read_success = tryRead(sock_, recv_options, 5, &num);
-
- // The query should fail and respond with an error
- EXPECT_TRUE(done);
- EXPECT_EQ(1, num);
- EXPECT_FALSE(read_success);
+ EXPECT_EQ(callback->result, MockResolverCallback::FAILURE);
}
// as mentioned above, we need a more better framework for this,
@@ -855,7 +873,88 @@ TEST_F(RecursiveQueryTest, DISABLED_recursiveSendNXDOMAIN) {
EXPECT_EQ(0, answer->getRRCount(Message::SECTION_ANSWER));
}
+// Test that we don't start at root when we have a lower NS cached.
+TEST_F(RecursiveQueryTest, CachedNS) {
+ setDNSService(true, true);
+
+ // Check we have a reasonable fallback - if there's nothing of interest
+ // in the cache, start at root.
+ EXPECT_EQ(".", deepestDelegation(Name("www.somewhere.deep.example.org"),
+ RRClass::IN(), cache_));
+
+ // Prefill the cache. There's a zone with a NS and IP address for one
+ // of them (to see that one is enough) and another deeper one, with NS,
+ // but without IP.
+ RRsetPtr nsUpper(new RRset(Name("example.org"), RRClass::IN(),
+ RRType::NS(), RRTTL(300)));
+ nsUpper->addRdata(rdata::generic::NS(Name("ns.example.org")));
+ nsUpper->addRdata(rdata::generic::NS(Name("ns2.example.org")));
+
+ RRsetPtr nsLower(new RRset(Name("somewhere.deep.example.org"),
+ RRClass::IN(), RRType::NS(), RRTTL(300)));
+ nsLower->addRdata(rdata::generic::NS(Name("ns.somewhere.deep.example.org"))
+ );
+
+ RRsetPtr nsIp(new RRset(Name("ns2.example.org"), RRClass::IN(),
+ RRType::A(), RRTTL(300)));
+ nsIp->addRdata(rdata::in::A("192.0.2.1"));
+
+ // Make sure the test runs in the correct environment (we don't test
+ // the cache, but we need it to unswer this way for the test, so we
+ // just make sure)
+ ASSERT_TRUE(cache_.update(nsUpper));
+ ASSERT_TRUE(cache_.update(nsLower));
+ ASSERT_TRUE(cache_.update(nsIp));
+ RRsetPtr deepest(cache_.lookupDeepestNS(Name(
+ "www.somewhere.deep.example.org"), RRClass::IN()));
+ ASSERT_NE(RRsetPtr(), deepest);
+ ASSERT_EQ(nsLower->getName(), deepest->getName());
+
+ // Direct check of the function that chooses the delegation point
+ // It should not use nsLower, because we don't have IP address for
+ // that one. But it can choose nsUpper.
+ EXPECT_EQ("example.org.",
+ deepestDelegation(Name("www.somewhere.deep.example.org"),
+ RRClass::IN(), cache_));
+
+ // Now more complex and indirect test:
+ // We ask it to resolve the name for us. It will pick up a delegation
+ // point and ask NSAS for it. NSAS will in turn ask resolver for NS record
+ // of the delegation point. We then pick it up from the fake resolver
+ // and check it is the correct one. This checks the delegation point
+ // travels safely trough the whole path there (it would be enough to check
+ // it up to NSAS, but replacing NSAS is more complicated, so we just
+ // include in the test as well for simplicity).
+
+ // Prepare the recursive query
+ vector<pair<string, uint16_t> > roots;
+ roots.push_back(pair<string, uint16_t>("192.0.2.2", 53));
+
+ RecursiveQuery rq(*dns_service_, *nsas_, cache_,
+ vector<pair<string, uint16_t> >(), roots);
+ // Ask a question at the bottom. It should not use the lower NS, because
+ // it would lead to a loop in NS. But it can use the nsUpper one, it has
+ // an IP address and we can avoid asking root.
+ Question q(Name("www.somewhere.deep.example.org"), RRClass::IN(),
+ RRType::A());
+ OutputBufferPtr buffer(new OutputBuffer(0));
+ MessagePtr answer(new Message(Message::RENDER));
+ // The server is here so we have something to pass there
+ MockServer server(*io_service_);
+ rq.resolve(q, answer, buffer, &server);
+ // We don't need to run the service in this test. We are interested only
+ // in the place it starts resolving at
+
+ // Look what is asked by NSAS - it should be our delegation point.
+ EXPECT_NO_THROW(EXPECT_EQ(nsUpper->getName(),
+ (*resolver_)[0]->getName()) <<
+ "It starts resolving at the wrong place") <<
+ "It does not ask NSAS anything, how does it know where to send?";
+}
+
// 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 12ff3cf..3e62336 100644
--- a/src/lib/resolve/tests/recursive_query_unittest_2.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest_2.cc
@@ -17,14 +17,16 @@
#include <iomanip>
#include <iostream>
#include <string>
+#include <vector>
#include <gtest/gtest.h>
#include <boost/bind.hpp>
-
#include <asio.hpp>
-#include <dns/buffer.h>
+#include <util/buffer.h>
+#include <util/io_utilities.h>
+
#include <dns/question.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
@@ -36,19 +38,21 @@
#include <dns/rrttl.h>
#include <dns/rdata.h>
-#include <asiolink/asiolink_utilities.h>
-#include <asiolink/dns_service.h>
+#include <util/io_utilities.h>
+#include <asiodns/dns_service.h>
+#include <asiodns/io_fetch.h>
#include <asiolink/io_address.h>
#include <asiolink/io_endpoint.h>
-#include <asiolink/io_fetch.h>
#include <asiolink/io_service.h>
#include <resolve/recursive_query.h>
#include <resolve/resolver_interface.h>
using namespace asio;
using namespace asio::ip;
+using namespace isc::asiolink;
using namespace isc::dns;
using namespace isc::dns::rdata;
+using namespace isc::util;
using namespace isc::resolve;
using namespace std;
@@ -72,7 +76,8 @@ using namespace std;
/// directed to one or other of the "servers" in the RecursiveQueryTest2 class,
/// regardless of the glue returned in referrals.
-namespace asiolink {
+namespace isc {
+namespace asiodns {
const std::string TEST_ADDRESS = "127.0.0.1"; ///< Servers are on this address
const uint16_t TEST_PORT = 5301; ///< ... and this port
@@ -85,9 +90,12 @@ const char* WWW_EXAMPLE_ORG = "192.0.2.254"; ///< Address of www.example.org
const bool DEBUG_PRINT = false;
class MockResolver : public isc::resolve::ResolverInterface {
- void resolve(const QuestionPtr& question,
+public:
+ virtual void resolve(const QuestionPtr& question,
const ResolverInterface::CallbackPtr& callback) {
}
+
+ virtual ~MockResolver() {}
};
@@ -105,8 +113,10 @@ public:
UDP_ROOT = 1, ///< Query root server over UDP
UDP_ORG = 2, ///< Query ORG server over UDP
TCP_ORG = 3, ///< Query ORG server over TCP
- UDP_EXAMPLE_ORG = 4, ///< Query EXAMPLE.ORG server over UDP
- COMPLETE = 5 ///< Query is complete
+ UDP_EXAMPLE_ORG_BAD = 4, ///< Query EXAMPLE.ORG server over UDP
+ ///< (return malformed packet)
+ UDP_EXAMPLE_ORG = 5, ///< Query EXAMPLE.ORG server over UDP
+ COMPLETE = 6 ///< Query is complete
};
// Common stuff
@@ -117,8 +127,9 @@ public:
QueryStatus last_; ///< What was the last state
QueryStatus expected_; ///< Expected next state
OutputBufferPtr question_buffer_; ///< Question we expect to receive
- isc::nsas::NameserverAddressStore* nsas_;
- isc::cache::ResolverCache cache_;
+ boost::shared_ptr<MockResolver> resolver_; ///< Mock resolver
+ isc::nsas::NameserverAddressStore* nsas_; ///< Nameserver address store
+ isc::cache::ResolverCache cache_; ///< Resolver cache
// Data for TCP Server
size_t tcp_cumulative_; ///< Cumulative TCP data received
@@ -144,6 +155,8 @@ public:
last_(NONE),
expected_(NONE),
question_buffer_(new OutputBuffer(BUFFER_SIZE)),
+ resolver_(new MockResolver()),
+ nsas_(new isc::nsas::NameserverAddressStore(resolver_)),
tcp_cumulative_(0),
tcp_endpoint_(asio::ip::address::from_string(TEST_ADDRESS), TEST_PORT),
tcp_length_(0),
@@ -156,8 +169,6 @@ public:
udp_send_buffer_(new OutputBuffer(BUFFER_SIZE)),
udp_socket_(service_.get_io_service(), udp::v4())
{
- boost::shared_ptr<MockResolver>mock_resolver(new MockResolver());
- nsas_ = new isc::nsas::NameserverAddressStore(mock_resolver);
}
/// \brief Set Common Message Bits
@@ -165,7 +176,7 @@ public:
/// Sets up the common bits of a response message returned by the handlers.
///
/// \param msg Message buffer in RENDER mode.
- /// \param qid QIT to set the message to
+ /// \param qid QID to set the message to
void setCommonMessage(isc::dns::Message& msg, uint16_t qid = 0) {
msg.setQid(qid);
msg.setHeaderFlag(Message::HEADERFLAG_QR);
@@ -278,17 +289,18 @@ public:
// The QID in the incoming data is random so set it to 0 for the
// data comparison check. (It is set to 0 in the buffer containing
// the expected data.)
- uint16_t qid = readUint16(udp_receive_buffer_);
- udp_receive_buffer_[0] = udp_receive_buffer_[1] = 0;
-
- // Check that question we received is what was expected.
- checkReceivedPacket(udp_receive_buffer_, length);
+ // And check that question we received is what was expected.
+ uint16_t qid = checkReceivedPacket(udp_receive_buffer_, length);
// The message returned depends on what state we are in. Set up
// common stuff first: bits not mentioned are set to 0.
Message msg(Message::RENDER);
setCommonMessage(msg, qid);
+ // In the case of UDP_EXAMPLE_ORG_BAD, we shall mangle the
+ // response
+ bool mangle_response = false;
+
// Set up state-dependent bits:
switch (expected_) {
case UDP_ROOT:
@@ -309,6 +321,14 @@ public:
expected_ = TCP_ORG;
break;
+ case UDP_EXAMPLE_ORG_BAD:
+ // Return the answer to the question.
+ setAnswerWwwExampleOrg(msg);
+ // Mangle the response to enfore another query
+ mangle_response = true;
+ expected_ = UDP_EXAMPLE_ORG;
+ break;
+
case UDP_EXAMPLE_ORG:
// Return the answer to the question.
setAnswerWwwExampleOrg(msg);
@@ -324,6 +344,12 @@ public:
MessageRenderer renderer(*udp_send_buffer_);
msg.toWire(renderer);
+ if (mangle_response) {
+ // mangle the packet a bit
+ // set additional to one more
+ udp_send_buffer_->writeUint8At(3, 11);
+ }
+
// Return a message back to the IOFetch object (after setting the
// expected length of data for the check in the send handler).
udp_length_ = udp_send_buffer_->getLength();
@@ -433,18 +459,20 @@ public:
// Check that question we received is what was expected. Note that we
// have to ignore the two-byte header in order to parse the message.
- checkReceivedPacket(tcp_receive_buffer_ + 2, length - 2);
+ qid_t qid = checkReceivedPacket(tcp_receive_buffer_ + 2, length - 2);
// Return a message back. This is a referral to example.org, which
// should result in another query over UDP. Note the setting of the
// QID in the returned message with what was in the received message.
Message msg(Message::RENDER);
- setCommonMessage(msg, readUint16(tcp_receive_buffer_));
+ setCommonMessage(msg, qid);
setReferralExampleOrg(msg);
// Convert to wire format
- tcp_send_buffer_->clear();
- MessageRenderer renderer(*tcp_send_buffer_);
+ // Use a temporary buffer for the dns wire data (we copy it
+ // to the 'real' buffer below)
+ OutputBuffer msg_buf(BUFFER_SIZE);
+ MessageRenderer renderer(msg_buf);
msg.toWire(renderer);
// Expected next state (when checked) is the UDP query to example.org.
@@ -452,19 +480,16 @@ public:
// readiness for the next read. (If any - at present, there is only
// one read in the test, although extensions to this test suite could
// change that.)
- expected_ = UDP_EXAMPLE_ORG;
+ expected_ = UDP_EXAMPLE_ORG_BAD;
tcp_cumulative_ = 0;
- // We'll write the message in two parts, the count and the message
- // itself. This saves having to prepend the count onto the start of a
- // buffer. When specifying the send handler, the expected size of the
- // data written is passed as the first parameter so that the handler
- // can check it.
- uint8_t count[2];
- writeUint16(tcp_send_buffer_->getLength(), count);
- tcp_socket_.async_send(asio::buffer(count, 2),
- boost::bind(&RecursiveQueryTest2::tcpSendHandler, this,
- 2, _1, _2));
+ // Unless we go through a callback loop we cannot simply use
+ // async_send() multiple times, so we cannot send the size first
+ // followed by the actual data. We copy them to a new buffer
+ // first
+ tcp_send_buffer_->clear();
+ tcp_send_buffer_->writeUint16(msg_buf.getLength());
+ tcp_send_buffer_->writeData(msg_buf.getData(), msg_buf.getLength());
tcp_socket_.async_send(asio::buffer(tcp_send_buffer_->getData(),
tcp_send_buffer_->getLength()),
boost::bind(&RecursiveQueryTest2::tcpSendHandler, this,
@@ -502,7 +527,8 @@ public:
/// the case of UDP data, and an offset into the buffer past the
/// count field for TCP data.
/// \param length Length of data.
- void checkReceivedPacket(uint8_t* data, size_t length) {
+ /// \return The QID of the message
+ qid_t checkReceivedPacket(uint8_t* data, size_t length) {
// Decode the received buffer.
InputBuffer buffer(data, length);
@@ -514,6 +540,8 @@ public:
Question question = **(message.beginQuestion());
EXPECT_TRUE(question == *question_);
+
+ return message.getQid();
}
};
@@ -539,6 +567,7 @@ public:
virtual void success(const isc::dns::MessagePtr response) {
if (debug_) {
cout << "ResolverCallback::success(): answer received" << endl;
+ cout << response->toText() << endl;
}
// There should be one RR each in the question and answer sections, and
@@ -607,7 +636,6 @@ private:
// Sets up the UDP and TCP "servers", then tries a resolution.
TEST_F(RecursiveQueryTest2, Resolve) {
-
// Set up the UDP server and issue the first read. The endpoint from which
// the query is sent is put in udp_endpoint_ when the read completes, which
// is referenced in the callback as the place to which the response is sent.
@@ -627,15 +655,18 @@ TEST_F(RecursiveQueryTest2, Resolve) {
boost::bind(&RecursiveQueryTest2::tcpAcceptHandler,
this, _1, 0));
- // Set up the RecursiveQuery object.
+ // Set up the RecursiveQuery object. We will also test that it correctly records
+ // RTT times by setting up a RTT recorder object as well.
std::vector<std::pair<std::string, uint16_t> > upstream; // Empty
std::vector<std::pair<std::string, uint16_t> > upstream_root; // Empty
RecursiveQuery query(dns_service_, *nsas_, cache_,
upstream, upstream_root);
query.setTestServer(TEST_ADDRESS, TEST_PORT);
- // Set up callback for the tor eceive notification that the query has
- // completed.
+ boost::shared_ptr<RttRecorder> recorder(new RttRecorder());
+ query.setRttRecorder(recorder);
+
+ // Set up callback to receive notification that the query has completed.
isc::resolve::ResolverInterface::CallbackPtr
resolver_callback(new ResolverCallback(service_));
@@ -651,6 +682,17 @@ TEST_F(RecursiveQueryTest2, Resolve) {
ResolverCallback* rc = static_cast<ResolverCallback*>(resolver_callback.get());
EXPECT_TRUE(rc->getRun());
EXPECT_TRUE(rc->getStatus());
+
+ // Finally, check that all the RTTs were "reasonable" (defined here as
+ // being below 2 seconds). This is an explicit check to test that the
+ // variables in the RTT calculation are at least being initialized; if they
+ // weren't, we would expect some absurdly high answers.
+ vector<uint32_t> rtt = recorder->getRtt();
+ EXPECT_GT(rtt.size(), 0);
+ for (int i = 0; i < rtt.size(); ++i) {
+ EXPECT_LT(rtt[i], 2000);
+ }
}
-} // namespace asiolink
+} // namespace asiodns
+} // namespace isc
diff --git a/src/lib/resolve/tests/resolver_callback_unittest.cc b/src/lib/resolve/tests/resolver_callback_unittest.cc
index 6370e22..e94f13d 100644
--- a/src/lib/resolve/tests/resolver_callback_unittest.cc
+++ b/src/lib/resolve/tests/resolver_callback_unittest.cc
@@ -13,8 +13,8 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <asiodns/dns_server.h>
#include <resolve/resolver_callback.h>
-#include <asiolink/asiolink.h>
using namespace isc::resolve;
@@ -22,7 +22,7 @@ using namespace isc::resolve;
// We want to check if resume is called
// Since the server will get cloned(), we want the clones to share
// our bools for whether resume got called and with what value
-class DummyServer : public asiolink::DNSServer {
+class DummyServer : public isc::asiodns::DNSServer {
public:
DummyServer(DummyServer* orig) {
resume_called_ = orig->getResumeCalled();
@@ -31,10 +31,10 @@ public:
DummyServer(bool* resume_called, bool* resume_value) :
resume_called_(resume_called), resume_value_(resume_value)
{}
-
+
bool* getResumeCalled() { return resume_called_; }
bool* getResumeValue() { return resume_value_; }
-
+
DNSServer* clone() {
DummyServer* n = new DummyServer(this);
return n;
diff --git a/src/lib/resolve/tests/run_unittests.cc b/src/lib/resolve/tests/run_unittests.cc
index f80e167..fe8124e 100644
--- a/src/lib/resolve/tests/run_unittests.cc
+++ b/src/lib/resolve/tests/run_unittests.cc
@@ -13,12 +13,15 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
+#include <log/logger_support.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
+ isc::log::initLogger();
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/server_common/Makefile.am b/src/lib/server_common/Makefile.am
index dfb3014..c2779b4 100644
--- a/src/lib/server_common/Makefile.am
+++ b/src/lib/server_common/Makefile.am
@@ -17,10 +17,23 @@ AM_CXXFLAGS += -Wno-unused-parameter
endif
lib_LTLIBRARIES = libserver_common.la
-libserver_common_la_SOURCES = portconfig.h portconfig.cc
+libserver_common_la_SOURCES = client.h client.cc
+libserver_common_la_SOURCES += keyring.h keyring.cc
+libserver_common_la_SOURCES += portconfig.h portconfig.cc
+libserver_common_la_SOURCES += logger.h logger.cc
+nodist_libserver_common_la_SOURCES = server_common_messages.h
+nodist_libserver_common_la_SOURCES += server_common_messages.cc
libserver_common_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
libserver_common_la_LIBADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
libserver_common_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la
+libserver_common_la_LIBADD += $(top_builddir)/src/lib/config/libcfgclient.la
libserver_common_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la
+libserver_common_la_LIBADD += $(top_builddir)/src/lib/acl/libacl.la
+libserver_common_la_LIBADD += $(top_builddir)/src/lib/dns/libdns++.la
+BUILT_SOURCES = server_common_messages.h server_common_messages.cc
+server_common_messages.h server_common_messages.cc: server_common_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/server_common/server_common_messages.mes
-CLEANFILES = *.gcno *.gcda
+EXTRA_DIST = server_common_messages.mes
+
+CLEANFILES = *.gcno *.gcda server_common_messages.h server_common_messages.cc
diff --git a/src/lib/server_common/client.cc b/src/lib/server_common/client.cc
new file mode 100644
index 0000000..e6383d6
--- /dev/null
+++ b/src/lib/server_common/client.cc
@@ -0,0 +1,68 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+#include <sstream>
+
+#include <acl/ip_check.h>
+
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_message.h>
+
+#include <server_common/client.h>
+
+using namespace isc::acl;
+using namespace isc::server_common;
+using namespace isc::asiolink;
+
+struct Client::ClientImpl {
+ ClientImpl(const IOMessage& request_message) :
+ request_(request_message),
+ request_src_(request_.getRemoteEndpoint().getSockAddr())
+ {}
+
+ const IOMessage& request_;
+ const IPAddress request_src_;
+};
+
+Client::Client(const IOMessage& request_message) :
+ impl_(new ClientImpl(request_message))
+{}
+
+Client::~Client() {
+ delete impl_;
+}
+
+const IOEndpoint&
+Client::getRequestSourceEndpoint() const {
+ return (impl_->request_.getRemoteEndpoint());
+}
+
+const IPAddress&
+Client::getRequestSourceIPAddress() const {
+ return (impl_->request_src_);
+}
+
+std::string
+Client::toText() const {
+ std::stringstream ss;
+ ss << impl_->request_.getRemoteEndpoint().getAddress().toText()
+ << '#' << impl_->request_.getRemoteEndpoint().getPort();
+ return (ss.str());
+}
+
+std::ostream&
+isc::server_common::operator<<(std::ostream& os, const Client& client) {
+ return (os << client.toText());
+}
diff --git a/src/lib/server_common/client.h b/src/lib/server_common/client.h
new file mode 100644
index 0000000..1c5928a
--- /dev/null
+++ b/src/lib/server_common/client.h
@@ -0,0 +1,154 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __CLIENT_H
+#define __CLIENT_H 1
+
+#include <string>
+#include <ostream>
+
+#include <boost/noncopyable.hpp>
+
+#include <acl/ip_check.h>
+
+namespace isc {
+namespace asiolink {
+class IOMessage;
+class IOEndpoint;
+}
+
+namespace acl {
+struct IPAddress;
+}
+
+namespace server_common {
+
+/// A DNS client with a single request context.
+///
+/// The \c Client class represents a DNS client with information of one
+/// DNS request (e.g., a query). The information includes the source and
+/// destination IP addresses of the request, information of the DNS request
+/// message such as the query name or (if provided) TSIG key information.
+///
+/// A \c Client class object is expected to be constructed on receiving a
+/// new request with lower level information such as IP addresses and is
+/// updated with DNS specific information as the server processes the request.
+/// It is also expected to be used as the primary interface for request
+/// processing such as query handling or access control.
+///
+/// Furthermore, to minimize the overhead, this class would be further
+/// extended so that it can be reusable with an additional method to reset
+/// the internal information.
+///
+/// In the current initial implementation, however, it only contains the
+/// lower level information in the form of \c IOMessage object and cannot
+/// be reused (it must be constructed for every new request). Also, the
+/// only actual usage of this class at this moment is for ACL handling.
+///
+/// A \c Client class object is generally assumed to be valid throughout
+/// the processing of a single request, and then be destructed or (when
+/// supported) reset. To avoid it is copied and held accidentally beyond
+/// the expected valid period, it is intentionally made non copyable.
+///
+/// Notes about other possibilities: we may want to abstract it further,
+/// so that it can also be used for DHCP. In that case, we'd subclass a
+/// base client class for DNS specific clients and DHCP specific clients.
+/// We might also want to separate DNS clients for authoritative servers
+/// and clients for the resolver, especially because the former could be
+/// simpler with performance optimizations.
+class Client : boost::noncopyable {
+public:
+ ///
+ /// \name Constructors and Destructor
+ ///
+ //@{
+ /// The constructor.
+ ///
+ /// This initial version of constructor takes an \c IOMessage object
+ /// that is supposed to represent a DNS request message sent from an
+ /// external client (but the constructor does not perform any assumption
+ /// check on the given \c IOMessage).
+ ///
+ /// If and when we extend the behavior and responsibility
+ /// of this class, this version of constructor will probably be
+ /// deprecated.
+ ///
+ /// \c request_message must be valid throughout the lifetime of the client.
+ ///
+ /// \exception None
+ /// \param request_message Refers to \c IOMessage corresponding to some
+ /// DNS request message.
+ explicit Client(const isc::asiolink::IOMessage& request_message);
+
+ /// The destructor
+ ~Client();
+ //@}
+
+ /// Return the client's endpoint of the request.
+ ///
+ /// This should be identical to the result of \c getRemoteEndpoint()
+ /// called on \c request_message passed to the constructor.
+ ///
+ /// \exception None
+ const isc::asiolink::IOEndpoint& getRequestSourceEndpoint() const;
+
+ /// Return the IP address part of the client request's endpoint.
+ ///
+ /// The resulting \c IPAddress can be constructed using
+ /// \c getRequestSourceEndpoint(), and in that sense this method is
+ /// redundant. But this implementation internally constructs the
+ /// \c IPAddress on construction and always returns a reference to it,
+ /// and should be more efficient. It is provided so that it can be
+ /// called multiple times in a complicated ACL with minimum cost.
+ ///
+ /// \exception None
+ const isc::acl::IPAddress& getRequestSourceIPAddress() const;
+
+ /// Convert the Client to a string.
+ ///
+ /// (In the initial implementation) the format of the resulting string
+ /// is as follows:
+ /// \code <IP address>#<port>
+ /// \endcode
+ /// The IP address is the textual representation of the client's IP
+ /// address, which is the source address of the request the client has
+ /// sent. The port is the UDP or TCP of the client's end of the request.
+ ///
+ /// \exception std::bad_alloc Internal resource allocation fails
+ std::string toText() const;
+
+private:
+ struct ClientImpl;
+ ClientImpl* impl_;
+};
+
+/// \brief Insert the \c Client as a string into stream.
+///
+/// This method convert \c client into a string and inserts it into the
+/// output stream \c os.
+///
+/// \param os A \c std::ostream object on which the insertion operation is
+/// performed.
+/// \param edns A reference to an \c Client object output by the operation.
+/// \return A reference to the same \c std::ostream object referenced by
+/// parameter \c os after the insertion operation.
+std::ostream& operator<<(std::ostream& os, const Client& client);
+}
+}
+
+#endif // __CLIENT_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/server_common/keyring.cc b/src/lib/server_common/keyring.cc
new file mode 100644
index 0000000..501dfd9
--- /dev/null
+++ b/src/lib/server_common/keyring.cc
@@ -0,0 +1,71 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <server_common/keyring.h>
+#include <server_common/logger.h>
+
+using namespace isc::dns;
+using namespace isc::data;
+
+namespace isc {
+namespace server_common {
+
+typedef boost::shared_ptr<TSIGKeyRing> KeyringPtr;
+
+KeyringPtr keyring;
+
+namespace {
+
+void
+updateKeyring(const std::string&, ConstElementPtr data,
+ const isc::config::ConfigData&) {
+ ConstElementPtr list(data->get("keys"));
+ KeyringPtr load(new TSIGKeyRing);
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, SRVCOMM_KEYS_UPDATE);
+
+ // Note that 'data' only contains explicitly configured config parameters.
+ // So if we use the default list is NULL, rather than an empty list, and
+ // we must explicitly expect that case (and handle it just like an empty
+ // list).
+ for (size_t i(0); list && i < list->size(); ++ i) {
+ load->add(TSIGKey(list->get(i)->stringValue()));
+ }
+ keyring.swap(load);
+}
+
+}
+
+void
+initKeyring(config::ModuleCCSession& session) {
+ if (keyring) {
+ // We are already initialized
+ return;
+ }
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, SRVCOMM_KEYS_INIT);
+ session.addRemoteConfig("tsig_keys", updateKeyring, false);
+}
+
+void
+deinitKeyring(config::ModuleCCSession& session) {
+ if (!keyring) {
+ // Not initialized, ignore it
+ return;
+ }
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, SRVCOMM_KEYS_DEINIT);
+ keyring.reset();
+ session.removeRemoteConfig("tsig_keys");
+}
+
+}
+}
diff --git a/src/lib/server_common/keyring.h b/src/lib/server_common/keyring.h
new file mode 100644
index 0000000..9c067e9
--- /dev/null
+++ b/src/lib/server_common/keyring.h
@@ -0,0 +1,102 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef ISC_SERVER_COMMON_KEYRING_H
+#define ISC_SERVER_COMMON_KEYRING_H
+
+#include <boost/shared_ptr.hpp>
+#include <dns/tsigkey.h>
+#include <config/ccsession.h>
+
+/**
+ * \file keyring.h
+ * \brief TSIG keyring loaded from configuration.
+ *
+ * This file contains routines for loading a TSIG key ring from
+ * the tsig_keys configuration section and keeping them up to date
+ * on updates.
+ *
+ * You simply initialize/load the keyring with isc::server_common::initKeyring
+ * and then just use the key ring referred to by isc::server_common::keyring. It
+ * is automatically reloaded, when the configuration updates, so you no longer
+ * needs to care about it.
+ *
+ * If you want to keep a key (or session) for longer time or your application
+ * is multithreaded, you might want to have a copy of the shared pointer to
+ * hold a reference. Otherwise an update might replace the keyring and delete
+ * the keys in the old one.
+ *
+ * Also note that, while the interface doesn't prevent application from
+ * modifying the keyring, it is not a good idea to do so. As mentioned above,
+ * it might get reloaded at any time, which would replace the modified keyring.
+ * The possibility to modify it is side effect of simpler implementation and
+ * shorter code, not a goal.
+ */
+
+namespace isc {
+
+namespace server_common {
+
+/**
+ * \brief The key ring itself
+ *
+ * This is where the key ring is stored. You can directly use it to your needs,
+ * but you need to call initKeyring first, otherwise you'll find a NULL pointer
+ * here only.
+ */
+extern boost::shared_ptr<dns::TSIGKeyRing> keyring;
+
+/**
+ * \brief Load the key ring for the first time
+ *
+ * This loads the key ring from configuration to keyring. It also registers for
+ * config updates, so from now on, it'll be kept up to date.
+ *
+ * You can unload the key ring with deinitKeyring.
+ *
+ * If it is already loaded, this function does nothing. So, if more than one
+ * part of an application needs to use the key ring, they all can just call
+ * this independently to ensure the keyring is loaded.
+ *
+ * \param session The configuration session used to talk to the config manager.
+ */
+void
+initKeyring(config::ModuleCCSession& session);
+
+/**
+ * \brief Unload the key ring
+ *
+ * This can be used to unload the key ring. It will reset the keyring to NULL
+ * and stop receiving updates of the configuration.
+ *
+ * The need for this function should be quite rare, as it isn't required to be
+ * called before application shutdown. And not calling it has only small
+ * performance penalty -- the keyring will be kept in memory and updated when
+ * the user changes configuration.
+ *
+ * This does nothing if the key ring is not loaded currently.
+ *
+ * \param session The configuration session used to talk to the config manager.
+ *
+ * \todo What do we do when the data that come are invalid? Should we ignore it,
+ * as walidity should have been checked already in the config manager, or
+ * throw? What about when we get an update and it's invalid?
+ */
+void
+deinitKeyring(config::ModuleCCSession& session);
+
+}
+}
+
+#endif
diff --git a/src/lib/server_common/logger.cc b/src/lib/server_common/logger.cc
new file mode 100644
index 0000000..0b9ab6e
--- /dev/null
+++ b/src/lib/server_common/logger.cc
@@ -0,0 +1,23 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <server_common/logger.h>
+
+namespace isc {
+namespace server_common {
+
+isc::log::Logger logger("server_common");
+
+}
+}
diff --git a/src/lib/server_common/logger.h b/src/lib/server_common/logger.h
new file mode 100644
index 0000000..cfca1f3
--- /dev/null
+++ b/src/lib/server_common/logger.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __SERVER_COMMON_LOGGER_H
+#define __SERVER_COMMON_LOGGER_H
+
+#include <log/macros.h>
+#include <server_common/server_common_messages.h>
+
+/// \file logger.h
+/// \brief Server Common library global logger
+///
+/// This holds the logger for the server common library. It is a private header
+/// and should not be included in any publicly used header, only in local
+/// cc files.
+
+namespace isc {
+namespace server_common {
+
+/// \brief The logger for this library
+extern isc::log::Logger logger;
+
+enum {
+ /// \brief Trace basic operations
+ DBG_TRACE_BASIC = 10,
+ /// \brief Print also values used
+ DBG_TRACE_VALUES = 40
+};
+
+}
+}
+
+#endif
diff --git a/src/lib/server_common/portconfig.cc b/src/lib/server_common/portconfig.cc
index 3765f52..379a0a1 100644
--- a/src/lib/server_common/portconfig.cc
+++ b/src/lib/server_common/portconfig.cc
@@ -13,18 +13,18 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <server_common/portconfig.h>
+#include <server_common/logger.h>
#include <asiolink/io_address.h>
-#include <asiolink/dns_service.h>
-#include <log/dummylog.h>
+#include <asiodns/dns_service.h>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
using namespace std;
using namespace isc::data;
-using namespace asiolink;
-using isc::log::dlog;
+using namespace isc::asiolink;
+using namespace isc::asiodns;
namespace isc {
namespace server_common {
@@ -42,6 +42,8 @@ parseAddresses(isc::data::ConstElementPtr addresses,
ConstElementPtr addr(addrPair->get("address"));
ConstElementPtr port(addrPair->get("port"));
if (!addr || ! port) {
+ LOG_ERROR(logger, SRVCOMM_ADDRESS_MISSING).
+ arg(addrPair->str());
isc_throw(BadValue, "Address must contain both the IP"
"address and port");
}
@@ -49,6 +51,8 @@ parseAddresses(isc::data::ConstElementPtr addresses,
IOAddress(addr->stringValue());
if (port->intValue() < 0 ||
port->intValue() > 0xffff) {
+ LOG_ERROR(logger, SRVCOMM_PORT_RANGE).
+ arg(port->intValue()).arg(addrPair->str());
isc_throw(BadValue, "Bad port value (" <<
port->intValue() << ")");
}
@@ -56,11 +60,14 @@ parseAddresses(isc::data::ConstElementPtr addresses,
port->intValue()));
}
catch (const TypeError &e) { // Better error message
+ LOG_ERROR(logger, SRVCOMM_ADDRESS_TYPE).
+ arg(addrPair->str());
isc_throw(TypeError,
"Address must be a string and port an integer");
}
}
} else if (addresses->getType() != Element::null) {
+ LOG_ERROR(logger, SRVCOMM_ADDRESSES_NOT_LIST).arg(elemName);
isc_throw(TypeError, elemName + " config element must be a list");
}
}
@@ -82,35 +89,41 @@ setAddresses(DNSService& service, const AddressList& addresses) {
void
installListenAddresses(const AddressList& newAddresses,
AddressList& addressStore,
- asiolink::DNSService& service)
+ isc::asiodns::DNSService& service)
{
try {
- dlog("Setting listen addresses:");
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, SRVCOMM_SET_LISTEN);
BOOST_FOREACH(const AddressPair& addr, newAddresses) {
- dlog(" " + addr.first + ":" +
- boost::lexical_cast<string>(addr.second));
+ LOG_DEBUG(logger, DBG_TRACE_VALUES, SRVCOMM_ADDRESS_VALUE).
+ arg(addr.first).arg(addr.second);
}
setAddresses(service, newAddresses);
addressStore = newAddresses;
}
catch (const exception& e) {
/*
- * We couldn't set it. So return it back. If that fails as well,
- * we have a problem.
+ * If one of the addresses isn't set successfully, we will restore
+ * the old addresses, the behavior is that either all address are
+ * set successuflly or none of them will be used. whether this
+ * behavior is user desired, maybe we need revisited it later. And
+ * if address setting is more smarter, it should check whether some
+ * part of the new address already in used to avoid interuption the
+ * service.
*
- * If that fails, bad luck, but we are useless anyway, so just die
- * and let boss start us again.
+ * If the address setting still failed, we can live with it, since
+ * user will get error info, command control can be used to set new
+ * address. So we just catch the exception without propagating outside
*/
- dlog(string("Unable to set new address: ") + e.what(), true);
+ LOG_ERROR(logger, SRVCOMM_ADDRESS_FAIL).arg(e.what());
try {
setAddresses(service, addressStore);
}
catch (const exception& e2) {
- dlog("Unable to recover from error;", true);
- dlog(string("Rollback failed with: ") + e2.what(), true);
- abort();
+ LOG_FATAL(logger, SRVCOMM_ADDRESS_UNRECOVERABLE).arg(e2.what());
}
- throw; // Let it fly a little bit further
+ //Anyway the new configure has problem, we need to notify configure
+ //manager the new configure doesn't work
+ throw;
}
}
diff --git a/src/lib/server_common/portconfig.h b/src/lib/server_common/portconfig.h
index bcb8528..e4e7bf6 100644
--- a/src/lib/server_common/portconfig.h
+++ b/src/lib/server_common/portconfig.h
@@ -25,9 +25,11 @@
/*
* Some forward declarations.
*/
-namespace asiolink {
+namespace isc {
+namespace asiodns {
class DNSService;
}
+}
namespace isc {
namespace server_common {
@@ -112,7 +114,7 @@ parseAddresses(isc::data::ConstElementPtr addresses,
void
installListenAddresses(const AddressList& newAddresses,
AddressList& addressStore,
- asiolink::DNSService& dnsService);
+ asiodns::DNSService& dnsService);
}
}
diff --git a/src/lib/server_common/server_common_messages.mes b/src/lib/server_common/server_common_messages.mes
new file mode 100644
index 0000000..5fbbb0b
--- /dev/null
+++ b/src/lib/server_common/server_common_messages.mes
@@ -0,0 +1,73 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+$NAMESPACE isc::server_common
+
+# \brief Messages for the server_common library
+
+% SRVCOMM_ADDRESSES_NOT_LIST the address and port specification is not a list in %1
+This points to an error in configuration. What was supposed to be a list of
+IP address - port pairs isn't a list at all but something else.
+
+% SRVCOMM_ADDRESS_FAIL failed to listen on addresses (%1)
+The server failed to bind to one of the address/port pair it should according
+to configuration, for reason listed in the message (usually because that pair
+is already used by other service or missing privileges). The server will try
+to recover and bind the address/port pairs it was listening to before (if any).
+
+% SRVCOMM_ADDRESS_MISSING address specification is missing "address" or "port" element in %1
+This points to an error in configuration. An address specification in the
+configuration is missing either an address or port and so cannot be used. The
+specification causing the error is given in the message.
+
+% SRVCOMM_ADDRESS_TYPE address specification type is invalid in %1
+This points to an error in configuration. An address specification in the
+configuration malformed. The specification causing the error is given in the
+message. A valid specification contains an address part (which must be a string
+and must represent a valid IPv4 or IPv6 address) and port (which must be an
+integer in the range valid for TCP/UDP ports on your system).
+
+% SRVCOMM_ADDRESS_UNRECOVERABLE failed to recover original addresses also (%2)
+The recovery of old addresses after SRVCOMM_ADDRESS_FAIL also failed for
+the reason listed.
+
+The condition indicates problems with the server and/or the system on
+which it is running. The server will continue running to allow
+reconfiguration, but will not be listening on any address or port until
+an administrator does so.
+
+% SRVCOMM_ADDRESS_VALUE address to set: %1#%2
+Debug message. This lists one address and port value of the set of
+addresses we are going to listen on (eg. there will be one log message
+per pair). This appears only after SRVCOMM_SET_LISTEN, but might
+be hidden, as it has higher debug level.
+
+% SRVCOMM_KEYS_DEINIT deinitializing TSIG keyring
+Debug message indicating that the server is deinitializing the TSIG keyring.
+
+% SRVCOMM_KEYS_INIT initializing TSIG keyring
+Debug message indicating that the server is initializing the global TSIG
+keyring. This should be seen only at server start.
+
+% SRVCOMM_KEYS_UPDATE updating TSIG keyring
+Debug message indicating new keyring is being loaded from configuration (either
+on startup or as a result of configuration update).
+
+% SRVCOMM_PORT_RANGE port out of valid range (%1 in %2)
+This points to an error in configuration. The port in an address
+specification is outside the valid range of 0 to 65535.
+
+% SRVCOMM_SET_LISTEN setting addresses to listen to
+Debug message, noting that the server is about to start listening on a
+different set of IP addresses and ports than before.
diff --git a/src/lib/server_common/tests/Makefile.am b/src/lib/server_common/tests/Makefile.am
index 55ccc85..d7e113a 100644
--- a/src/lib/server_common/tests/Makefile.am
+++ b/src/lib/server_common/tests/Makefile.am
@@ -12,7 +12,7 @@ endif
# Some versions of GCC warn about some versions of Boost regarding
# missing initializer for members in its posix_time.
# https://svn.boost.org/trac/boost/ticket/3477
-# But older GCC compilers don't have the flag.
+# But older GCC compilers don't have the flag.
AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
if USE_CLANGPP
@@ -26,17 +26,29 @@ TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += client_unittest.cc
run_unittests_SOURCES += portconfig_unittest.cc
+run_unittests_SOURCES += keyring_test.cc
+nodist_run_unittests_SOURCES = data_path.h
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
-
run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/acl/libacl.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
+run_unittests_LDADD += $(top_builddir)/src/lib/config/tests/libfake_session.la
endif
noinst_PROGRAMS = $(TESTS)
+
+EXTRA_DIST = testdata/spec.spec
diff --git a/src/lib/server_common/tests/client_unittest.cc b/src/lib/server_common/tests/client_unittest.cc
new file mode 100644
index 0000000..287a926
--- /dev/null
+++ b/src/lib/server_common/tests/client_unittest.cc
@@ -0,0 +1,103 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <sys/socket.h>
+#include <string.h>
+
+#include <string>
+#include <sstream>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <acl/ip_check.h>
+
+#include <asiolink/io_address.h>
+#include <asiolink/io_socket.h>
+#include <asiolink/io_message.h>
+
+#include <server_common/client.h>
+
+#include <gtest/gtest.h>
+
+using namespace boost;
+using namespace isc::acl;
+using namespace isc::asiolink;
+using namespace isc::server_common;
+
+namespace {
+
+class ClientTest : public ::testing::Test {
+protected:
+ ClientTest() {
+ endpoint4.reset(IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.1"),
+ 53214));
+ endpoint6.reset(IOEndpoint::create(IPPROTO_TCP,
+ IOAddress("2001:db8::1"), 53216));
+ request4.reset(new IOMessage(NULL, 0, IOSocket::getDummyUDPSocket(),
+ *endpoint4));
+ request6.reset(new IOMessage(NULL, 0, IOSocket::getDummyTCPSocket(),
+ *endpoint6));
+ client4.reset(new Client(*request4));
+ client6.reset(new Client(*request6));
+ }
+ scoped_ptr<const IOEndpoint> endpoint4;
+ scoped_ptr<const IOEndpoint> endpoint6;
+ scoped_ptr<const IOMessage> request4;
+ scoped_ptr<const IOMessage> request6;
+ scoped_ptr<const Client> client4;
+ scoped_ptr<const Client> client6;
+};
+
+TEST_F(ClientTest, constructIPv4) {
+ EXPECT_EQ(AF_INET, client4->getRequestSourceEndpoint().getFamily());
+ EXPECT_EQ(IPPROTO_UDP, client4->getRequestSourceEndpoint().getProtocol());
+ EXPECT_EQ("192.0.2.1",
+ client4->getRequestSourceEndpoint().getAddress().toText());
+ EXPECT_EQ(53214, client4->getRequestSourceEndpoint().getPort());
+
+ const uint8_t expected_data[] = { 192, 0, 2, 1 };
+ EXPECT_EQ(AF_INET, client4->getRequestSourceIPAddress().getFamily());
+ ASSERT_EQ(4, client4->getRequestSourceIPAddress().getLength());
+ EXPECT_EQ(0, memcmp(expected_data,
+ client4->getRequestSourceIPAddress().getData(), 4));
+}
+
+TEST_F(ClientTest, constructIPv6) {
+ EXPECT_EQ(AF_INET6, client6->getRequestSourceEndpoint().getFamily());
+ EXPECT_EQ(IPPROTO_TCP, client6->getRequestSourceEndpoint().getProtocol());
+ EXPECT_EQ("2001:db8::1",
+ client6->getRequestSourceEndpoint().getAddress().toText());
+ EXPECT_EQ(53216, client6->getRequestSourceEndpoint().getPort());
+
+ const uint8_t expected_data[] = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01 };
+ EXPECT_EQ(AF_INET6, client6->getRequestSourceIPAddress().getFamily());
+ ASSERT_EQ(16, client6->getRequestSourceIPAddress().getLength());
+ EXPECT_EQ(0, memcmp(expected_data,
+ client6->getRequestSourceIPAddress().getData(), 16));
+}
+
+TEST_F(ClientTest, toText) {
+ EXPECT_EQ("192.0.2.1#53214", client4->toText());
+ EXPECT_EQ("2001:db8::1#53216", client6->toText());
+}
+
+// test operator<<. We simply confirm it appends the result of toText().
+TEST_F(ClientTest, LeftShiftOperator) {
+ std::ostringstream oss;
+ oss << *client4 << "more text";
+ EXPECT_EQ(client4->toText() + std::string("more text"), oss.str());
+}
+}
diff --git a/src/lib/server_common/tests/data_path.h.in b/src/lib/server_common/tests/data_path.h.in
new file mode 100644
index 0000000..8ac0380
--- /dev/null
+++ b/src/lib/server_common/tests/data_path.h.in
@@ -0,0 +1,16 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#define TEST_DATA_PATH "@abs_srcdir@/testdata"
+#define PLUGIN_DATA_PATH "@top_srcdir@/src/bin/cfgmgr/plugins"
diff --git a/src/lib/server_common/tests/keyring_test.cc b/src/lib/server_common/tests/keyring_test.cc
new file mode 100644
index 0000000..d79b541
--- /dev/null
+++ b/src/lib/server_common/tests/keyring_test.cc
@@ -0,0 +1,149 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <server_common/keyring.h>
+#include <server_common/tests/data_path.h>
+
+#include <config/tests/fake_session.h>
+#include <config/ccsession.h>
+#include <dns/name.h>
+
+#include <gtest/gtest.h>
+#include <memory>
+#include <string>
+
+using namespace isc::data;
+using namespace isc::config;
+using namespace isc::server_common;
+using namespace isc::dns;
+
+namespace {
+
+class KeyringTest : public ::testing::Test {
+public:
+ KeyringTest() :
+ session(ElementPtr(new ListElement), ElementPtr(new ListElement),
+ ElementPtr(new ListElement)),
+ specfile(std::string(TEST_DATA_PATH) + "/spec.spec")
+ {
+ session.getMessages()->add(createAnswer());
+ mccs.reset(new ModuleCCSession(specfile, session, NULL, NULL, false));
+ }
+ isc::cc::FakeSession session;
+ std::auto_ptr<ModuleCCSession> mccs;
+ std::string specfile;
+ void doInit(bool with_key = true) {
+ // Prepare the module specification for it and the config
+ session.getMessages()->
+ add(createAnswer(0,
+ moduleSpecFromFile(std::string(PLUGIN_DATA_PATH) +
+ "/tsig_keys.spec").
+ getFullSpec()));
+ if (with_key) {
+ session.getMessages()->add(
+ createAnswer(0, Element::fromJSON(
+ "{\"keys\": [\"key:MTIzNAo=:hmac-sha1\"]}")));
+ } else {
+ // This emulates the case of using the spec default. Note that
+ // the default value won't be passed to the config handler, so
+ // we'll pass an empty object, instead of {"keys": []}.
+ session.getMessages()->add(createAnswer(0,
+ Element::fromJSON("{}")));
+ }
+ // Now load it
+ EXPECT_NO_THROW(initKeyring(*mccs));
+ EXPECT_NE(keyring, boost::shared_ptr<TSIGKeyRing>()) <<
+ "No keyring even after init";
+ }
+};
+
+// Test usual use - init, using the keyring, update, deinit
+TEST_F(KeyringTest, keyring) {
+ // First, initialize it
+ {
+ SCOPED_TRACE("Init");
+ doInit();
+
+ // Make sure it contains the correct key
+ TSIGKeyRing::FindResult result(keyring->find(Name("key"),
+ TSIGKey::HMACSHA1_NAME()));
+ EXPECT_EQ(TSIGKeyRing::SUCCESS, result.code);
+ }
+
+ {
+ SCOPED_TRACE("Update");
+ session.addMessage(createCommand("config_update", Element::fromJSON(
+ "{\"keys\": [\"another:MTIzNAo=:hmac-sha256\"]}")),
+ "tsig_keys", "*");
+ mccs->checkCommand();
+
+ // Make sure it no longer contains the original key
+ TSIGKeyRing::FindResult result(keyring->find(Name("key"),
+ TSIGKey::HMACSHA1_NAME()));
+ EXPECT_EQ(TSIGKeyRing::NOTFOUND, result.code);
+ // but it does contain the new one
+ TSIGKeyRing::FindResult result2 = keyring->find(Name("another"),
+ TSIGKey::HMACSHA256_NAME());
+ EXPECT_EQ(TSIGKeyRing::SUCCESS, result2.code);
+ }
+
+ {
+ SCOPED_TRACE("Deinit");
+ deinitKeyring(*mccs);
+ EXPECT_EQ(keyring, boost::shared_ptr<TSIGKeyRing>()) <<
+ "The keyring didn't disappear";
+ }
+}
+
+TEST_F(KeyringTest, keyringWithDefault) {
+ // If we don't explicitly specify a keyring, the default (no key) will
+ // be used.
+ doInit(false);
+ EXPECT_EQ(0, keyring->size());
+ deinitKeyring(*mccs);
+}
+
+// Init twice
+TEST_F(KeyringTest, initTwice) {
+ // It is NULL before
+ EXPECT_EQ(keyring, boost::shared_ptr<TSIGKeyRing>()) <<
+ "Someone forgot to deinit it before";
+ {
+ SCOPED_TRACE("First init");
+ doInit();
+ }
+ boost::shared_ptr<TSIGKeyRing> backup(keyring);
+ {
+ SCOPED_TRACE("Second init");
+ EXPECT_NO_THROW(initKeyring(*mccs)) <<
+ "It not only does something when it is already initialized, "
+ "it even throws at it";
+ }
+ EXPECT_EQ(backup, keyring) << "The second init replaced the data";
+ deinitKeyring(*mccs);
+}
+
+// deinit when not initialized
+TEST_F(KeyringTest, extraDeinit) {
+ // It is NULL before
+ EXPECT_EQ(boost::shared_ptr<TSIGKeyRing>(), keyring) <<
+ "Someone forgot to deinit it before";
+ // Check that it doesn't get confused when we do not have it initialized
+ EXPECT_NO_THROW(deinitKeyring(*mccs));
+ // It is still NULL
+ EXPECT_EQ(keyring, boost::shared_ptr<TSIGKeyRing>()) <<
+ "Where did it get something after deinit?";
+}
+
+}
diff --git a/src/lib/server_common/tests/portconfig_unittest.cc b/src/lib/server_common/tests/portconfig_unittest.cc
index fabdfa2..65963eb 100644
--- a/src/lib/server_common/tests/portconfig_unittest.cc
+++ b/src/lib/server_common/tests/portconfig_unittest.cc
@@ -17,6 +17,7 @@
#include <cc/data.h>
#include <exceptions/exceptions.h>
#include <asiolink/asiolink.h>
+#include <asiodns/asiodns.h>
#include <gtest/gtest.h>
#include <string>
@@ -25,7 +26,8 @@ using namespace isc::server_common::portconfig;
using namespace isc::data;
using namespace isc;
using namespace std;
-using namespace asiolink;
+using namespace isc::asiolink;
+using namespace isc::asiodns;
namespace {
@@ -175,7 +177,7 @@ TEST_F(InstallListenAddresses, rollback) {
EXPECT_NO_THROW(installListenAddresses(valid_, store_, dnss_));
checkAddresses(valid_, "Before rollback");
// This should not bind them, but should leave the original addresses
- EXPECT_THROW(installListenAddresses(invalid_, store_, dnss_), IOError);
+ EXPECT_THROW(installListenAddresses(invalid_, store_, dnss_), exception);
checkAddresses(valid_, "After rollback");
}
diff --git a/src/lib/server_common/tests/run_unittests.cc b/src/lib/server_common/tests/run_unittests.cc
index 7ebc985..860cb77 100644
--- a/src/lib/server_common/tests/run_unittests.cc
+++ b/src/lib/server_common/tests/run_unittests.cc
@@ -15,6 +15,8 @@
#include <config.h>
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+#include <log/logger_support.h>
#include <dns/tests/unittest_util.h>
@@ -22,5 +24,7 @@ int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+ isc::log::initLogger();
+
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/server_common/tests/testdata/spec.spec b/src/lib/server_common/tests/testdata/spec.spec
new file mode 100644
index 0000000..3e0a822
--- /dev/null
+++ b/src/lib/server_common/tests/testdata/spec.spec
@@ -0,0 +1,6 @@
+{
+ "module_spec": {
+ "module_name": "test"
+ }
+}
+
diff --git a/src/lib/testutils/mockups.h b/src/lib/testutils/mockups.h
index 4bec83d..2441ad7 100644
--- a/src/lib/testutils/mockups.h
+++ b/src/lib/testutils/mockups.h
@@ -19,7 +19,7 @@
#include <xfr/xfrout_client.h>
-#include <asiolink/asiolink.h>
+#include <asiodns/asiodns.h>
// A minimal mock configuration session. Most the methods are
// stubbed out, except for a very basic group_sendmsg() and
@@ -94,7 +94,7 @@ private:
};
// A nonoperative DNSServer object to be used in calls to processMessage().
-class MockServer : public asiolink::DNSServer {
+class MockServer : public isc::asiodns::DNSServer {
public:
MockServer() : done_(false) {}
void operator()(asio::error_code, size_t) {}
diff --git a/src/lib/testutils/srv_test.cc b/src/lib/testutils/srv_test.cc
index 4fec4ca..dd3e425 100644
--- a/src/lib/testutils/srv_test.cc
+++ b/src/lib/testutils/srv_test.cc
@@ -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 <netinet/in.h>
#include <dns/message.h>
@@ -25,7 +27,8 @@
#include <testutils/srv_test.h>
using namespace isc::dns;
-using namespace asiolink;
+using namespace isc::util;
+using namespace isc::asiolink;
namespace isc {
namespace testutils {
@@ -69,9 +72,13 @@ SrvTestBase::createDataFromFile(const char* const datafile,
void
SrvTestBase::createRequestPacket(Message& message,
- const int protocol)
+ const int protocol, TSIGContext* context)
{
- message.toWire(request_renderer);
+ if (context == NULL) {
+ message.toWire(request_renderer);
+ } else {
+ message.toWire(request_renderer, *context);
+ }
delete io_message;
@@ -203,7 +210,7 @@ SrvTestBase::ednsBadVers() {
opcode.getCode(), QR_FLAG, 1, 0, 0, 1);
EXPECT_FALSE(parse_message->getEDNS()); // EDNS isn't added at this point
- isc::dns::InputBuffer ib(response_obuffer->getData(),
+ InputBuffer ib(response_obuffer->getData(),
response_obuffer->getLength());
isc::dns::Message parsed(isc::dns::Message::PARSE);
parsed.fromWire(ib);
diff --git a/src/lib/testutils/srv_test.h b/src/lib/testutils/srv_test.h
index 7361a76..c92e876 100644
--- a/src/lib/testutils/srv_test.h
+++ b/src/lib/testutils/srv_test.h
@@ -12,7 +12,7 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <dns/buffer.h>
+#include <util/buffer.h>
#include <dns/name.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
@@ -84,7 +84,8 @@ protected:
/// form of \c IOMessage in \c io_message.
/// The existing content of \c io_message, if any, will be deleted.
void createRequestPacket(isc::dns::Message& message,
- const int protocol = IPPROTO_UDP);
+ const int protocol = IPPROTO_UDP,
+ isc::dns::TSIGContext* context = NULL);
MockSession notify_session;
MockServer dnsserv;
@@ -99,9 +100,9 @@ protected:
asiolink::IOSocket* io_sock;
asiolink::IOMessage* io_message;
const asiolink::IOEndpoint* endpoint;
- isc::dns::OutputBuffer request_obuffer;
+ isc::util::OutputBuffer request_obuffer;
isc::dns::MessageRenderer request_renderer;
- isc::dns::OutputBufferPtr response_obuffer;
+ isc::util::OutputBufferPtr response_obuffer;
std::vector<uint8_t> data;
};
} // end of namespace testutils
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
new file mode 100644
index 0000000..3db9ac4
--- /dev/null
+++ b/src/lib/util/Makefile.am
@@ -0,0 +1,28 @@
+SUBDIRS = . io unittests tests pyunittests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/exceptions -I$(top_builddir)/src/lib/exceptions
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+lib_LTLIBRARIES = libutil.la
+libutil_la_SOURCES = filename.h filename.cc
+libutil_la_SOURCES += locks.h lru_list.h
+libutil_la_SOURCES += strutil.h strutil.cc
+libutil_la_SOURCES += buffer.h io_utilities.h
+libutil_la_SOURCES += time_utilities.h time_utilities.cc
+libutil_la_SOURCES += hash/sha1.h hash/sha1.cc
+libutil_la_SOURCES += encode/base16_from_binary.h
+libutil_la_SOURCES += encode/base32hex.h encode/base64.h
+libutil_la_SOURCES += encode/base32hex_from_binary.h
+libutil_la_SOURCES += encode/base_n.cc encode/hex.h
+libutil_la_SOURCES += encode/binary_from_base32hex.h
+libutil_la_SOURCES += encode/binary_from_base16.h
+libutil_la_SOURCES += random/qid_gen.h random/qid_gen.cc
+libutil_la_SOURCES += random/random_number_generator.h
+
+EXTRA_DIST = python/pycppwrapper_util.h
+
+libutil_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
+CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/util/buffer.h b/src/lib/util/buffer.h
new file mode 100644
index 0000000..b7a8e28
--- /dev/null
+++ b/src/lib/util/buffer.h
@@ -0,0 +1,524 @@
+// Copyright (C) 2009 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 __BUFFER_H
+#define __BUFFER_H 1
+
+#include <stdlib.h>
+#include <cstring>
+#include <vector>
+
+#include <string.h>
+
+#include <stdint.h>
+
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace util {
+
+///
+/// \brief A standard DNS module exception that is thrown if an out-of-range
+/// buffer operation is being performed.
+///
+class InvalidBufferPosition : public Exception {
+public:
+ InvalidBufferPosition(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+///\brief The \c InputBuffer class is a buffer abstraction for manipulating
+/// read-only data.
+///
+/// The main purpose of this class is to provide a safe placeholder for
+/// examining wire-format data received from a network.
+///
+/// Applications normally use this class only in a limited situation: as an
+/// interface between legacy I/O operation (such as receiving data from a BSD
+/// socket) and the rest of the BIND10 DNS library. One common usage of this
+/// class for an application would therefore be something like this:
+///
+/// \code unsigned char buf[1024];
+/// struct sockaddr addr;
+/// socklen_t addrlen = sizeof(addr);
+/// int cc = recvfrom(s, buf, sizeof(buf), 0, &addr, &addrlen);
+/// InputBuffer buffer(buf, cc);
+/// // pass the buffer to a DNS message object to parse the message \endcode
+///
+/// Other BIND10 DNS classes will then use methods of this class to get access
+/// to the data, but the application normally doesn't have to care about the
+/// details.
+///
+/// An \c InputBuffer object internally holds a reference to the given data,
+/// rather than make a local copy of the data. Also, it does not have an
+/// ownership of the given data. It is application's responsibility to ensure
+/// the data remains valid throughout the lifetime of the \c InputBuffer
+/// object. Likewise, this object generally assumes the data isn't modified
+/// throughout its lifetime; if the application modifies the data while this
+/// object retains a reference to it, the result is undefined. The application
+/// will also be responsible for releasing the data when it's not needed if it
+/// was dynamically acquired.
+///
+/// This is a deliberate design choice: although it's safer to make a local
+/// copy of the given data on construction, it would cause unacceptable
+/// performance overhead, especially considering that a DNS message can be
+/// as large as a few KB. Alternatively, we could allow the object to allocate
+/// memory internally and expose it to the application to store network data
+/// in it. This is also a bad design, however, in that we would effectively
+/// break the abstraction employed in the class, and do so by publishing
+/// "read-only" stuff as a writable memory region. Since there doesn't seem to
+/// be a perfect solution, we have adopted what we thought a "least bad" one.
+///
+/// Methods for reading data from the buffer generally work like an input
+/// stream: it begins with the head of the data, and once some length of data
+/// is read from the buffer, the next read operation will take place from the
+/// head of the unread data. An object of this class internally holds (a
+/// notion of) where the next read operation should start. We call it the
+/// <em>read position</em> in this document.
+class InputBuffer {
+public:
+ ///
+ /// \name Constructors and Destructor
+ //@{
+ /// \brief Constructor from variable length of data.
+ ///
+ /// It is caller's responsibility to ensure that the data is valid as long
+ /// as the buffer exists.
+ /// \param data A pointer to the data stored in the buffer.
+ /// \param len The length of the data in bytes.
+ InputBuffer(const void* data, size_t len) :
+ position_(0), data_(static_cast<const uint8_t*>(data)), len_(len) {}
+ //@}
+
+ ///
+ /// \name Getter Methods
+ //@{
+ /// \brief Return the length of the data stored in the buffer.
+ size_t getLength() const { return (len_); }
+ /// \brief Return the current read position.
+ size_t getPosition() const { return (position_); }
+ //@}
+
+ ///
+ /// \name Setter Methods
+ ///
+ //@{
+ /// \brief Set the read position of the buffer to the given value.
+ ///
+ /// The new position must be in the valid range of the buffer; otherwise
+ /// an exception of class \c isc::dns::InvalidBufferPosition will be thrown.
+ /// \param position The new position (offset from the beginning of the
+ /// buffer).
+ void setPosition(size_t position)
+ {
+ if (position > len_)
+ isc_throw(InvalidBufferPosition, "position is too large");
+ position_ = position;
+ }
+ //@}
+
+ ///
+ /// \name Methods for reading data from the buffer.
+ //@{
+ /// \brief Read an unsigned 8-bit integer from the buffer and return it.
+ ///
+ /// If the remaining length of the buffer is smaller than 8-bit, an
+ /// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
+ uint8_t readUint8()
+ {
+ if (position_ + sizeof(uint8_t) > len_) {
+ isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+ }
+
+ return (data_[position_++]);
+ }
+ /// \brief Read an unsigned 16-bit integer in network byte order from the
+ /// buffer, convert it to host byte order, and return it.
+ ///
+ /// If the remaining length of the buffer is smaller than 16-bit, an
+ /// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
+ uint16_t readUint16()
+ {
+ uint16_t data;
+ const uint8_t* cp;
+
+ if (position_ + sizeof(data) > len_) {
+ isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+ }
+
+ cp = &data_[position_];
+ data = ((unsigned int)(cp[0])) << 8;
+ data |= ((unsigned int)(cp[1]));
+ position_ += sizeof(data);
+
+ return (data);
+ }
+ /// \brief Read an unsigned 32-bit integer in network byte order from the
+ /// buffer, convert it to host byte order, and return it.
+ ///
+ /// If the remaining length of the buffer is smaller than 32-bit, an
+ /// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
+ uint32_t readUint32()
+ {
+ uint32_t data;
+ const uint8_t* cp;
+
+ if (position_ + sizeof(data) > len_) {
+ isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+ }
+
+ cp = &data_[position_];
+ data = ((unsigned int)(cp[0])) << 24;
+ data |= ((unsigned int)(cp[1])) << 16;
+ data |= ((unsigned int)(cp[2])) << 8;
+ data |= ((unsigned int)(cp[3]));
+ position_ += sizeof(data);
+
+ return (data);
+ }
+ /// \brief Read data of the specified length from the buffer and copy it to
+ /// the caller supplied buffer.
+ ///
+ /// The data is copied as stored in the buffer; no conversion is performed.
+ /// If the remaining length of the buffer is smaller than the specified
+ /// length, an exception of class \c isc::dns::InvalidBufferPosition will
+ /// be thrown.
+ void readData(void* data, size_t len)
+ {
+ if (position_ + len > len_) {
+ isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+ }
+
+ memcpy(data, &data_[position_], len);
+ position_ += len;
+ }
+ //@}
+
+private:
+ size_t position_;
+
+ // XXX: The following must be private, but for a short term workaround with
+ // Boost.Python binding, we changed it to protected. We should soon
+ // revisit it.
+protected:
+ const uint8_t* data_;
+ size_t len_;
+};
+
+///
+///\brief The \c OutputBuffer class is a buffer abstraction for manipulating
+/// mutable data.
+///
+/// The main purpose of this class is to provide a safe workplace for
+/// constructing wire-format data to be sent out to a network. Here,
+/// <em>safe</em> means that it automatically allocates necessary memory and
+/// avoid buffer overrun.
+///
+/// Like for the \c InputBuffer class, applications normally use this class only
+/// in a limited situation. One common usage of this class for an application
+/// would be something like this:
+///
+/// \code OutputBuffer buffer(4096); // give a sufficiently large initial size
+/// // pass the buffer to a DNS message object to construct a wire-format
+/// // DNS message.
+/// struct sockaddr to;
+/// sendto(s, buffer.getData(), buffer.getLength(), 0, &to, sizeof(to));
+/// \endcode
+///
+/// where the \c getData() method gives a reference to the internal memory
+/// region stored in the \c buffer object. This is a suboptimal design in that
+/// it exposes an encapsulated "handle" of an object to its user.
+/// Unfortunately, there is no easy way to avoid this without involving
+/// expensive data copy if we want to use this object with a legacy API such as
+/// a BSD socket interface. And, indeed, this is one major purpose for this
+/// object. Applications should use this method only under such a special
+/// circumstance. It should also be noted that the memory region returned by
+/// \c getData() may be invalidated after a subsequent write operation.
+///
+/// An \c OutputBuffer class object automatically extends its memory region when
+/// data is written beyond the end of the current buffer. However, it will
+/// involve performance overhead such as reallocating more memory and copying
+/// data. It is therefore recommended to construct the buffer object with a
+/// sufficiently large initial size.
+/// The \c getCapacity() method provides the current maximum size of data
+/// (including the portion already written) that can be written into the buffer
+/// without causing memory reallocation.
+///
+/// Methods for writing data into the buffer generally work like an output
+/// stream: it begins with the head of the buffer, and once some length of data
+/// is written into the buffer, the next write operation will take place from
+/// the end of the buffer. Other methods to emulate "random access" are also
+/// provided (e.g., \c writeUint16At()). The normal write operations are
+/// normally exception-free as this class automatically extends the buffer
+/// when necessary. However, in extreme cases such as an attempt of writing
+/// multi-GB data, a separate exception (e.g., \c std::bad_alloc) may be thrown
+/// by the system. This also applies to the constructor with a very large
+/// initial size.
+///
+/// Note to developers: it may make more sense to introduce an abstract base
+/// class for the \c OutputBuffer and define the simple implementation as a
+/// a concrete derived class. That way we can provide flexibility for future
+/// extension such as more efficient buffer implementation or allowing users
+/// to have their own customized version without modifying the source code.
+/// We in fact considered that option, but at the moment chose the simpler
+/// approach with a single concrete class because it may make the
+/// implementation unnecessarily complicated while we were still not certain
+/// if we really want that flexibility. We may revisit the class design as
+/// we see more applications of the class. The same considerations apply to
+/// the \c InputBuffer and \c MessageRenderer classes.
+class OutputBuffer {
+public:
+ ///
+ /// \name Constructors and Destructor
+ ///
+ //@{
+ /// \brief Constructor from the initial size of the buffer.
+ ///
+ /// \param len The initial length of the buffer in bytes.
+ OutputBuffer(size_t len) :
+ buffer_(NULL),
+ size_(0),
+ allocated_(len)
+ {
+ // We use malloc and free instead of C++ new[] and delete[].
+ // This way we can use realloc, which may in fact do it without a copy.
+ buffer_ = static_cast<uint8_t*>(malloc(allocated_));
+ if (buffer_ == NULL && len != 0) {
+ throw std::bad_alloc();
+ }
+ }
+
+ /// \brief Copy constructor
+ OutputBuffer(const OutputBuffer& other) :
+ buffer_(NULL),
+ size_(other.size_),
+ allocated_(other.allocated_)
+ {
+ buffer_ = static_cast<uint8_t*>(malloc(allocated_));
+ if (buffer_ == NULL && allocated_ != 0) {
+ throw std::bad_alloc();
+ }
+ memcpy(buffer_, other.buffer_, size_);
+ }
+
+ /// \brief Destructor
+ ~ OutputBuffer() {
+ free(buffer_);
+ }
+ //@}
+
+ /// \brief Assignment operator
+ OutputBuffer& operator =(const OutputBuffer& other) {
+ uint8_t* newbuff(static_cast<uint8_t*>(malloc(other.allocated_)));
+ if (newbuff == NULL && other.allocated_ != 0) {
+ throw std::bad_alloc();
+ }
+ free(buffer_);
+ buffer_ = newbuff;
+ size_ = other.size_;
+ allocated_ = other.allocated_;
+ memcpy(buffer_, other.buffer_, size_);
+ return (*this);
+ }
+
+ ///
+ /// \name Getter Methods
+ ///
+ //@{
+ /// \brief Return the current capacity of the buffer.
+ size_t getCapacity() const { return (allocated_); }
+ /// \brief Return a pointer to the head of the data stored in the buffer.
+ ///
+ /// The caller can assume that the subsequent \c getLength() bytes are
+ /// identical to the stored data of the buffer.
+ ///
+ /// Note: The pointer returned by this method may be invalidated after a
+ /// subsequent write operation.
+ const void* getData() const { return (buffer_); }
+ /// \brief Return the length of data written in the buffer.
+ size_t getLength() const { return (size_); }
+ /// \brief Return the value of the buffer at the specified position.
+ ///
+ /// \c pos must specify the valid position of the buffer; otherwise an
+ /// exception class of \c InvalidBufferPosition will be thrown.
+ ///
+ /// \param pos The position in the buffer to be returned.
+ uint8_t operator[](size_t pos) const
+ {
+ if (pos >= size_) {
+ isc_throw(InvalidBufferPosition, "read at invalid position");
+ }
+ return (buffer_[pos]);
+ }
+ //@}
+
+ ///
+ /// \name Methods for writing data into the buffer.
+ ///
+ //@{
+ /// \brief Insert a specified length of gap at the end of the buffer.
+ ///
+ /// The caller should not assume any particular value to be inserted.
+ /// This method is provided as a shortcut to make a hole in the buffer
+ /// that is to be filled in later, e.g, by \ref writeUint16At().
+ /// \param len The length of the gap to be inserted in bytes.
+ void skip(size_t len) {
+ ensureAllocated(size_ + len);
+ size_ += len;
+ }
+
+ /// \brief Trim the specified length of data from the end of the buffer.
+ ///
+ /// The specified length must not exceed the current data size of the
+ /// buffer; otherwise an exception of class \c isc::OutOfRange will
+ /// be thrown.
+ ///
+ /// \param len The length of data that should be trimmed.
+ void trim(size_t len)
+ {
+ if (len > size_) {
+ isc_throw(OutOfRange, "trimming too large from output buffer");
+ }
+ size_ -= len;
+ }
+ /// \brief Clear buffer content.
+ ///
+ /// This method can be used to re-initialize and reuse the buffer without
+ /// constructing a new one.
+ void clear() { size_ = 0; }
+ /// \brief Write an unsigned 8-bit integer into the buffer.
+ ///
+ /// \param data The 8-bit integer to be written into the buffer.
+ void writeUint8(uint8_t data) {
+ ensureAllocated(size_ + 1);
+ buffer_[size_ ++] = data;
+ }
+
+ /// \brief Write an unsigned 8-bit integer into the buffer.
+ ///
+ /// The position must be lower than the size of the buffer,
+ /// otherwise an exception of class \c isc::dns::InvalidBufferPosition
+ /// will be thrown.
+ ///
+ /// \param data The 8-bit integer to be written into the buffer.
+ /// \param pos The position in the buffer to write the data.
+ void writeUint8At(uint8_t data, size_t pos) {
+ if (pos + sizeof(data) > size_) {
+ isc_throw(InvalidBufferPosition, "write at invalid position");
+ }
+ buffer_[pos] = data;
+ }
+
+ /// \brief Write an unsigned 16-bit integer in host byte order into the
+ /// buffer in network byte order.
+ ///
+ /// \param data The 16-bit integer to be written into the buffer.
+ void writeUint16(uint16_t data)
+ {
+ ensureAllocated(size_ + sizeof(data));
+ buffer_[size_ ++] = static_cast<uint8_t>((data & 0xff00U) >> 8);
+ buffer_[size_ ++] = static_cast<uint8_t>(data & 0x00ffU);
+ }
+ /// \brief Write an unsigned 16-bit integer in host byte order at the
+ /// specified position of the buffer in network byte order.
+ ///
+ /// The buffer must have a sufficient room to store the given data at the
+ /// given position, that is, <code>pos + 2 < getLength()</code>;
+ /// otherwise an exception of class \c isc::dns::InvalidBufferPosition will
+ /// be thrown.
+ /// Note also that this method never extends the buffer.
+ ///
+ /// \param data The 16-bit integer to be written into the buffer.
+ /// \param pos The beginning position in the buffer to write the data.
+ void writeUint16At(uint16_t data, size_t pos)
+ {
+ if (pos + sizeof(data) > size_) {
+ isc_throw(InvalidBufferPosition, "write at invalid position");
+ }
+
+ buffer_[pos] = static_cast<uint8_t>((data & 0xff00U) >> 8);
+ buffer_[pos + 1] = static_cast<uint8_t>(data & 0x00ffU);
+ }
+ /// \brief Write an unsigned 32-bit integer in host byte order
+ /// into the buffer in network byte order.
+ ///
+ /// \param data The 32-bit integer to be written into the buffer.
+ void writeUint32(uint32_t data)
+ {
+ ensureAllocated(size_ + sizeof(data));
+ buffer_[size_ ++] = static_cast<uint8_t>((data & 0xff000000) >> 24);
+ buffer_[size_ ++] = static_cast<uint8_t>((data & 0x00ff0000) >> 16);
+ buffer_[size_ ++] = static_cast<uint8_t>((data & 0x0000ff00) >> 8);
+ buffer_[size_ ++] = static_cast<uint8_t>(data & 0x000000ff);
+ }
+ /// \brief Copy an arbitrary length of data into the buffer.
+ ///
+ /// No conversion on the copied data is performed.
+ ///
+ /// \param data A pointer to the data to be copied into the buffer.
+ /// \param len The length of the data in bytes.
+ void writeData(const void *data, size_t len)
+ {
+ ensureAllocated(size_ + len);
+ memcpy(buffer_ + size_, data, len);
+ size_ += len;
+ }
+ //@}
+
+private:
+ // The actual data
+ uint8_t* buffer_;
+ // How many bytes are used
+ size_t size_;
+ // How many bytes do we have preallocated (eg. the capacity)
+ size_t allocated_;
+ // Make sure at last needed_size bytes are allocated in the buffer
+ void ensureAllocated(size_t needed_size) {
+ if (allocated_ < needed_size) {
+ // Guess some bigger size
+ size_t new_size = (allocated_ == 0) ? 1024 : allocated_;
+ while (new_size < needed_size) {
+ new_size *= 2;
+ }
+ // Allocate bigger space
+ uint8_t* new_buffer_(static_cast<uint8_t*>(realloc(buffer_,
+ new_size)));
+ if (new_buffer_ == NULL) {
+ // If it fails, the original block is left intact by it
+ throw std::bad_alloc();
+ }
+ buffer_ = new_buffer_;
+ allocated_ = new_size;
+ }
+ }
+};
+
+/// \brief Pointer-like types pointing to \c InputBuffer or \c OutputBuffer
+///
+/// These types are expected to be used as an argument in asynchronous
+/// callback functions. The internal reference-counting will ensure that
+/// that ongoing state information will not be lost if the object
+/// that originated the asynchronous call falls out of scope.
+typedef boost::shared_ptr<InputBuffer> InputBufferPtr;
+typedef boost::shared_ptr<OutputBuffer> OutputBufferPtr;
+
+} // namespace util
+} // namespace isc
+#endif // __BUFFER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/encode/base16_from_binary.h b/src/lib/util/encode/base16_from_binary.h
new file mode 100644
index 0000000..8606c61
--- /dev/null
+++ b/src/lib/util/encode/base16_from_binary.h
@@ -0,0 +1,108 @@
+#ifndef BOOST_ARCHIVE_ITERATORS_BASE16_FROM_BINARY_HPP
+#define BOOST_ARCHIVE_ITERATORS_BASE16_FROM_BINARY_HPP
+
+// MS compatible compilers support #pragma once
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// base16_from_binary.h (derived from boost base64_from_binary.hpp)
+
+// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+// See http://www.boost.org for updates, documentation, and revision history.
+
+#include <cassert>
+
+#include <cstddef> // size_t
+#include <boost/config.hpp> // for BOOST_DEDUCED_TYPENAME
+#if defined(BOOST_NO_STDC_NAMESPACE)
+namespace std{
+ using ::size_t;
+} // namespace std
+#endif
+
+// See base32hex_from_binary.h for why we need base64_from...hpp here.
+#include <boost/archive/iterators/base64_from_binary.hpp>
+
+namespace boost {
+namespace archive {
+namespace iterators {
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// convert binary integers to base16 characters
+
+namespace detail {
+
+template<class CharType>
+struct from_4_bit {
+ typedef CharType result_type;
+ CharType operator()(CharType t) const{
+ const char * lookup_table =
+ "0123456789"
+ "ABCDEF";
+ assert(t < 16);
+ return (lookup_table[static_cast<size_t>(t)]);
+ }
+};
+
+} // namespace detail
+
+// note: what we would like to do is
+// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+// typedef transform_iterator<
+// from_4_bit<CharType>,
+// transform_width<Base, 4, sizeof(Base::value_type) * 8, CharType>
+// > base16_from_binary;
+// but C++ won't accept this. Rather than using a "type generator" and
+// using a different syntax, make a derivation which should be equivalent.
+//
+// Another issue addressed here is that the transform_iterator doesn't have
+// a templated constructor. This makes it incompatible with the dataflow
+// ideal. This is also addressed here.
+
+//template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+template<
+ class Base,
+ class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
+>
+class base16_from_binary :
+ public transform_iterator<
+ detail::from_4_bit<CharType>,
+ Base
+ >
+{
+ friend class boost::iterator_core_access;
+ typedef transform_iterator<
+ BOOST_DEDUCED_TYPENAME detail::from_4_bit<CharType>,
+ Base
+ > super_t;
+
+public:
+ // make composible buy using templated constructor
+ template<class T>
+ base16_from_binary(BOOST_PFTO_WRAPPER(T) start) :
+ super_t(
+ Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))),
+ detail::from_4_bit<CharType>()
+ )
+ {}
+ // intel 7.1 doesn't like default copy constructor
+ base16_from_binary(const base16_from_binary & rhs) :
+ super_t(
+ Base(rhs.base_reference()),
+ detail::from_4_bit<CharType>()
+ )
+ {}
+// base16_from_binary(){};
+};
+
+} // namespace iterators
+} // namespace archive
+} // namespace boost
+
+#endif // BOOST_ARCHIVE_ITERATORS_BASE16_FROM_BINARY_HPP
diff --git a/src/lib/util/encode/base32hex.h b/src/lib/util/encode/base32hex.h
new file mode 100644
index 0000000..d7129d7
--- /dev/null
+++ b/src/lib/util/encode/base32hex.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2009 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 __BASE32HEX_H
+#define __BASE32HEX_H 1
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+//
+// Note: this helper module isn't specific to the DNS protocol per se.
+// We should probably move this to somewhere else, possibly in some common
+// utility area.
+//
+
+namespace isc {
+namespace util {
+namespace encode {
+
+/// \brief Encode binary data in the base32hex format.
+///
+/// The underlying implementation is shared with \c encodeBase64, and all
+/// description except the format (base32hex) equally applies.
+///
+/// Note: the encoding format is base32hex, not base32.
+///
+/// \param binary A vector object storing the data to be encoded.
+/// \return A newly created string that stores base32hex encoded value for
+/// binary.
+std::string encodeBase32Hex(const std::vector<uint8_t>& binary);
+
+/// \brief Decode a text encoded in the base32hex format into the
+/// original %data.
+///
+/// The underlying implementation is shared with \c decodeBase64, and all
+/// description except the format (base32hex) equally applies.
+///
+/// Note: the encoding format is base32hex, not base32.
+///
+/// \param input A text encoded in the base32hex format.
+/// \param result A vector in which the decoded %data is to be stored.
+void decodeBase32Hex(const std::string& input, std::vector<uint8_t>& result);
+
+} // namespace encode
+} // namespace util
+} // namespace isc
+
+#endif // __BASE32HEX_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/encode/base32hex_from_binary.h b/src/lib/util/encode/base32hex_from_binary.h
new file mode 100644
index 0000000..5d16d04
--- /dev/null
+++ b/src/lib/util/encode/base32hex_from_binary.h
@@ -0,0 +1,110 @@
+#ifndef BOOST_ARCHIVE_ITERATORS_BASE32HEX_FROM_BINARY_HPP
+#define BOOST_ARCHIVE_ITERATORS_BASE32HEX_FROM_BINARY_HPP
+
+// MS compatible compilers support #pragma once
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// base32hex_from_binary.h (derived from boost base64_from_binary.hpp)
+
+// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+// See http://www.boost.org for updates, documentation, and revision history.
+
+#include <cassert>
+
+#include <cstddef> // size_t
+#include <boost/config.hpp> // for BOOST_DEDUCED_TYPENAME
+#if defined(BOOST_NO_STDC_NAMESPACE)
+namespace std{
+ using ::size_t;
+} // namespace std
+#endif
+
+// We use the same boost header files used in "base64_from_". Since the
+// precise path to these headers may vary depending on the boost version we
+// simply include the base64 header here.
+#include <boost/archive/iterators/base64_from_binary.hpp>
+
+namespace boost {
+namespace archive {
+namespace iterators {
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// convert binary integers to base32hex characters
+
+namespace detail {
+
+template<class CharType>
+struct from_5_bit {
+ typedef CharType result_type;
+ CharType operator()(CharType t) const{
+ const char * lookup_table =
+ "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUV";
+ assert(t < 32);
+ return (lookup_table[static_cast<size_t>(t)]);
+ }
+};
+
+} // namespace detail
+
+// note: what we would like to do is
+// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+// typedef transform_iterator<
+// from_5_bit<CharType>,
+// transform_width<Base, 5, sizeof(Base::value_type) * 8, CharType>
+// > base32hex_from_binary;
+// but C++ won't accept this. Rather than using a "type generator" and
+// using a different syntax, make a derivation which should be equivalent.
+//
+// Another issue addressed here is that the transform_iterator doesn't have
+// a templated constructor. This makes it incompatible with the dataflow
+// ideal. This is also addressed here.
+
+//template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+template<
+ class Base,
+ class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
+>
+class base32hex_from_binary :
+ public transform_iterator<
+ detail::from_5_bit<CharType>,
+ Base
+ >
+{
+ friend class boost::iterator_core_access;
+ typedef transform_iterator<
+ BOOST_DEDUCED_TYPENAME detail::from_5_bit<CharType>,
+ Base
+ > super_t;
+
+public:
+ // make composible buy using templated constructor
+ template<class T>
+ base32hex_from_binary(BOOST_PFTO_WRAPPER(T) start) :
+ super_t(
+ Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))),
+ detail::from_5_bit<CharType>()
+ )
+ {}
+ // intel 7.1 doesn't like default copy constructor
+ base32hex_from_binary(const base32hex_from_binary & rhs) :
+ super_t(
+ Base(rhs.base_reference()),
+ detail::from_5_bit<CharType>()
+ )
+ {}
+// base32hex_from_binary(){};
+};
+
+} // namespace iterators
+} // namespace archive
+} // namespace boost
+
+#endif // BOOST_ARCHIVE_ITERATORS_BASE32HEX_FROM_BINARY_HPP
diff --git a/src/lib/util/encode/base64.h b/src/lib/util/encode/base64.h
new file mode 100644
index 0000000..6b1b346
--- /dev/null
+++ b/src/lib/util/encode/base64.h
@@ -0,0 +1,79 @@
+// Copyright (C) 2009 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 __BASE64_H
+#define __BASE64_H 1
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+//
+// Note: this helper module isn't specific to the DNS protocol per se.
+// We should probably move this to somewhere else, possibly in some common
+// utility area.
+//
+
+namespace isc {
+namespace util {
+namespace encode {
+
+/// \brief Encode binary data in the base64 format.
+///
+/// This function returns a new \c std::string object that stores a text
+/// encoded in the base64 format for the given \c binary %data.
+/// The resulting string will be a valid, canonical form of base64
+/// representation as specified in RFC4648.
+///
+/// If memory allocation for the returned string fails, a corresponding
+/// standard exception will be thrown. This function never throws exceptions
+/// otherwise.
+///
+/// \param binary A vector object storing the data to be encoded.
+/// \return A newly created string that stores base64 encoded value for binary.
+std::string encodeBase64(const std::vector<uint8_t>& binary);
+
+/// \brief Decode a text encoded in the base64 format into the original %data.
+///
+/// The \c input argument must be a valid string represented in the base64
+/// format as specified in RFC4648. Space characters (spaces, tabs, newlines)
+/// can be included in \c input and will be ignored. Without spaces, the
+/// length of string must be a multiple of 4 bytes with necessary paddings.
+/// Also it must be encoded using the canonical encoding (see RFC4648).
+/// If any of these conditions is not met, an exception of class
+/// \c isc::BadValue will be thrown.
+///
+/// If \c result doesn't have sufficient capacity to store all decoded %data
+/// and memory allocation fails, a corresponding standard exception will be
+/// thrown. If the caller knows the necessary length (which can in theory
+/// be calculated from the input string), this situation can be avoided by
+/// reserving sufficient space for \c result beforehand.
+///
+/// Any existing %data in \c result will be removed. This is the case in some
+/// of the cases where an exception is thrown; that is, this function only
+/// provides the basic exception guarantee.
+///
+/// \param input A text encoded in the base64 format.
+/// \param result A vector in which the decoded %data is to be stored.
+void decodeBase64(const std::string& input, std::vector<uint8_t>& result);
+
+} // namespace encode
+} // namespace util
+} // namespace isc
+
+#endif // __BASE64_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/encode/base_n.cc b/src/lib/util/encode/base_n.cc
new file mode 100644
index 0000000..0026a0b
--- /dev/null
+++ b/src/lib/util/encode/base_n.cc
@@ -0,0 +1,421 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+#include <cassert>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include <boost/archive/iterators/base64_from_binary.hpp>
+#include <boost/archive/iterators/binary_from_base64.hpp>
+#include <boost/archive/iterators/transform_width.hpp>
+#include <boost/math/common_factor.hpp>
+
+#include <util/encode/base32hex_from_binary.h>
+#include <util/encode/binary_from_base32hex.h>
+#include <util/encode/base16_from_binary.h>
+#include <util/encode/binary_from_base16.h>
+#include <util/encode/base32hex.h>
+#include <util/encode/base64.h>
+
+#include <exceptions/exceptions.h>
+
+using namespace std;
+using namespace boost::archive::iterators;
+
+namespace isc {
+namespace util {
+namespace encode {
+
+// In the following anonymous namespace, we provide a generic framework
+// to encode/decode baseN format. We use the following tools:
+// - boost base64_from_binary/binary_from_base64: provide mapping table for
+// base64.
+// These classes take another iterator (Base) as a template argument, and
+// their dereference operator (operator*()) first retrieves an input value
+// from Base via Base::operator* and converts the value using their mapping
+// table. The converted value is returned as their own operator*.
+// - base{32hex,16}_from_binary/binary_from_base{32hex,16}: provide mapping
+// table for base32hex and base16. A straightforward variation of their
+// base64 counterparts.
+// - EncodeNormalizer/DecodeNormalizer: supplemental filter handling baseN
+// padding characters (=)
+// - boost transform_width: an iterator framework for handling data stream
+// per bit-group. It takes another iterator (Base) and output/input bit
+// numbers (BitsOut/BitsIn) template arguments. A transform_width object
+// internally maintains a bit stream, which can be retrieved per BitsOut
+// bits via its dereference operator (operator*()). It builds the stream
+// by internally iterating over the Base object via Base::operator++ and
+// Base::operator*, using the least BitsIn bits of the result of
+// Base::operator*. In our usage BitsIn for encoding and BitsOut for
+// decoding are always 8 (# of bits for one byte).
+//
+// Its dereference operator
+// retrieves BitsIn bits from the result of "*Base" (if necessary it
+// internally calls ++Base)
+//
+// A conceptual description of how the encoding and decoding work is as
+// follows:
+// Encoding:
+// input binary data => Normalizer (append sufficient number of 0 bits)
+// => transform_width (extract bit groups from the original
+// stream)
+// => baseXX_from_binary (convert each bit group to an
+// encoded byte using the mapping)
+// Decoding:
+// input baseXX text => Normalizer (convert '='s to the encoded characters
+// corresponding to 0, e.g. 'A's in base64)
+// => binary_from_baseXX (convert each encoded byte into
+// the original group bit)
+// => transform_width (build original byte stream by
+// concatenating the decoded bit
+// stream)
+//
+// Below, we define a set of templated classes to handle different parameters
+// for different encoding algorithms.
+namespace {
+// Common constants used for all baseN encoding.
+const char BASE_PADDING_CHAR = '=';
+const uint8_t BINARY_ZERO_CODE = 0;
+
+// EncodeNormalizer is an input iterator intended to be used as a filter
+// between the binary stream and baseXX_from_binary translator (via
+// transform_width). An EncodeNormalizer object is configured with two
+// iterators (base and base_end), specifying the head and end of the input
+// stream. It internally iterators over the original stream, and return
+// each byte of the stream intact via its dereference operator until it
+// reaches the end of the stream. After that the EncodeNormalizer object
+// will return 0 no matter how many times it is subsequently incremented.
+// This is necessary because the input binary stream may not contain
+// sufficient bits for a full encoded text while baseXX_from_binary expects
+// a sufficient length of input.
+// Note: this class is intended to be used within this implementation file,
+// and assumes "base < base_end" on construction without validating the
+// arguments. The behavior is undefined if this assumption doesn't hold.
+class EncodeNormalizer : public iterator<input_iterator_tag, uint8_t> {
+public:
+ EncodeNormalizer(const vector<uint8_t>::const_iterator& base,
+ const vector<uint8_t>::const_iterator& base_end) :
+ base_(base), base_end_(base_end), in_pad_(false)
+ {}
+ EncodeNormalizer& operator++() {
+ if (!in_pad_) {
+ ++base_;
+ }
+ if (base_ == base_end_) {
+ in_pad_ = true;
+ }
+ return (*this);
+ }
+ const uint8_t& operator*() const {
+ if (in_pad_) {
+ return (BINARY_ZERO_CODE);
+ } else {
+ return (*base_);
+ }
+ }
+ bool operator==(const EncodeNormalizer& other) const {
+ return (base_ == other.base_);
+ }
+private:
+ vector<uint8_t>::const_iterator base_;
+ const vector<uint8_t>::const_iterator base_end_;
+ bool in_pad_;
+};
+
+// DecodeNormalizer is an input iterator intended to be used as a filter
+// between the encoded baseX stream and binary_from_baseXX.
+// A DecodeNormalizer object is configured with three string iterators
+// (base, base_beginpad, and base_end), specifying the head of the string,
+// the beginning position of baseX padding (when there's padding), and
+// end of the string, respectively. It internally iterators over the original
+// stream, and return each character of the encoded string via its dereference
+// operator until it reaches base_beginpad. After that the DecodeNormalizer
+// will return the encoding character corresponding to the all-0 value
+// (which is specified on construction via base_zero_code. see also
+// BaseZeroCode below). This translation is necessary because
+// binary_from_baseXX doesn't accept the padding character (i.e. '=').
+// Note: this class is intended to be used within this implementation file,
+// and for simplicity assumes "base < base_beginpad <= base_end" on
+// construction without validating the arguments. The behavior is undefined
+// if this assumption doesn't hold.
+class DecodeNormalizer : public iterator<input_iterator_tag, char> {
+public:
+ DecodeNormalizer(const char base_zero_code,
+ const string::const_iterator& base,
+ const string::const_iterator& base_beginpad,
+ const string::const_iterator& base_end) :
+ base_zero_code_(base_zero_code),
+ base_(base), base_beginpad_(base_beginpad), base_end_(base_end),
+ in_pad_(false)
+ {
+ // Skip beginning spaces, if any. We need do it here because
+ // otherwise the first call to operator*() would be confused.
+ skipSpaces();
+ }
+ DecodeNormalizer& operator++() {
+ ++base_;
+ skipSpaces();
+ if (base_ == base_beginpad_) {
+ in_pad_ = true;
+ }
+ return (*this);
+ }
+ void skipSpaces() {
+ // If (char is signed and) *base_ < 0, on Windows platform with Visual
+ // Studio compiler it may trigger _ASSERTE((unsigned)(c + 1) <= 256);
+ // so make sure that the parameter of isspace() is larger than 0.
+ // We don't simply cast it to unsigned char to avoid confusing the
+ // isspace() implementation with a possible extension for values
+ // larger than 127. Also note the check is not ">= 0"; for systems
+ // where char is unsigned that would always be true and would possibly
+ // trigger a compiler warning that could stop the build.
+ while (base_ != base_end_ && *base_ > 0 && isspace(*base_)) {
+ ++base_;
+ }
+ }
+ const char& operator*() const {
+ if (base_ == base_end_) {
+ // binary_from_baseX calls this operator when it needs more bits
+ // even if the internal iterator (base_) has reached its end
+ // (if that happens it means the input is an incomplete baseX
+ // string and should be rejected). So this is the only point
+ // we can catch and reject this type of invalid input.
+ isc_throw(BadValue, "Unexpected end of input in BASE decoder");
+ }
+ if (in_pad_) {
+ return (base_zero_code_);
+ } else {
+ return (*base_);
+ }
+ }
+ bool operator==(const DecodeNormalizer& other) const {
+ return (base_ == other.base_);
+ }
+private:
+ const char base_zero_code_;
+ string::const_iterator base_;
+ const string::const_iterator base_beginpad_;
+ const string::const_iterator base_end_;
+ bool in_pad_;
+};
+
+// BitsPerChunk: number of bits to be converted using the baseN mapping table.
+// e.g. 6 for base64.
+// BaseZeroCode: the byte character that represents a value of 0 in
+// the corresponding encoding. e.g. 'A' for base64.
+// Encoder: baseX_from_binary<transform_width<EncodeNormalizer,
+// BitsPerChunk, 8> >
+// Decoder: transform_width<binary_from_baseX<DecodeNormalizer>,
+// 8, BitsPerChunk>
+template <int BitsPerChunk, char BaseZeroCode,
+ typename Encoder, typename Decoder>
+struct BaseNTransformer {
+ static string encode(const vector<uint8_t>& binary);
+ static void decode(const char* algorithm,
+ const string& base64, vector<uint8_t>& result);
+
+ // BITS_PER_GROUP is the number of bits for the smallest possible (non
+ // empty) bit string that can be converted to a valid baseN encoded text
+ // without padding. It's the least common multiple of 8 and BitsPerChunk,
+ // e.g. 24 for base64.
+ static const int BITS_PER_GROUP =
+ boost::math::static_lcm<BitsPerChunk, 8>::value;
+
+ // MAX_PADDING_CHARS is the maximum number of padding characters
+ // that can appear in a valid baseN encoded text.
+ // It's group_len - chars_for_byte, where group_len is the number of
+ // encoded characters to represent BITS_PER_GROUP bits, and
+ // chars_for_byte is the number of encoded character that is needed to
+ // represent a single byte, which is ceil(8 / BitsPerChunk).
+ // For example, for base64 we need two encoded characters to represent a
+ // byte, and each group consists of 4 encoded characters, so
+ // MAX_PADDING_CHARS is 4 - 2 = 2.
+ static const int MAX_PADDING_CHARS =
+ BITS_PER_GROUP / BitsPerChunk -
+ (8 / BitsPerChunk + ((8 % BitsPerChunk) == 0 ? 0 : 1));
+};
+
+template <int BitsPerChunk, char BaseZeroCode,
+ typename Encoder, typename Decoder>
+string
+BaseNTransformer<BitsPerChunk, BaseZeroCode, Encoder, Decoder>::encode(
+ const vector<uint8_t>& binary)
+{
+ // calculate the resulting length.
+ size_t bits = binary.size() * 8;
+ if (bits % BITS_PER_GROUP > 0) {
+ bits += (BITS_PER_GROUP - (bits % BITS_PER_GROUP));
+ }
+ const size_t len = bits / BitsPerChunk;
+
+ string result;
+ result.reserve(len);
+ result.assign(Encoder(EncodeNormalizer(binary.begin(), binary.end())),
+ Encoder(EncodeNormalizer(binary.end(), binary.end())));
+ assert(len >= result.length());
+ result.append(len - result.length(), BASE_PADDING_CHAR);
+ return (result);
+}
+
+template <int BitsPerChunk, char BaseZeroCode,
+ typename Encoder, typename Decoder>
+void
+BaseNTransformer<BitsPerChunk, BaseZeroCode, Encoder, Decoder>::decode(
+ const char* const algorithm,
+ const string& input,
+ vector<uint8_t>& result)
+{
+ // enumerate the number of trailing padding characters (=), ignoring
+ // white spaces. since baseN_from_binary doesn't accept padding,
+ // we handle it explicitly.
+ size_t padchars = 0;
+ string::const_reverse_iterator srit = input.rbegin();
+ string::const_reverse_iterator srit_end = input.rend();
+ while (srit != srit_end) {
+ char ch = *srit;
+ if (ch == BASE_PADDING_CHAR) {
+ if (++padchars > MAX_PADDING_CHARS) {
+ isc_throw(BadValue, "Too many " << algorithm
+ << " padding characters: " << input);
+ }
+ } else if (ch < 0 || !isspace(ch)) {
+ break;
+ }
+ ++srit;
+ }
+ // then calculate the number of padding bits corresponding to the padding
+ // characters. In general, the padding bits consist of all-zero
+ // trailing bits of the last encoded character followed by zero bits
+ // represented by the padding characters:
+ // 1st pad 2nd pad 3rd pad...
+ // +++===== ======= ===... (+: from encoded chars, =: from pad chars)
+ // 0000...0 0......0 000...
+ // 0 7 8 15 16.... (bits)
+ // The number of bits for the '==...' part is padchars * BitsPerChunk.
+ // So the total number of padding bits is the smallest multiple of 8
+ // that is >= padchars * BitsPerChunk.
+ // (Below, note the common idiom of the bitwise AND with ~7. It clears the
+ // lowest three bits, so has the effect of rounding the result down to the
+ // nearest multiple of 8)
+ const size_t padbits = (padchars * BitsPerChunk + 7) & ~7;
+
+ // In some encoding algorithm, it could happen that a padding byte would
+ // contain a full set of encoded bits, which is not allowed by definition
+ // of padding. For example, if BitsPerChunk is 5, the following
+ // representation could happen:
+ // ++00000= (+: from encoded chars, 0: encoded char for '0', =: pad chars)
+ // 0 7 (bits)
+ // This must actually be encoded as follows:
+ // ++======
+ // 0 7 (bits)
+ // The following check rejects this type of invalid encoding.
+ if (padbits > BitsPerChunk * (padchars + 1)) {
+ isc_throw(BadValue, "Invalid " << algorithm << "padding: " << input);
+ }
+
+ // convert the number of bits in bytes for convenience.
+ const size_t padbytes = padbits / 8;
+
+ try {
+ result.assign(Decoder(DecodeNormalizer(BaseZeroCode, input.begin(),
+ srit.base(), input.end())),
+ Decoder(DecodeNormalizer(BaseZeroCode, input.end(),
+ input.end(), input.end())));
+ } catch (const dataflow_exception& ex) {
+ // convert any boost exceptions into our local one.
+ isc_throw(BadValue, ex.what());
+ }
+
+ // Confirm the original BaseX text is the canonical encoding of the
+ // data, that is, that the first byte of padding is indeed 0.
+ // (DecodeNormalizer and binary_from_baseXX ensure that the rest of the
+ // padding is all zero).
+ assert(result.size() >= padbytes);
+ if (padbytes > 0 && *(result.end() - padbytes) != 0) {
+ isc_throw(BadValue, "Non 0 bits included in " << algorithm
+ << " padding: " << input);
+ }
+
+ // strip the padded zero-bit fields
+ result.resize(result.size() - padbytes);
+}
+
+//
+// Instantiation for BASE-64
+//
+typedef
+base64_from_binary<transform_width<EncodeNormalizer, 6, 8> > base64_encoder;
+typedef
+transform_width<binary_from_base64<DecodeNormalizer>, 8, 6> base64_decoder;
+typedef BaseNTransformer<6, 'A', base64_encoder, base64_decoder>
+Base64Transformer;
+
+//
+// Instantiation for BASE-32HEX
+//
+typedef
+base32hex_from_binary<transform_width<EncodeNormalizer, 5, 8> >
+base32hex_encoder;
+typedef
+transform_width<binary_from_base32hex<DecodeNormalizer>, 8, 5>
+base32hex_decoder;
+typedef BaseNTransformer<5, '0', base32hex_encoder, base32hex_decoder>
+Base32HexTransformer;
+
+//
+// Instantiation for BASE-16 (HEX)
+//
+typedef
+base16_from_binary<transform_width<EncodeNormalizer, 4, 8> > base16_encoder;
+typedef
+transform_width<binary_from_base16<DecodeNormalizer>, 8, 4> base16_decoder;
+typedef BaseNTransformer<4, '0', base16_encoder, base16_decoder>
+Base16Transformer;
+}
+
+string
+encodeBase64(const vector<uint8_t>& binary) {
+ return (Base64Transformer::encode(binary));
+}
+
+void
+decodeBase64(const string& input, vector<uint8_t>& result) {
+ Base64Transformer::decode("base64", input, result);
+}
+
+string
+encodeBase32Hex(const vector<uint8_t>& binary) {
+ return (Base32HexTransformer::encode(binary));
+}
+
+void
+decodeBase32Hex(const string& input, vector<uint8_t>& result) {
+ Base32HexTransformer::decode("base32hex", input, result);
+}
+
+string
+encodeHex(const vector<uint8_t>& binary) {
+ return (Base16Transformer::encode(binary));
+}
+
+void
+decodeHex(const string& input, vector<uint8_t>& result) {
+ Base16Transformer::decode("base16", input, result);
+}
+
+} // namespace encode
+} // namespace util
+} // namespace isc
diff --git a/src/lib/util/encode/binary_from_base16.h b/src/lib/util/encode/binary_from_base16.h
new file mode 100644
index 0000000..50342f1
--- /dev/null
+++ b/src/lib/util/encode/binary_from_base16.h
@@ -0,0 +1,120 @@
+#ifndef BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE16_HPP
+#define BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE16_HPP
+
+// MS compatible compilers support #pragma once
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// binary_from_base16.h (derived from boost binary_from_base64.hpp)
+
+// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+// See http://www.boost.org for updates, documentation, and revision history.
+
+#include <cassert>
+
+// See binary_from_base32hex.h for why we need _from_base64.hpp here.
+#include <boost/archive/iterators/binary_from_base64.hpp>
+
+#include <exceptions/exceptions.h>
+
+namespace boost {
+namespace archive {
+namespace iterators {
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// convert base16 characters to binary data
+
+namespace detail {
+
+template<class CharType>
+struct to_4_bit {
+ typedef CharType result_type;
+ CharType operator()(CharType t) const{
+ const char lookup_table[] = {
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 00-0f
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 10-1f
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 20-2f
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 30-3f
+ -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 40-4f
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 50-5f
+ -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 60-6f
+ };
+ // metrowerks trips this assertion - how come?
+ #if ! defined(__MWERKS__)
+ BOOST_STATIC_ASSERT(0x70 == sizeof(lookup_table));
+ #endif
+ signed char value = -1;
+ if((unsigned)t < sizeof(lookup_table))
+ value = lookup_table[(unsigned)t];
+ if(-1 == value) {
+ isc_throw(isc::BadValue,
+ "attempt to decode a value not in base16 char set");
+ }
+ return (value);
+ }
+};
+
+} // namespace detail
+
+// note: what we would like to do is
+// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+// typedef transform_iterator<
+// from_4_bit<CharType>,
+// transform_width<Base, 4, sizeof(Base::value_type) * 8, CharType>
+// > base16_from_binary;
+// but C++ won't accept this. Rather than using a "type generator" and
+// using a different syntax, make a derivation which should be equivalent.
+//
+// Another issue addressed here is that the transform_iterator doesn't have
+// a templated constructor. This makes it incompatible with the dataflow
+// ideal. This is also addressed here.
+
+template<
+ class Base,
+ class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
+>
+class binary_from_base16 : public
+ transform_iterator<
+ detail::to_4_bit<CharType>,
+ Base
+ >
+{
+ friend class boost::iterator_core_access;
+ typedef transform_iterator<
+ detail::to_4_bit<CharType>,
+ Base
+ > super_t;
+public:
+ // make composible buy using templated constructor
+ template<class T>
+ binary_from_base16(BOOST_PFTO_WRAPPER(T) start) :
+ super_t(
+ Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))),
+ detail::to_4_bit<CharType>()
+ )
+ {}
+ // intel 7.1 doesn't like default copy constructor
+ binary_from_base16(const binary_from_base16 & rhs) :
+ super_t(
+ Base(rhs.base_reference()),
+ detail::to_4_bit<CharType>()
+ )
+ {}
+// binary_from_base16(){};
+};
+
+} // namespace iterators
+} // namespace archive
+} // namespace boost
+
+#endif // BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE16_HPP
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/encode/binary_from_base32hex.h b/src/lib/util/encode/binary_from_base32hex.h
new file mode 100644
index 0000000..1d83f54
--- /dev/null
+++ b/src/lib/util/encode/binary_from_base32hex.h
@@ -0,0 +1,123 @@
+#ifndef BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE32HEX_HPP
+#define BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE32HEX_HPP
+
+// MS compatible compilers support #pragma once
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// binary_from_base32hex.h (derived from boost binary_from_base64.hpp)
+
+// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+// See http://www.boost.org for updates, documentation, and revision history.
+
+#include <cassert>
+
+// We use the same boost header files used in "_from_base64". Since the
+// precise path to these headers may vary depending on the boost version we
+// simply include the base64 header here.
+#include <boost/archive/iterators/binary_from_base64.hpp>
+
+#include <exceptions/exceptions.h>
+
+namespace boost {
+namespace archive {
+namespace iterators {
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// convert base32hex characters to binary data
+
+namespace detail {
+
+template<class CharType>
+struct to_5_bit {
+ typedef CharType result_type;
+ CharType operator()(CharType t) const{
+ const char lookup_table[] = {
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 00-0f
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 10-1f
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 20-2f
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 30-3f
+ -1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, // 40-4f
+ 25,26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 50-5f
+ -1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, // 60-6f
+ 25,26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 70-7f
+ };
+ // metrowerks trips this assertion - how come?
+ #if ! defined(__MWERKS__)
+ BOOST_STATIC_ASSERT(0x80 == sizeof(lookup_table));
+ #endif
+ signed char value = -1;
+ if((unsigned)t < sizeof(lookup_table))
+ value = lookup_table[(unsigned)t];
+ if(-1 == value) {
+ isc_throw(isc::BadValue,
+ "attempt to decode a value not in base32hex char set");
+ }
+ return (value);
+ }
+};
+
+} // namespace detail
+
+// note: what we would like to do is
+// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+// typedef transform_iterator<
+// from_5_bit<CharType>,
+// transform_width<Base, 5, sizeof(Base::value_type) * 8, CharType>
+// > base32hex_from_binary;
+// but C++ won't accept this. Rather than using a "type generator" and
+// using a different syntax, make a derivation which should be equivalent.
+//
+// Another issue addressed here is that the transform_iterator doesn't have
+// a templated constructor. This makes it incompatible with the dataflow
+// ideal. This is also addressed here.
+
+template<
+ class Base,
+ class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
+>
+class binary_from_base32hex : public
+ transform_iterator<
+ detail::to_5_bit<CharType>,
+ Base
+ >
+{
+ friend class boost::iterator_core_access;
+ typedef transform_iterator<
+ detail::to_5_bit<CharType>,
+ Base
+ > super_t;
+public:
+ // make composible buy using templated constructor
+ template<class T>
+ binary_from_base32hex(BOOST_PFTO_WRAPPER(T) start) :
+ super_t(
+ Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))),
+ detail::to_5_bit<CharType>()
+ )
+ {}
+ // intel 7.1 doesn't like default copy constructor
+ binary_from_base32hex(const binary_from_base32hex & rhs) :
+ super_t(
+ Base(rhs.base_reference()),
+ detail::to_5_bit<CharType>()
+ )
+ {}
+// binary_from_base32hex(){};
+};
+
+} // namespace iterators
+} // namespace archive
+} // namespace boost
+
+#endif // BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE32HEX_HPP
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/encode/hex.h b/src/lib/util/encode/hex.h
new file mode 100644
index 0000000..5c806fc
--- /dev/null
+++ b/src/lib/util/encode/hex.h
@@ -0,0 +1,65 @@
+// Copyright (C) 2009 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 __HEX_H
+#define __HEX_H 1
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+//
+// Note: this helper module isn't specific to the DNS protocol per se.
+// We should probably move this to somewhere else, possibly in some common
+// utility area.
+//
+
+namespace isc {
+namespace util {
+namespace encode {
+/// \brief Encode binary data in the base16 ('hex') format.
+///
+/// The underlying implementation is shared with \c encodeBase64, and most of
+/// the description except the format (base16) equally applies.
+/// Another notable exception is that the base16 encoding doesn't require
+/// padding, so padding related considerations and the notion of canonical
+/// encoding don't apply.
+///
+/// \param binary A vector object storing the data to be encoded.
+/// \return A newly created string that stores base16 encoded value for
+/// binary.
+std::string encodeHex(const std::vector<uint8_t>& binary);
+
+/// \brief Decode a text encoded in the base16 ('hex') format into the
+/// original %data.
+///
+/// The underlying implementation is shared with \c decodeBase64, and most
+/// of the description except the format (base16) equally applies.
+/// Another notable exception is that the base16 encoding doesn't require
+/// padding, so padding related considerations and the notion of canonical
+/// encoding don't apply.
+///
+/// \param input A text encoded in the base16 format.
+/// \param result A vector in which the decoded %data is to be stored.
+void decodeHex(const std::string& input, std::vector<uint8_t>& result);
+
+} // namespace encode
+} // namespace util
+} // namespace isc
+
+#endif // __HEX_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/filename.cc b/src/lib/util/filename.cc
new file mode 100644
index 0000000..1f2e5db
--- /dev/null
+++ b/src/lib/util/filename.cc
@@ -0,0 +1,137 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <algorithm>
+#include <string>
+
+#include <ctype.h>
+
+#include <util/filename.h>
+
+using namespace std;
+
+
+namespace isc {
+namespace util {
+
+// Split string into components. Any backslashes are assumed to have
+// been replaced by forward slashes.
+
+void
+Filename::split(const string& full_name, string& directory,
+ string& name, string& extension) const
+{
+ directory = name = extension = "";
+ bool dir_present = false;
+ if (!full_name.empty()) {
+
+ // Find the directory.
+ size_t last_slash = full_name.find_last_of('/');
+ if (last_slash != string::npos) {
+
+ // Found the last slash, so extract directory component and
+ // set where the scan for the last_dot should terminate.
+ directory = full_name.substr(0, last_slash + 1);
+ if (last_slash == full_name.size()) {
+
+ // The entire string was a directory, so exit not and don't
+ // do any more searching.
+ return;
+ }
+
+ // Found a directory so note the fact.
+ dir_present = true;
+ }
+
+ // Now search backwards for the last ".".
+ size_t last_dot = full_name.find_last_of('.');
+ if ((last_dot == string::npos) ||
+ (dir_present && (last_dot < last_slash))) {
+
+ // Last "." either not found or it occurs to the left of the last
+ // slash if a directory was present (so it is part of a directory
+ // name). In this case, the remainder of the string after the slash
+ // is the name part.
+ name = full_name.substr(last_slash + 1);
+ return;
+ }
+
+ // Did find a valid dot, so it and everything to the right is the
+ // extension...
+ extension = full_name.substr(last_dot);
+
+ // ... and the name of the file is everything in between.
+ if ((last_dot - last_slash) > 1) {
+ name = full_name.substr(last_slash + 1, last_dot - last_slash - 1);
+ }
+ }
+
+}
+
+// Expand the stored filename with the default.
+
+string
+Filename::expandWithDefault(const string& defname) const {
+
+ string def_directory("");
+ string def_name("");
+ string def_extension("");
+
+ // Normalize the input string.
+ string copy_defname = isc::util::str::trim(defname);
+#ifdef WIN32
+ isc::util::str::normalizeSlash(copy_defname);
+#endif
+
+ // Split into the components
+ split(copy_defname, def_directory, def_name, def_extension);
+
+ // Now construct the result.
+ string retstring =
+ (directory_.empty() ? def_directory : directory_) +
+ (name_.empty() ? def_name : name_) +
+ (extension_.empty() ? def_extension : extension_);
+ return (retstring);
+}
+
+// Use the stored name as default for a given name
+
+string
+Filename::useAsDefault(const string& name) const {
+
+ string name_directory("");
+ string name_name("");
+ string name_extension("");
+
+ // Normalize the input string.
+ string copy_name = isc::util::str::trim(name);
+#ifdef WIN32
+ isc::util::str::normalizeSlash(copy_name);
+#endif
+
+ // Split into the components
+ split(copy_name, name_directory, name_name, name_extension);
+
+ // Now construct the result.
+ string retstring =
+ (name_directory.empty() ? directory_ : name_directory) +
+ (name_name.empty() ? name_ : name_name) +
+ (name_extension.empty() ? extension_ : name_extension);
+ return (retstring);
+}
+
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/util/filename.h b/src/lib/util/filename.h
new file mode 100644
index 0000000..984ecb0
--- /dev/null
+++ b/src/lib/util/filename.h
@@ -0,0 +1,161 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __FILENAME_H
+#define __FILENAME_H
+
+#include <string>
+
+#include <util/strutil.h>
+
+namespace isc {
+namespace util {
+
+/// \brief Class to Manipulate Filenames
+///
+/// This is a utility class to manipulate filenames. It repeats some of the
+/// features found in the Boost filename class, but is self-contained so avoids
+/// the need to link in the Boost library.
+///
+/// A Unix-style filename comprises three parts:
+///
+/// Directory - everything up to and including the last "/". If there is no
+/// "/" in the string, there is no directory component. Note that the
+/// requirement of a trailing slash eliminates the ambiguity of whether a
+/// component is a directory or not, e.g. in /alpha/beta", "beta" could be the
+/// name of a directory or is could be a file. The interpretation here is that
+/// "beta" is the name of a file (although that file could be a directory).
+///
+/// Note: Under Windows, the drive letter is considered to be part of the
+/// directory specification. Unless this class becomes more widely-used on
+/// Windows, there is no point in adding redundant code.
+///
+/// Name - everthing from the character after the last "/" up to but not
+/// including the last ".".
+///
+/// Extension - everthing from the right-most "." (after the right-most "/") to
+/// the end of the string. If there is no "." after the last "/", there is
+/// no file extension.
+///
+/// (Note that on Windows, this function will replace all "\" characters
+/// with "/" characters on input strings.)
+///
+/// This class provides functions for extracting the components and for
+/// substituting components.
+
+
+class Filename {
+public:
+
+ /// \brief Constructor
+ Filename(const std::string& name) :
+ full_name_(""), directory_(""), name_(""), extension_("")
+ {
+ setName(name);
+ }
+
+ /// \brief Sets Stored Filename
+ ///
+ /// \param name New name to replaced currently stored name
+ void setName(const std::string& name) {
+ full_name_ = isc::util::str::trim(name);
+#ifdef WIN32
+ isc::util::str::normalizeSlash(full_name_);
+#endif
+ split(full_name_, directory_, name_, extension_);
+ }
+
+ /// \return Stored Filename
+ std::string fullName() const {
+ return (full_name_);
+ }
+
+ /// \return Directory of Given File Name
+ std::string directory() const {
+ return (directory_);
+ }
+
+ /// \return Name of Given File Name
+ std::string name() const {
+ return (name_);
+ }
+
+ /// \return Extension of Given File Name
+ std::string extension() const {
+ return (extension_);
+ }
+
+ /// \brief Expand Name with Default
+ ///
+ /// A default file specified is supplied and used to fill in any missing
+ /// fields. For example, if the name stored is "/a/b" and the supplied
+ /// name is "c.d", the result is "/a/b.d": the only field missing from the
+ /// stored name is the extension, which is supplied by the default.
+ /// Another example would be to store "a.b" and to supply a default of
+ /// "/c/d/" - the result is "/c/d/a.b". (Note that if the supplied default
+ /// was "/c/d", the result would be "/c/a.b", even if "/c/d" were actually
+ /// a directory.)
+ ///
+ /// \param defname Default name
+ ///
+ /// \return Name expanded with defname.
+ std::string expandWithDefault(const std::string& defname) const;
+
+ /// \brief Use as Default and Substitute into String
+ ///
+ /// Does essentially the inverse of expand(); that filled in the stored
+ /// name with a default and returned the result. This treats the stored
+ /// name as the default and uses it to fill in a given name. In essence,
+ /// the code:
+ /// \code
+ /// Filename f("/a/b");
+ /// result = f.expandWithdefault("c.d");
+ /// \endcode
+ /// gives as a result "/a/b.d". This is the same as:
+ /// \code
+ /// Filename f("c.d");
+ /// result = f.useAsDefault("/a/b");
+ /// \endcode
+ ///
+ /// \param name Name to expand
+ ///
+ /// \return Name expanded with stored name
+ std::string useAsDefault(const std::string& name) const;
+
+private:
+ /// \brief Split Name into Components
+ ///
+ /// Splits the file name into the directory, name and extension parts.
+ /// The name is assumed to have had back slashes replaced by forward
+ /// slashes (if appropriate).
+ ///
+ /// \param full_name Name to split
+ /// \param directory Returned directory part
+ /// \param name Returned name part
+ /// \param extension Returned extension part
+ void split(const std::string& full_name, std::string& directory,
+ std::string& name, std::string& extension) const;
+
+ // Members
+
+ std::string full_name_; ///< Given name
+ std::string directory_; ///< Directory part
+ std::string name_; ///< Name part
+ std::string extension_; ///< Extension part
+};
+
+} // namespace util
+} // namespace isc
+
+#endif // __FILENAME_H
diff --git a/src/lib/util/hash/sha1.cc b/src/lib/util/hash/sha1.cc
new file mode 100644
index 0000000..091e293
--- /dev/null
+++ b/src/lib/util/hash/sha1.cc
@@ -0,0 +1,492 @@
+/*
+ * Description:
+ * This file implements the Secure Hash Signature Standard
+ * algorithms as defined in the National Institute of Standards
+ * and Technology Federal Information Processing Standards
+ * Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2
+ * published on August 1, 2002, and the FIPS PUB 180-2 Change
+ * Notice published on February 28, 2004.
+ *
+ * A combined document showing all algorithms is available at
+ * http://csrc.nist.gov/publications/fips/
+ * fips180-2/fips180-2withchangenotice.pdf
+ *
+ * The SHA-1 algorithm produces a 160-bit message digest for a
+ * given data stream. It should take about 2**n steps to find a
+ * message with the same digest as a given message and
+ * 2**(n/2) to find any two messages with the same digest,
+ * when n is the digest size in bits. Therefore, this
+ * algorithm can serve as a means of providing a
+ * "fingerprint" for a message.
+ *
+ * Portability Issues:
+ * SHA-1 is defined in terms of 32-bit "words". This code
+ * uses <stdint.h> (included via "sha.h") to define 32 and 8
+ * bit unsigned integer types. If your C compiler does not
+ * support 32 bit unsigned integers, this code is not
+ * appropriate.
+ *
+ * Caveats:
+ * SHA-1 is designed to work with messages less than 2^64 bits
+ * long. This implementation uses SHA1Input() to hash the bits
+ * that are a multiple of the size of an 8-bit character, and then
+ * uses SHA1FinalBits() to hash the final few bits of the input.
+ *
+ * Authorship:
+ * This file is adapted from RFC 4634, by D. Eastlake et al.
+ * Copyright (C) The Internet Society (2006).
+ *
+ * Permission is granted for all uses, commercial and non-commercial,
+ * of the sample code found in Section 8. Royalty free license to
+ * use, copy, modify and distribute the software found in Section 8 is
+ * granted, provided that this document is identified in all material
+ * mentioning or referencing this software, and provided that
+ * redistributed derivative works do not contain misleading author or
+ * version information.
+ *
+ * The authors make no representations concerning either the
+ * merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ */
+#include <util/hash/sha1.h>
+
+namespace isc {
+namespace util {
+namespace hash {
+
+/* Local Function Prototyptes */
+static void SHA1Finalize(SHA1Context *, uint8_t Pad_Byte);
+static void SHA1PadMessage(SHA1Context *, uint8_t Pad_Byte);
+static void SHA1ProcessMessageBlock(SHA1Context *);
+
+/*
+ * Define functions used by SHA1 hash
+ */
+static inline uint32_t
+SHA_Ch(const uint32_t x, const uint32_t y, const uint32_t z) {
+ return (((x) & ((y) ^ (z))) ^ (z));
+}
+
+static inline uint32_t
+SHA_Maj(const uint32_t x, const uint32_t y, const uint32_t z) {
+ return (((x) & ((y) | (z))) | ((y) & (z)));
+}
+
+static inline uint32_t
+SHA_Parity(const uint32_t x, const uint32_t y, const uint32_t z) {
+ return ((x) ^ (y) ^ (z));
+}
+
+static inline int
+SHA1CircularShift(uint8_t bits, uint32_t word) {
+ return ((word << bits) | (word >> (32 - bits)));
+}
+
+static inline bool
+SHA1AddLength(SHA1Context *context, uint32_t length) {
+ uint32_t addTemp = context->Length_Low;
+ context->Length_Low += length;
+ if (context->Length_Low < addTemp && ++context->Length_High == 0) {
+ return (true);
+ } else {
+ return (false);
+ }
+}
+
+/*
+ * SHA1Reset
+ *
+ * Description:
+ * This function will initialize the SHA1Context in preparation
+ * for computing a new SHA1 message digest.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to reset.
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+int
+SHA1Reset(SHA1Context *context) {
+ if (!context) {
+ return (SHA_NULL);
+ }
+
+ context->Length_Low = 0;
+ context->Length_High = 0;
+ context->Message_Block_Index = 0;
+
+ context->Intermediate_Hash[0] = 0x67452301;
+ context->Intermediate_Hash[1] = 0xEFCDAB89;
+ context->Intermediate_Hash[2] = 0x98BADCFE;
+ context->Intermediate_Hash[3] = 0x10325476;
+ context->Intermediate_Hash[4] = 0xC3D2E1F0;
+
+ context->Computed = 0;
+ context->Corrupted = 0;
+ return (SHA_SUCCESS);
+}
+
+
+/*
+ * SHA1Input
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion
+ * of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update
+ * message_array: [in]
+ * An array of characters representing the next portion of
+ * the message.
+ * length: [in]
+ * The length of the message in message_array
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+int
+SHA1Input(SHA1Context *context, const uint8_t *message_array, unsigned length) {
+ if (!length) {
+ return (SHA_SUCCESS);
+ }
+
+ if (!context || !message_array) {
+ return (SHA_NULL);
+ }
+
+ if (context->Computed) {
+ context->Corrupted = SHA_STATEERROR;
+ return (SHA_STATEERROR);
+ }
+
+ if (context->Corrupted) {
+ return (context->Corrupted);
+ }
+
+ while(length-- && !context->Corrupted) {
+ context->Message_Block[context->Message_Block_Index++] =
+ (*message_array & 0xFF);
+
+ if (!SHA1AddLength(context, 8) &&
+ (context->Message_Block_Index == SHA1_BLOCKSIZE))
+ {
+ SHA1ProcessMessageBlock(context);
+ }
+
+ message_array++;
+ }
+
+ return (SHA_SUCCESS);
+}
+
+/*
+ * SHA1FinalBits
+ *
+ * Description:
+ * This function will add in any final bits of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update
+ * message_bits: [in]
+ * The final bits of the message, in the upper portion of the
+ * byte. (Use 0b###00000 instead of 0b00000### to input the
+ * three bits ###.)
+ * length: [in]
+ * The number of bits in message_bits, between 1 and 7.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA1FinalBits(SHA1Context *context, const uint8_t message_bits,
+ unsigned int length)
+{
+ uint8_t masks[8] = {
+ /* 0 0b00000000 */ 0x00,
+ /* 1 0b10000000 */ 0x80,
+ /* 2 0b11000000 */ 0xC0,
+ /* 3 0b11100000 */ 0xE0,
+ /* 4 0b11110000 */ 0xF0,
+ /* 5 0b11111000 */ 0xF8,
+ /* 6 0b11111100 */ 0xFC,
+ /* 7 0b11111110 */ 0xFE
+ };
+ uint8_t markbit[8] = {
+ /* 0 0b10000000 */ 0x80,
+ /* 1 0b01000000 */ 0x40,
+ /* 2 0b00100000 */ 0x20,
+ /* 3 0b00010000 */ 0x10,
+ /* 4 0b00001000 */ 0x08,
+ /* 5 0b00000100 */ 0x04,
+ /* 6 0b00000010 */ 0x02,
+ /* 7 0b00000001 */ 0x01
+ };
+
+ if (!length) {
+ return (SHA_SUCCESS);
+ }
+
+ if (!context) {
+ return (SHA_NULL);
+ }
+
+ if (context->Computed || (length >= 8) || (length == 0)) {
+ context->Corrupted = SHA_STATEERROR;
+ return (SHA_STATEERROR);
+ }
+
+ if (context->Corrupted) {
+ return (context->Corrupted);
+ }
+
+ SHA1AddLength(context, length);
+ SHA1Finalize(context,
+ (uint8_t) ((message_bits & masks[length]) | markbit[length]));
+
+ return (SHA_SUCCESS);
+}
+
+/*
+ * SHA1Result
+ *
+ * Description:
+ * This function will return the 160-bit message digest into the
+ * Message_Digest array provided by the caller.
+ * NOTE: The first octet of hash is stored in the 0th element,
+ * the last octet of hash in the 19th element.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to use to calculate the SHA-1 hash.
+ * Message_Digest: [out]
+ * Where the digest is returned.
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+int
+SHA1Result(SHA1Context *context, uint8_t Message_Digest[SHA1_HASHSIZE]) {
+ int i;
+
+ if (!context || !Message_Digest) {
+ return (SHA_NULL);
+ }
+
+ if (context->Corrupted) {
+ return (context->Corrupted);
+ }
+
+ if (!context->Computed) {
+ SHA1Finalize(context, 0x80);
+ }
+
+ for(i = 0; i < SHA1_HASHSIZE; ++i) {
+ Message_Digest[i] = context->Intermediate_Hash[i>>2]
+ >> 8 * (3 - (i & 0x03));
+ }
+
+ return (SHA_SUCCESS);
+}
+
+/*
+ * SHA1Finalize
+ *
+ * Description:
+ * This helper function finishes off the digest calculations.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update
+ * Pad_Byte: [in]
+ * The last byte to add to the digest before the 0-padding
+ * and length. This will contain the last bits of the message
+ * followed by another single bit. If the message was an
+ * exact multiple of 8-bits long, Pad_Byte will be 0x80.
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+static void SHA1Finalize(SHA1Context *context, uint8_t Pad_Byte) {
+ int i;
+ SHA1PadMessage(context, Pad_Byte);
+ /* message may be sensitive, clear it out */
+ for (i = 0; i < SHA1_BLOCKSIZE; ++i)
+ context->Message_Block[i] = 0;
+ context->Length_Low = 0; /* and clear length */
+ context->Length_High = 0;
+ context->Computed = 1;
+}
+
+/*
+ * SHA1PadMessage
+ *
+ * Description:
+ * According to the standard, the message must be padded to an even
+ * 512 bits. The first padding bit must be a '1'. The last 64
+ * bits represent the length of the original message. All bits in
+ * between should be 0. This function will pad the message
+ * according to those rules by filling the Message_Block array
+ * accordingly. It will also call the ProcessMessageBlock function
+ * provided appropriately. When it returns, it can be assumed that
+ * the message digest has been computed.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to pad
+ * Pad_Byte: [in]
+ * The last byte to add to the digest before the 0-padding
+ * and length. This will contain the last bits of the message
+ * followed by another single bit. If the message was an
+ * exact multiple of 8-bits long, Pad_Byte will be 0x80.
+ *
+ * Returns:
+ * Nothing.
+ *
+ */
+static void SHA1PadMessage(SHA1Context *context, uint8_t Pad_Byte) {
+ /*
+ * Check to see if the current message block is too small to hold
+ * the initial padding bits and length. If so, we will pad the
+ * block, process it, and then continue padding into a second
+ * block.
+ */
+ if (context->Message_Block_Index >= (SHA1_BLOCKSIZE - 8)) {
+ context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
+ while (context->Message_Block_Index < SHA1_BLOCKSIZE) {
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ }
+
+ SHA1ProcessMessageBlock(context);
+ } else
+ context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
+
+ while (context->Message_Block_Index < (SHA1_BLOCKSIZE - 8))
+ context->Message_Block[context->Message_Block_Index++] = 0;
+
+ /*
+ * Store the message length as the last 8 octets
+ */
+ context->Message_Block[56] = (uint8_t) (context->Length_High >> 24);
+ context->Message_Block[57] = (uint8_t) (context->Length_High >> 16);
+ context->Message_Block[58] = (uint8_t) (context->Length_High >> 8);
+ context->Message_Block[59] = (uint8_t) (context->Length_High);
+ context->Message_Block[60] = (uint8_t) (context->Length_Low >> 24);
+ context->Message_Block[61] = (uint8_t) (context->Length_Low >> 16);
+ context->Message_Block[62] = (uint8_t) (context->Length_Low >> 8);
+ context->Message_Block[63] = (uint8_t) (context->Length_Low);
+
+ SHA1ProcessMessageBlock(context);
+}
+
+/*
+ * SHA1ProcessMessageBlock
+ *
+ * Description:
+ * This helper function will process the next 512 bits of the
+ * message stored in the Message_Block array.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ * Many of the variable names in this code, especially the
+ * single character names, were used because those were the
+ * names used in the publication.
+ *
+ *
+ */
+static void
+SHA1ProcessMessageBlock(SHA1Context *context) {
+ /* Constants defined in FIPS-180-2, section 4.2.1 */
+ const uint32_t K[] = {
+ 0x5A827999,
+ 0x6ED9EBA1,
+ 0x8F1BBCDC,
+ 0xCA62C1D6
+ };
+ int t; /* Loop counter */
+ uint32_t temp; /* Temporary word value */
+ uint32_t W[80]; /* Word sequence */
+ uint32_t A, B, C, D, E; /* Word buffers */
+
+ /*
+ * Initialize the first 16 words in the array W
+ */
+ for (t = 0; t < 16; t++) {
+ W[t] = ((uint32_t)context->Message_Block[t * 4]) << 24;
+ W[t] |= ((uint32_t)context->Message_Block[t * 4 + 1]) << 16;
+ W[t] |= ((uint32_t)context->Message_Block[t * 4 + 2]) << 8;
+ W[t] |= ((uint32_t)context->Message_Block[t * 4 + 3]);
+ }
+
+ for (t = 16; t < 80; t++) {
+ W[t] = SHA1CircularShift(1, W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+ }
+
+ A = context->Intermediate_Hash[0];
+ B = context->Intermediate_Hash[1];
+ C = context->Intermediate_Hash[2];
+ D = context->Intermediate_Hash[3];
+ E = context->Intermediate_Hash[4];
+
+ for (t = 0; t < 20; t++) {
+ temp = SHA1CircularShift(5,A) + SHA_Ch(B, C, D) + E + W[t] + K[0];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for (t = 20; t < 40; t++) {
+ temp = SHA1CircularShift(5,A) + SHA_Parity(B, C, D) + E + W[t] + K[1];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for (t = 40; t < 60; t++) {
+ temp = SHA1CircularShift(5,A) + SHA_Maj(B, C, D) + E + W[t] + K[2];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for (t = 60; t < 80; t++) {
+ temp = SHA1CircularShift(5,A) + SHA_Parity(B, C, D) + E + W[t] + K[3];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ context->Intermediate_Hash[0] += A;
+ context->Intermediate_Hash[1] += B;
+ context->Intermediate_Hash[2] += C;
+ context->Intermediate_Hash[3] += D;
+ context->Intermediate_Hash[4] += E;
+
+ context->Message_Block_Index = 0;
+}
+
+} // namespace hash
+} // namespace util
+} // namespace isc
diff --git a/src/lib/util/hash/sha1.h b/src/lib/util/hash/sha1.h
new file mode 100644
index 0000000..6089ca8
--- /dev/null
+++ b/src/lib/util/hash/sha1.h
@@ -0,0 +1,91 @@
+/*
+ * sha1.h
+ *
+ * Description:
+ * This is the header file for code which implements the Secure
+ * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
+ * April 17, 1995.
+ *
+ * Many of the variable names in this code, especially the
+ * single character names, were used because those were the names
+ * used in the publication.
+ *
+ * Please read the file sha1.cc for more information.
+ *
+ * Authorship:
+ * This file is adapted from RFC 4634, by D. Eastlake et al.
+ * Copyright (C) The Internet Society (2006).
+ *
+ * Permission is granted for all uses, commercial and non-commercial,
+ * of the sample code found in Section 8. Royalty free license to
+ * use, copy, modify and distribute the software found in Section 8 is
+ * granted, provided that this document is identified in all material
+ * mentioning or referencing this software, and provided that
+ * redistributed derivative works do not contain misleading author or
+ * version information.
+ *
+ * The authors make no representations concerning either the
+ * merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ */
+
+#ifndef _SHA1_H_
+#define _SHA1_H_
+
+#include <stdint.h>
+
+namespace isc {
+namespace util {
+namespace hash {
+/*
+ * If you do not have the ISO standard stdint.h header file, then you
+ * must typdef the following:
+ * name meaning
+ * uint32_t unsigned 32 bit integer
+ * uint8_t unsigned 8 bit integer (i.e., unsigned char)
+ * int_least16_t integer of >= 16 bits
+ *
+ */
+
+enum {
+ SHA_SUCCESS = 0,
+ SHA_NULL, /* Null pointer parameter */
+ SHA_STATEERROR /* called Input after Result */
+};
+
+enum {
+ SHA1_HASHSIZE = 20,
+ SHA1_HASHBITS = 20,
+ SHA1_BLOCKSIZE = 64
+};
+
+/*
+ * This structure will hold context information for the SHA-1
+ * hashing operation
+ */
+typedef struct SHA1Context
+{
+ uint32_t Intermediate_Hash[SHA1_HASHSIZE/4]; /* Message Digest */
+ uint32_t Length_Low; /* Message length in bits */
+ uint32_t Length_High; /* Message length in bits */
+ int_least16_t Message_Block_Index; /* Index into message block array */
+ uint8_t Message_Block[64]; /* 512-bit message blocks */
+ int Computed; /* Is the digest computed? */
+ int Corrupted; /* Is the message digest corrupted? */
+} SHA1Context;
+
+/*
+ * Function Prototypes
+ */
+extern int SHA1Reset(SHA1Context *);
+extern int SHA1Input(SHA1Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA1FinalBits(SHA1Context *, const uint8_t bits,
+ unsigned int bitcount);
+extern int SHA1Result(SHA1Context *, uint8_t Message_Digest[SHA1_HASHSIZE]);
+
+} // namespace hash
+} // namespace util
+} // namespace isc
+#endif
diff --git a/src/lib/util/io/Makefile.am b/src/lib/util/io/Makefile.am
new file mode 100644
index 0000000..cbcd54d
--- /dev/null
+++ b/src/lib/util/io/Makefile.am
@@ -0,0 +1,18 @@
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+lib_LTLIBRARIES = libutil_io.la
+libutil_io_la_SOURCES = fd.h fd.cc fd_share.h fd_share.cc
+libutil_io_la_CXXFLAGS = $(AM_CXXFLAGS) -fno-strict-aliasing
+
+CLEANFILES = *.gcno *.gcda
+
+pyexec_LTLIBRARIES = libutil_io_python.la
+# Python prefers .so, while some OSes (specifically MacOS) use a different
+# suffix for dynamic objects. -module is necessary to work this around.
+libutil_io_python_la_LDFLAGS = -module
+libutil_io_python_la_SOURCES = fdshare_python.cc
+libutil_io_python_la_LIBADD = libutil_io.la
+libutil_io_python_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+# Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be
+# placed after -Wextra defined in AM_CXXFLAGS
+libutil_io_python_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
diff --git a/src/lib/util/io/fd.cc b/src/lib/util/io/fd.cc
new file mode 100644
index 0000000..04e64dc
--- /dev/null
+++ b/src/lib/util/io/fd.cc
@@ -0,0 +1,70 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "fd.h"
+
+#include <unistd.h>
+#include <cerrno>
+
+namespace isc {
+namespace util {
+namespace io {
+
+bool
+write_data(const int fd, const void *buffer_v, const size_t length) {
+ const unsigned char *buffer(static_cast<const unsigned char *>(buffer_v));
+ size_t rest(length);
+ // Just keep writing until all is written
+ while (rest) {
+ ssize_t written(write(fd, buffer, rest));
+ if (rest == -1) {
+ if (errno == EINTR) { // Just keep going
+ continue;
+ } else {
+ return false;
+ }
+ } else { // Wrote something
+ rest -= written;
+ buffer += written;
+ }
+ }
+ return true;
+}
+
+ssize_t
+read_data(const int fd, void *buffer_v, const size_t length) {
+ unsigned char *buffer(static_cast<unsigned char *>(buffer_v));
+ size_t rest(length), already(0);
+ while (rest) { // Stil something to read
+ ssize_t amount(read(fd, buffer, rest));
+ if (rest == -1) {
+ if (errno == EINTR) { // Continue on interrupted call
+ continue;
+ } else {
+ return -1;
+ }
+ } else if (amount) {
+ already += amount;
+ rest -= amount;
+ buffer += amount;
+ } else { // EOF
+ return already;
+ }
+ }
+ return already;
+}
+
+}
+}
+}
diff --git a/src/lib/util/io/fd.h b/src/lib/util/io/fd.h
new file mode 100644
index 0000000..bdd2d41
--- /dev/null
+++ b/src/lib/util/io/fd.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __UTIL_IO_FD_H
+#define __UTIL_IO_FD_H 1
+
+#include <unistd.h>
+
+/**
+ * @file fd.h
+ * @short Wrappers around common unix fd manipulation functions.
+ */
+
+namespace isc {
+namespace util {
+namespace io {
+
+/*
+ * \short write() that writes everything.
+ * Wrapper around write(). The difference is, it never writes less data
+ * and looks successfull (eg. it blocks until all data are written).
+ * Retries on signals.
+ *
+ * \return True if sucessfull, false otherwise. The errno variable is left
+ * intact.
+ * \param fd Where to write.
+ * \param data The buffer to write.
+ * \param length How much data is there to write.
+ */
+bool
+write_data(const int fd, const void *data, const size_t length);
+
+/*
+ * \short read() that reads everything.
+ * Wrapper around read(). It does not do short reads, if it returns less,
+ * it means there was EOF. It retries on signals.
+ *
+ * \return Number of bytes read or -1 on error.
+ * \param fd Where to read data from.
+ * \param data Where to put the data.
+ * \param length How many of them.
+ */
+ssize_t
+read_data(const int fd, void *buffer, const size_t length);
+
+}
+}
+}
+
+#endif // __UTIL_IO_FD_H
diff --git a/src/lib/util/io/fd_share.cc b/src/lib/util/io/fd_share.cc
new file mode 100644
index 0000000..92576e0
--- /dev/null
+++ b/src/lib/util/io/fd_share.cc
@@ -0,0 +1,139 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cstring>
+#include <cstdlib>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <stdlib.h> // for malloc and free
+#include "fd_share.h"
+
+namespace isc {
+namespace util {
+namespace io {
+
+namespace {
+// Not all OSes support advanced CMSG macros: CMSG_LEN and CMSG_SPACE.
+// In order to ensure as much portability as possible, we provide wrapper
+// functions of these macros.
+// Note that cmsg_space() could run slow on OSes that do not have
+// CMSG_SPACE.
+inline socklen_t
+cmsg_len(const socklen_t len) {
+#ifdef CMSG_LEN
+ return (CMSG_LEN(len));
+#else
+ // Cast NULL so that any pointer arithmetic performed by CMSG_DATA
+ // is correct.
+ const uintptr_t hdrlen = (uintptr_t)CMSG_DATA(((struct cmsghdr*)NULL));
+ return (hdrlen + len);
+#endif
+}
+
+inline socklen_t
+cmsg_space(const socklen_t len) {
+#ifdef CMSG_SPACE
+ return (CMSG_SPACE(len));
+#else
+ struct msghdr msg;
+ struct cmsghdr* cmsgp;
+ // XXX: The buffer length is an ad hoc value, but should be enough
+ // in a practical sense.
+ char dummybuf[sizeof(struct cmsghdr) + 1024];
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_control = dummybuf;
+ msg.msg_controllen = sizeof(dummybuf);
+
+ cmsgp = (struct cmsghdr*)dummybuf;
+ cmsgp->cmsg_len = cmsg_len(len);
+
+ cmsgp = CMSG_NXTHDR(&msg, cmsgp);
+ if (cmsgp != NULL) {
+ return ((char*)cmsgp - (char*)msg.msg_control);
+ } else {
+ return (0);
+ }
+#endif // CMSG_SPACE
+}
+}
+
+int
+recv_fd(const int sock) {
+ struct msghdr msghdr;
+ struct iovec iov_dummy;
+ unsigned char dummy_data;
+
+ iov_dummy.iov_base = &dummy_data;
+ iov_dummy.iov_len = sizeof(dummy_data);
+ msghdr.msg_name = NULL;
+ msghdr.msg_namelen = 0;
+ msghdr.msg_iov = &iov_dummy;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_flags = 0;
+ msghdr.msg_controllen = cmsg_space(sizeof(int));
+ msghdr.msg_control = malloc(msghdr.msg_controllen);
+ if (msghdr.msg_control == NULL) {
+ return (FD_OTHER_ERROR);
+ }
+
+ if (recvmsg(sock, &msghdr, 0) < 0) {
+ free(msghdr.msg_control);
+ return (FD_COMM_ERROR);
+ }
+ const struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr);
+ int fd = FD_OTHER_ERROR;
+ if (cmsg != NULL && cmsg->cmsg_len == cmsg_len(sizeof(int)) &&
+ cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ fd = *(const int*)CMSG_DATA(cmsg);
+ }
+ free(msghdr.msg_control);
+ return (fd);
+}
+
+int
+send_fd(const int sock, const int fd) {
+ struct msghdr msghdr;
+ struct iovec iov_dummy;
+ unsigned char dummy_data = 0;
+
+ iov_dummy.iov_base = &dummy_data;
+ iov_dummy.iov_len = sizeof(dummy_data);
+ msghdr.msg_name = NULL;
+ msghdr.msg_namelen = 0;
+ msghdr.msg_iov = &iov_dummy;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_flags = 0;
+ msghdr.msg_controllen = cmsg_space(sizeof(int));
+ msghdr.msg_control = malloc(msghdr.msg_controllen);
+ if (msghdr.msg_control == NULL) {
+ return (FD_OTHER_ERROR);
+ }
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr);
+ cmsg->cmsg_len = cmsg_len(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *(int*)CMSG_DATA(cmsg) = fd;
+
+ const int ret = sendmsg(sock, &msghdr, 0);
+ free(msghdr.msg_control);
+ return (ret >= 0 ? 0 : FD_COMM_ERROR);
+}
+
+} // End for namespace io
+} // End for namespace util
+} // End for namespace isc
diff --git a/src/lib/util/io/fd_share.h b/src/lib/util/io/fd_share.h
new file mode 100644
index 0000000..b74d4ef
--- /dev/null
+++ b/src/lib/util/io/fd_share.h
@@ -0,0 +1,65 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef FD_SHARE_H_
+#define FD_SHARE_H_
+
+/**
+ * \file fd_share.h
+ * \short Support to transfer file descriptors between processes.
+ * \todo This interface is very C-ish. Should we have some kind of exceptions?
+ */
+
+namespace isc {
+namespace util {
+namespace io {
+
+const int FD_COMM_ERROR = -2;
+const int FD_OTHER_ERROR = -1;
+
+/**
+ * \short Receives a file descriptor.
+ * This receives a file descriptor sent over an unix domain socket. This
+ * is the counterpart of send_fd().
+ *
+ * \return FD_COMM_ERROR when there's error receiving the socket, FD_OTHER_ERROR
+ * when there's a different error.
+ * \param sock The unix domain socket to read from. Tested and it does
+ * not work with a pipe.
+ */
+int recv_fd(const int sock);
+
+/**
+ * \short Sends a file descriptor.
+ * This sends a file descriptor over an unix domain socket. This is the
+ * counterpart of recv_fd().
+ *
+ * \return FD_COMM_ERROR when there's error sending the socket, FD_OTHER_ERROR
+ * for all other possible errors.
+ * \param sock The unix domain socket to send to. Tested and it does not
+ * work with a pipe.
+ * \param fd The file descriptor to send. It should work with any valid
+ * file descriptor.
+ */
+int send_fd(const int sock, const int fd);
+
+} // End for namespace io
+} // End for namespace util
+} // End for namespace isc
+
+#endif
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/io/fdshare_python.cc b/src/lib/util/io/fdshare_python.cc
new file mode 100644
index 0000000..0a41107
--- /dev/null
+++ b/src/lib/util/io/fdshare_python.cc
@@ -0,0 +1,97 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+#include <structmember.h>
+
+#include <config.h>
+
+#include "fd_share.h"
+
+
+static PyObject*
+fdshare_recv_fd(PyObject*, PyObject* args) {
+ int sock, fd;
+ if (!PyArg_ParseTuple(args, "i", &sock)) {
+ return (NULL);
+ }
+ fd = isc::util::io::recv_fd(sock);
+ return (Py_BuildValue("i", fd));
+}
+
+static PyObject*
+fdshare_send_fd(PyObject*, PyObject* args) {
+ int sock, fd, result;
+ if (!PyArg_ParseTuple(args, "ii", &sock, &fd)) {
+ return (NULL);
+ }
+ result = isc::util::io::send_fd(sock, fd);
+ return (Py_BuildValue("i", result));
+}
+
+static PyMethodDef fdshare_Methods[] = {
+ {"send_fd", fdshare_send_fd, METH_VARARGS, ""},
+ {"recv_fd", fdshare_recv_fd, METH_VARARGS, ""},
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+
+static PyModuleDef bind10_fdshare_python = {
+ { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
+ "bind10_fdshare",
+ "Python bindings for fdshare",
+ -1,
+ fdshare_Methods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+PyMODINIT_FUNC
+PyInit_libutil_io_python(void) {
+ PyObject *mod = PyModule_Create(&bind10_fdshare_python);
+ if (mod == NULL) {
+ return (NULL);
+ }
+
+ PyObject* FD_COMM_ERROR = Py_BuildValue("i", isc::util::io::FD_COMM_ERROR);
+ if (FD_COMM_ERROR == NULL) {
+ Py_XDECREF(mod);
+ return (NULL);
+ }
+ int ret = PyModule_AddObject(mod, "FD_COMM_ERROR", FD_COMM_ERROR);
+ if (-1 == ret) {
+ Py_XDECREF(FD_COMM_ERROR);
+ Py_XDECREF(mod);
+ return (NULL);
+ }
+
+ PyObject* FD_OTHER_ERROR = Py_BuildValue("i",
+ isc::util::io::FD_OTHER_ERROR);
+ if (FD_OTHER_ERROR == NULL) {
+ Py_XDECREF(mod);
+ return (NULL);
+ }
+ ret = PyModule_AddObject(mod, "FD_OTHER_ERROR", FD_OTHER_ERROR);
+ if (-1 == ret) {
+ Py_XDECREF(FD_OTHER_ERROR);
+ Py_XDECREF(mod);
+ return (NULL);
+ }
+
+ return (mod);
+}
+
diff --git a/src/lib/util/io_utilities.h b/src/lib/util/io_utilities.h
new file mode 100644
index 0000000..ecab3ce
--- /dev/null
+++ b/src/lib/util/io_utilities.h
@@ -0,0 +1,63 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __IO_UTILITIES_H
+#define __IO_UTILITIES_H
+
+#include <cstddef>
+
+namespace isc {
+namespace util {
+
+/// \brief Read Unsigned 16-Bit Integer from Buffer
+///
+/// This is essentially a copy of the isc::util::InputBuffer::readUint16. It
+/// should really be moved into a separate library.
+///
+/// \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.
+///
+/// \return Value of 16-bit integer
+inline uint16_t
+readUint16(const void* buffer) {
+ const uint8_t* byte_buffer = static_cast<const uint8_t*>(buffer);
+
+ uint16_t result = (static_cast<uint16_t>(byte_buffer[0])) << 8;
+ result |= static_cast<uint16_t>(byte_buffer[1]);
+
+ return (result);
+}
+
+/// \brief Write Unisgned 16-Bit Integer to Buffer
+///
+/// This is essentially a copy of isc::util::OutputBuffer::writeUint16. It
+/// should really be moved into a separate library.
+///
+/// \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.
+
+inline void
+writeUint16(uint16_t value, void* buffer) {
+ uint8_t* byte_buffer = static_cast<uint8_t*>(buffer);
+
+ byte_buffer[0] = static_cast<uint8_t>((value & 0xff00U) >> 8);
+ byte_buffer[1] = static_cast<uint8_t>(value & 0x00ffU);
+}
+
+} // namespace util
+} // namespace isc
+
+#endif // __ASIOLINK_UTILITIES_H
diff --git a/src/lib/util/locks.h b/src/lib/util/locks.h
new file mode 100644
index 0000000..971c413
--- /dev/null
+++ b/src/lib/util/locks.h
@@ -0,0 +1,115 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// This file (right now) provides dummy locks
+/// It also contains code to use boost/threads locks:
+///
+/// if USE_BOOST_THREADS is defined, we typedef the relevant classes
+/// and derive from the relevant templates so our dummy locks are
+/// replaced by the boost locks (--enable-boost-threads)
+///
+/// If USE_BOOST_THREADS is NOT defined, all locks are dummy classes
+/// that don't actually do anything. At this moment, only the very
+/// minimal set of methods that we actually use is defined.
+///
+/// Note that we need to include <config.h> in our .cc files for that
+/// to be set. we might want to enfore this at compile time with a check
+/// (TODO)
+
+#ifndef __LOCKS_
+#define __LOCKS_
+
+#ifndef USE_BOOST_THREADS
+
+namespace isc {
+namespace util {
+namespace locks {
+
+class mutex {
+};
+
+class recursive_mutex {
+};
+
+class upgradable_mutex {
+};
+
+template <typename T>
+class sharable_lock {
+public:
+ sharable_lock(T) { }
+};
+
+template <typename T>
+class scoped_lock {
+public:
+ scoped_lock(T) { }
+
+ void lock() {}
+ void unlock() {}
+};
+
+} // namespace locks
+} // namespace util
+} // namespace isc
+
+#else // USE_BOOST_THREADS
+
+// Workaround for a problem with boost and sunstudio 5.10
+// There is a version check in there that appears wrong,
+// which makes including boost/thread.hpp fail
+// This will probably be fixed in a future version of boost,
+// in which case this part can be removed then
+#ifdef NEED_SUNPRO_WORKAROUND
+#if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
+#undef __SUNPRO_CC
+#define __SUNPRO_CC 0x5090
+#endif
+#endif // NEED_SUNPRO_WORKAROUND
+
+#include <boost/thread.hpp>
+#include <boost/interprocess/sync/sharable_lock.hpp>
+#include <boost/interprocess/sync/scoped_lock.hpp>
+#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
+#include <boost/interprocess/sync/interprocess_recursive_mutex.hpp>
+
+namespace isc {
+namespace util {
+namespace locks {
+
+typedef boost::mutex mutex;
+typedef boost::interprocess::interprocess_upgradable_mutex upgradable_mutex;
+typedef boost::interprocess::interprocess_recursive_mutex recursive_mutex;
+
+template <typename T>
+struct sharable_lock : public boost::interprocess::sharable_lock<T> {
+public:
+ sharable_lock(T& mtype) : boost::interprocess::sharable_lock<T>(mtype) {}
+};
+
+
+template <class T>
+struct scoped_lock : public boost::interprocess::scoped_lock<T> {
+public:
+ scoped_lock(T& mtype) : boost::interprocess::scoped_lock<T>(mtype) { }
+};
+
+} // namespace locks
+} // namespace util
+} // namespace isc
+
+
+#endif // USE_BOOST_THREADS
+
+#endif // __LOCKS_
diff --git a/src/lib/util/lru_list.h b/src/lib/util/lru_list.h
new file mode 100644
index 0000000..797c3c9
--- /dev/null
+++ b/src/lib/util/lru_list.h
@@ -0,0 +1,260 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LRU_LIST_H
+#define __LRU_LIST_H
+
+#include <list>
+#include <string>
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <util/locks.h>
+
+namespace isc {
+namespace util {
+
+/// \brief LRU List
+///
+/// Provides the LRU list for the zone and nameserver objects. The list is
+/// created with a specific size. Entries are added to the back of the list
+/// and removed from the front. It is also possible to pull an element out
+/// of the middle of the list and add it to the end of the list, an action that
+/// should be done when the element is referenced.
+///
+/// It is not intended that the class be copied, and the derivation from
+/// boost::noncopyable enforces this.
+template <typename T>
+class LruList : boost::noncopyable {
+public:
+ typedef typename std::list<boost::shared_ptr<T> > lru_list;
+ typedef typename lru_list::iterator iterator;
+
+ /// \brief Dropped Operation
+ ///
+ /// When an object is dropped from the LRU list because it has not been
+ /// accessed for some time, it is possible that the action should trigger
+ /// some other functions. For this reason, it is possible to register
+ /// a list-wide functor object to execute in this casee.
+ ///
+ /// Note that the function does not execute as the result of a call to
+ /// remove() - that is an explicit call and it is assumed that the caller
+ /// will handle any additional operations needed.
+ class Dropped {
+ public:
+ /// \brief Constructor
+ Dropped(){}
+
+ /// \brief Virtual Destructor
+ virtual ~Dropped(){}
+
+ /// \brief Dropped Object Handler
+ ///
+ /// Function object called when the object drops off the end of the
+ /// LRU list.
+ ///
+ /// \param drop Object being dropped.
+ virtual void operator()(T* drop) const = 0;
+ };
+
+ /// \brief Constructor
+ ///
+ /// \param max_size Maximum size of the list before elements are dropped.
+ /// \param dropped Pointer to a function object that will get called as
+ /// elements are dropped. This object will be stored using a shared_ptr,
+ /// so should be allocated with new().
+ LruList(uint32_t max_size = 1000, Dropped* dropped = NULL) :
+ max_size_(max_size), count_(0), dropped_(dropped)
+ {}
+
+ /// \brief Virtual Destructor
+ virtual ~LruList()
+ {}
+
+ /// \brief Add Element
+ ///
+ /// Add a new element to the end of the list.
+ ///
+ /// \param element Reference to the element to add.
+ ///
+ /// \return Handle describing the element in the LRU list.
+ virtual void add(boost::shared_ptr<T>& element);
+
+ /// \brief Remove Element
+ ///
+ /// Removes an element from the list. If the element is not present (i.e.
+ /// its internal list pointer is invalid), this is a no-op.
+ ///
+ /// \param element Reference to the element to remove.
+ virtual void remove(boost::shared_ptr<T>& element);
+
+ /// \brief Touch Element
+ ///
+ /// The name comes from the Unix "touch" command. All this does is to
+ /// move the specified entry from the middle of the list to the end of
+ /// the list.
+ ///
+ /// \param element Reference to the element to touch.
+ virtual void touch(boost::shared_ptr<T>& element);
+
+ /// \brief Drop All the Elements in the List .
+ ///
+ /// All the elements will be dropped from the list container, and their
+ /// drop handler(if there is one) will be called, when done, the size of
+ /// of list will be 0.
+ virtual void clear();
+
+ /// \brief Return Size of the List
+ ///
+ /// An independent count is kept of the list size, as list.size() may take
+ /// some time if the list is big.
+ ///
+ /// \return Number of elements in the list
+ virtual uint32_t size() const {
+
+ // Don't bother to lock the mutex. If an update is in progress, we
+ // receive either the value just before the update or just after it.
+ // Either way, this call could have come just before or just after
+ // that operation, so the value would have been just as uncertain.
+ return count_;
+ }
+
+ /// \brief Return Maximum Size
+ ///
+ /// \return Maximum size of the list
+ virtual uint32_t getMaxSize() const {
+ return max_size_;
+ }
+
+ /// \brief Set Maximum Size
+ ///
+ /// \param max_size New maximum list size
+ virtual void setMaxSize(uint32_t max_size) {
+ max_size_ = max_size;
+ }
+
+private:
+ locks::mutex mutex_; ///< List protection
+ std::list<boost::shared_ptr<T> > lru_; ///< The LRU list itself
+ uint32_t max_size_; ///< Max size of the list
+ uint32_t count_; ///< Count of elements
+ boost::shared_ptr<Dropped> dropped_; ///< Dropped object
+};
+
+// Add entry to the list
+template <typename T>
+void LruList<T>::add(boost::shared_ptr<T>& element) {
+
+ // Protect list against concurrent access
+ locks::scoped_lock<locks::mutex> lock(mutex_);
+
+ // Add the entry and set its pointer field to point into the list.
+ // insert() is used to get the pointer.
+ element->setLruIterator(lru_.insert(lru_.end(), element));
+
+ // ... and update the count while we have the mutex.
+ ++count_;
+
+ // If the count takes us above the maximum size of the list, remove elements
+ // from the front. The current list size could be more than one above the
+ // maximum size of the list if the maximum size was changed after
+ // construction.
+ while (count_ > max_size_) {
+ if (!lru_.empty()) {
+
+ // Run the drop handler (if there is one) on the
+
+ // to-be-dropped object.
+ if (dropped_) {
+ (*dropped_)(lru_.begin()->get());
+ }
+
+ // ... and get rid of it from the list
+ lru_.pop_front();
+ --count_;
+ }
+ else {
+
+ // TODO: Log this condition (count_ > 0 when list empty) -
+ // it should not happen
+ count_ = 0;
+ break;
+ }
+ }
+}
+
+// Remove an element from the list
+template <typename T>
+void LruList<T>::remove(boost::shared_ptr<T>& element) {
+
+ // An element can only be removed it its internal pointer is valid.
+ // If it is, the pointer can be used to access the list because no matter
+ // what other elements are added or removed, the pointer remains valid.
+ //
+ // If the pointer is not valid, this is a no-op.
+ if (element->iteratorValid()) {
+
+ // Is valid, so protect list against concurrent access
+ locks::scoped_lock<locks::mutex> lock(mutex_);
+
+ lru_.erase(element->getLruIterator()); // Remove element from list
+ element->invalidateIterator(); // Invalidate pointer
+ --count_; // One less element
+ }
+}
+
+// Touch an element - remove it from the list and add to the end
+template <typename T>
+void LruList<T>::touch(boost::shared_ptr<T>& element) {
+
+ // As before, if the pointer is not valid, this is a no-op.
+ if (element->iteratorValid()) {
+
+ // Protect list against concurrent access
+ locks::scoped_lock<locks::mutex> lock(mutex_);
+
+ // Move the element to the end of the list.
+ lru_.splice(lru_.end(), lru_, element->getLruIterator());
+
+ // Update the iterator in the element to point to it. We can
+ // offset from end() as a list has a bidirectional iterator.
+ iterator i = lru_.end();
+ element->setLruIterator(--i);
+ }
+}
+
+// Clear the list- when done, the size of list will be 0.
+template <typename T>
+void LruList<T>::clear() {
+ // Protect list against concurrent access
+ locks::scoped_lock<locks::mutex> lock(mutex_);
+
+ // ... and update the count while we have the mutex.
+ count_ = 0;
+ typename std::list<boost::shared_ptr<T> >::iterator iter;
+ if (dropped_) {
+ for (iter = lru_.begin(); iter != lru_.end(); ++iter) {
+ // Call the drop handler.
+ (*dropped_)(iter->get());
+ }
+ }
+
+ lru_.clear();
+}
+
+} // namespace util
+} // namespace isc
+
+#endif // __LRU_LIST_H
diff --git a/src/lib/util/python/mkpywrapper.py.in b/src/lib/util/python/mkpywrapper.py.in
new file mode 100755
index 0000000..4bf7752
--- /dev/null
+++ b/src/lib/util/python/mkpywrapper.py.in
@@ -0,0 +1,100 @@
+#!@PYTHON@
+
+# Copyright (C) 2011 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""This utility program generates a C++ header and implementation files
+that can be used as a template of C++ python binding for a C++ class.
+
+Usage: ./mkpywrapper.py ClassName
+(the script should be run on this directory)
+
+It will generate two files: classname_python.h and classname_python.cc,
+many of whose definitions are in the namespace isc::MODULE_NAME::python.
+By default MODULE_NAME will be 'dns' (because this tool is originally
+intended to be used for the C++ python binding of the DNS library), but
+can be changed via the -m command line option.
+
+The generated files contain code fragments that are commonly used in
+C++ python binding implementations. It will define a class named
+s_ClassName which is a derived class of PyModule and can meet the
+requirement of the CPPPyObjectContainer template class (see
+pycppwrapper_util.h). It also defines (and declares in the header file)
+"classname_type", which is of PyTypeObject and is intended to be used
+to define details of the python bindings for the ClassName class.
+
+In many cases the header file can be used as a startpoint of the
+binding development without modification. But you may want to make
+ClassName::cppobj a constant variable (and you should if you can).
+Many definitions of classname_python.cc should also be able to be used
+just as defined, but some will need to be changed or removed. In
+particular, you should at least adjust ClassName_init(). You'll
+probably also need to add more definitions to that file to provide
+complete features of the C++ class.
+"""
+
+import datetime, string, sys
+from optparse import OptionParser
+
+# Remember the current year to produce the copyright boilerplate
+YEAR = datetime.date.today().timetuple()[0]
+
+def dump_file(out_file, temp_file, class_name, module):
+ for line in temp_file.readlines():
+ line = line.replace("@YEAR@", str(YEAR))
+ line = line.replace("@CPPCLASS at _H", class_name.upper() + "_H")
+ line = line.replace("@CPPCLASS@", class_name)
+ line = line.replace("@cppclass@", class_name.lower())
+ line = line.replace("@MODULE@", module)
+ out_file.write(line)
+
+def dump_wrappers(class_name, output, module):
+ try:
+ if output == "-":
+ header_file = sys.stdout
+ else:
+ header_file = open(output + "_python.h", "w")
+ header_template_file = open("wrapper_template.h", "r")
+ if output == "-":
+ impl_file = sys.stdout
+ else:
+ impl_file = open(output + "_python.cc", "w")
+ impl_template_file = open("wrapper_template.cc", "r")
+ except:
+ sys.stderr.write('Failed to open C++ file(s)\n')
+ sys.exit(1)
+ dump_file(header_file, header_template_file, class_name, module)
+ dump_file(impl_file, impl_template_file, class_name, module)
+
+usage = '''usage: %prog [options] class_name'''
+
+if __name__ == "__main__":
+ parser = OptionParser(usage=usage)
+ parser.add_option('-o', '--output', action='store', dest='output',
+ default=None, metavar='FILE',
+ help='prefix of output file names [default: derived from the class name]')
+ parser.add_option('-m', '--module', action='store', dest='module',
+ default='dns',
+ help='C++ module name of the wrapper (for namespaces) [default: dns]')
+ (options, args) = parser.parse_args()
+
+ if len(args) == 0:
+ parser.error('input file is missing')
+
+ class_name = args[0]
+ if not options.output:
+ options.output = class_name.lower()
+
+ dump_wrappers(class_name, options.output, options.module)
diff --git a/src/lib/util/python/pycppwrapper_util.h b/src/lib/util/python/pycppwrapper_util.h
new file mode 100644
index 0000000..fd55c19
--- /dev/null
+++ b/src/lib/util/python/pycppwrapper_util.h
@@ -0,0 +1,308 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYCPPWRAPPER_UTIL_H
+#define __PYCPPWRAPPER_UTIL_H 1
+
+#include <Python.h>
+
+#include <exceptions/exceptions.h>
+
+/**
+ * @file pycppwrapper_util.h
+ * @short Shared definitions for python/C(++) API
+ *
+ * This utility defines a set of convenient wrappers for the python C API
+ * to use it safely from our C++ bindings. The python C API has many pitfalls
+ * such as not-so-consistent reference count policies. Also, many existing
+ * examples are careless about error handling. It's easy to find on the net
+ * example (even of "production use") python extensions like this:
+ *
+ * \code
+ * new_exception = PyErr_NewException("mymodule.Exception", NULL, NULL);
+ * // new_exception can be NULL, in which case the call to
+ * // PyModule_AddObject will cause a surprising disruption.
+ * PyModule_AddObject(mymodule, "Exception", new_exception); \endcode
+ *
+ * When using the python C API with C++, we should also be careful about
+ * exception safety. The underlying C++ code (including standard C++ libraries
+ * and memory allocation) can throw exceptions, in which case we need to
+ * make sure any intermediate python objects are cleaned up (we also need to
+ * catch the C++ exceptions inside the binding and convert them to python
+ * errors, but that's a different subject). This is not a trivial task
+ * because the python objects are represented as bare C pointers (so there's
+ * no destructor) and we need to address the exception safety along with python
+ * reference counters (so we cannot naively apply standard smart pointers).
+ *
+ * This utility tries to help address these issues.
+ *
+ * Also, it's intentional that this is a header-only utility. This way the
+ * C++ loadable module won't depend on another C++ library (which is not
+ * necessarily wrong, but would increase management cost such as link-time
+ * troubles only for a small utility feature).
+ */
+
+namespace isc {
+namespace util {
+namespace python {
+
+/// This is thrown inside this utility when it finds a NULL pointer is passed
+/// when it should not be NULL.
+class PyCPPWrapperException : public isc::Exception {
+public:
+ PyCPPWrapperException(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// This helper class is similar to the standard autoptr and manages PyObject
+/// using some kind of RAII techniques. It is, however, customized for the
+/// python C API.
+///
+/// A PyObjectContainer object is constructed with a pointer to PyObject,
+/// which is often just created dynamically. The caller will eventually
+/// attach the object to a different python object (often a module or class)
+/// via specific methods or directly return it to the python interpreter.
+///
+/// There are two cases in destructing the object: with or without decreasing
+/// a reference to the PyObject. If the object is intended to be an argument
+/// to another python C library that increases the reference to the object for
+/// itself, we should normally release our own reference; otherwise the
+/// reference will leak and the object won't be garbage collected. Also, when
+/// an unexpected error happens in the form of C++ exception, we should
+/// release the reference to prevent resource leak.
+///
+/// In some other cases, we should simply give our reference to the caller.
+/// That is the case when the created object itself is a return value of
+/// an extended python method written in the C++ binding. Likewise, some
+/// python C library functions "steal" the reference. In these cases we
+/// should not decrease the reference; otherwise it would cause duplicate free.
+///
+/// By default, the destructor of this class releases the reference to the
+/// PyObject. If this behavior is desirable, you can extract the original
+/// bare pointer to the PyObject by the \c get() method. If you don't want
+/// the reference to be decreased, the original bare pointer should be
+/// extracted using the \c release() method.
+///
+/// There are two convenience methods for commonly used operations:
+/// \c installAsClassVariable() to add the PyObject as a class variable
+/// and \c installToModule to add the PyObject to a specified python module.
+/// These methods (at least to some extent) take care of the reference to
+/// the object (either release or keep) depending on the usage context so
+/// that the user don't have to worry about it.
+///
+/// On construction, this class expects the pointer can be NULL.
+/// If it happens it immediately throws a \c PyCPPWrapperException exception.
+/// This behavior is to convert failures in the python C API (such as
+/// PyObject_New() returning NULL) to C++ exception so that we can unify
+/// error handling in the style of C++ exceptions.
+///
+/// Examples 1: To create a tuple of two python objects, do this:
+///
+/// \code
+/// try {
+/// PyObjectContainer container0(Py_BuildValue("I", 0));
+/// PyObjectContainer container1(Py_BuildValue("s", cppobj.toText().c_str()));
+/// return (Py_BuildValue("OO", container0.get(), container1.get()));
+/// } catch { ... set python exception, etc ... } \endcode
+///
+/// Commonly deployed buggy implementation to achieve this would be like this:
+/// \code
+/// return (Py_BuildValue("OO", Py_BuildValue("I", 0),
+/// Py_BuildValue("s", cppobj.toText().c_str())));
+/// \endcode
+/// One clear bug of this code is that references to the element objects of
+/// the tuple will leak.
+/// (Assuming \c cppobj.toText() can throw) this code is also not exception
+/// safe; if \c cppobj.toText() throws the reference to the first object
+/// will leak, even if the code tried to do the necessary cleanup in the
+/// successful case.
+/// Further, this code naively passes the result of the first two calls to
+/// \c Py_BuildValue() to the third one even if they can be NULL.
+/// In this specific case, it happens to be okay because \c Py_BuildValue()
+/// accepts NULL and treats it as an indication of error. But not all
+/// python C library works that way (remember, the API is so inconsistent)
+/// and we need to refer to the API manual every time we have to worry about
+/// passing a NULL object to a library function. We'd certainly like to
+/// avoid such development overhead. The code using \c PyObjectContainer
+/// addresses all these problems.
+///
+/// Examples 2: Install a (constant) variable to a class.
+///
+/// \code
+/// try {
+/// // installClassVariable is a wrapper of
+/// // PyObjectContainer::installAsClassVariable. See below.
+/// installClassVariable(myclass_type, "SOME_CONSTANT",
+/// Py_BuildValue("I", 0));
+/// } catch { ... }
+/// \endcode
+///
+/// Examples 3: Install a custom exception to a module.
+///
+/// \code
+/// PyObject* new_exception; // publicly visible
+/// ...
+/// try {
+/// new_exception = PyErr_NewException("mymodule.NewException",
+/// NULL, NULL);
+/// PyObjectContainer(new_exception).installToModule(mymodule,
+/// "NewException");
+/// } catch { ... }
+/// \endcode
+///
+/// Note that \c installToModule() keeps the reference to \c new_exception
+/// by default. This is a common practice when we introduce a custom
+/// exception in a python biding written in C/C++. See the code comment
+/// of the method for more details.
+struct PyObjectContainer {
+ PyObjectContainer(PyObject* obj) : obj_(obj) {
+ if (obj_ == NULL) {
+ isc_throw(PyCPPWrapperException, "Unexpected NULL PyObject, "
+ "probably due to short memory");
+ }
+ }
+ virtual ~PyObjectContainer() {
+ if (obj_ != NULL) {
+ Py_DECREF(obj_);
+ }
+ }
+ PyObject* get() {
+ return (obj_);
+ }
+ PyObject* release() {
+ PyObject* ret = obj_;
+ obj_ = NULL;
+ return (ret);
+ }
+
+ // Install the enclosed PyObject to the specified python class 'pyclass'
+ // as a variable named 'name'.
+ void installAsClassVariable(PyTypeObject& pyclass, const char* name) {
+ if (PyDict_SetItemString(pyclass.tp_dict, name, obj_) < 0) {
+ isc_throw(PyCPPWrapperException, "Failed to set a class variable, "
+ "probably due to short memory");
+ }
+ // Ownership successfully transferred to the class object. We'll let
+ // it be released in the destructor.
+ }
+
+ // Install the enclosed PyObject to the specified module 'mod' as an
+ // object named 'name'.
+ // By default, this method explicitly keeps the reference to the object
+ // even after the module "steals" it. To cancel this behavior and give
+ // the reference to the module completely, the third parameter 'keep_ref'
+ // should be set to false.
+ void installToModule(PyObject* mod, const char* name,
+ bool keep_ref = true)
+ {
+ if (PyModule_AddObject(mod, name, obj_) < 0) {
+ isc_throw(PyCPPWrapperException, "Failed to add an object to "
+ "module, probably due to short memory");
+ }
+ // PyModule_AddObject has "stolen" the reference, so unless we
+ // have to retain it ourselves we don't (shouldn't) decrease it.
+ // However, we actually often need to keep our own reference because
+ // objects added to a module are often referenced via non local
+ // C/C++ variables in various places of the C/C++ code. In order
+ // for the code to run safely even if some buggy/evil python program
+ // performs 'del mod.obj', we need the extra reference. See, e.g.:
+ // http://docs.python.org/py3k/c-api/init.html#Py_Initialize
+ // http://mail.python.org/pipermail/python-dev/2005-June/054238.html
+ if (keep_ref) {
+ Py_INCREF(obj_);
+ }
+ obj_ = NULL;
+ }
+
+protected:
+ PyObject* obj_;
+};
+
+/// This templated class is a derived class of \c PyObjectContainer and
+/// manages C++-class based python objects.
+///
+/// The template parameter \c PYSTRUCT must be a derived class (structure) of
+/// \c PyObject that has a member variable named \c cppobj, which must be a
+/// a pointer to \c CPPCLASS (the second template parameter).
+///
+/// For example, to define a custom python class based on a C++ class, MyClass,
+/// we'd define a class (struct) named \c s_MyClass like this:
+/// \code
+/// class s_MyClass : public PyObject {
+/// public:
+/// s_MyClass() : cppobj(NULL) {}
+/// MyClass* cppobj;
+/// };
+/// \endcode
+///
+/// And, to build and return a python version of MyClass object, write the
+/// following C++ code:
+/// \code
+/// typedef CPPPyObjectContainer<s_MyClass, MyClass> MyContainer;
+/// try {
+/// // below, myclass_type is of \c PyTypeObject that defines
+/// // a python class (type) for MyClass
+/// MyContainer container(PyObject_New(s_MyClass, myclass_type));
+/// container.set(new MyClass());
+/// return (container.release()); // give the reference to the caller
+/// } catch { ... }
+/// \endcode
+///
+/// This code prevents bugs like NULL pointer dereference when \c PyObject_New
+/// fails or resource leaks when new'ing \c MyClass results in an exception.
+/// Note that we use \c release() (derived from the base class) instead of
+/// \c get(); in this case we should simply pass the reference generated in
+/// \c PyObject_New() to the caller.
+template <typename PYSTRUCT, typename CPPCLASS>
+struct CPPPyObjectContainer : public PyObjectContainer {
+ CPPPyObjectContainer(PYSTRUCT* obj) : PyObjectContainer(obj) {}
+
+ // This method associates a C++ object with the corresponding python
+ // object enclosed in this class.
+ void set(CPPCLASS* value) {
+ if (value == NULL) {
+ isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, "
+ "probably due to short memory");
+ }
+ static_cast<PYSTRUCT*>(obj_)->cppobj = value;
+ }
+
+ // This is a convenience short cut to associate a C++ object with the
+ // python object and install it to the specified python class \c pyclass
+ // as a variable named \c name.
+ void installAsClassVariable(PyTypeObject& pyclass, const char* name,
+ CPPCLASS* value)
+ {
+ set(value);
+ PyObjectContainer::installAsClassVariable(pyclass, name);
+ }
+};
+
+/// A shortcut function to install a python class variable.
+///
+/// It installs a python object \c obj to a specified class \c pyclass
+/// as a variable named \c name.
+inline void
+installClassVariable(PyTypeObject& pyclass, const char* name, PyObject* obj) {
+ PyObjectContainer(obj).installAsClassVariable(pyclass, name);
+}
+
+} // namespace python
+} // namespace util
+} // namespace isc
+#endif // __PYCPPWRAPPER_UTIL_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/python/wrapper_template.cc b/src/lib/util/python/wrapper_template.cc
new file mode 100644
index 0000000..691e4bf
--- /dev/null
+++ b/src/lib/util/python/wrapper_template.cc
@@ -0,0 +1,309 @@
+// Copyright (C) @YEAR@ 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.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include "@cppclass at _python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::@MODULE@;
+using namespace isc::@MODULE@::python;
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+//
+// @CPPCLASS@
+//
+
+// Trivial constructor.
+s_ at CPPCLASS@::s_ at CPPCLASS@() : cppobj(NULL) {
+}
+
+namespace {
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_ at CPPCLASS@, @CPPCLASS@> @CPPCLASS at Container;
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+
+// General creation and destruction
+int @CPPCLASS at _init(s_ at CPPCLASS@* self, PyObject* args);
+void @CPPCLASS at _destroy(s_ at CPPCLASS@* self);
+
+// These are the functions we export
+// ADD/REMOVE/MODIFY THE FOLLOWING AS APPROPRIATE FOR THE ACTUAL CLASS.
+//
+PyObject* @CPPCLASS at _toText(const s_ at CPPCLASS@* const self);
+PyObject* @CPPCLASS at _str(PyObject* self);
+PyObject* @CPPCLASS at _richcmp(const s_ at CPPCLASS@* const self,
+ const s_ at CPPCLASS@* const other, int op);
+
+// This is quite specific pydnspp. For other wrappers this should probably
+// be removed.
+PyObject* @CPPCLASS at _toWire(const s_ at CPPCLASS@* self, PyObject* args);
+
+// These are the functions we export
+// For a minimal support, we don't need them.
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef @CPPCLASS at _methods[] = {
+ { "to_text", reinterpret_cast<PyCFunction>(@CPPCLASS at _toText), METH_NOARGS,
+ "Returns the text representation" },
+ // This is quite specific pydnspp. For other wrappers this should probably
+ // be removed:
+ { "to_wire", reinterpret_cast<PyCFunction>(@CPPCLASS at _toWire), METH_VARARGS,
+ "Converts the @CPPCLASS@ object to wire format.\n"
+ "The argument can be either a MessageRenderer or an object that "
+ "implements the sequence interface. If the object is mutable "
+ "(for instance a bytearray()), the wire data is added in-place.\n"
+ "If it is not (for instance a bytes() object), a new object is "
+ "returned" },
+ { NULL, NULL, 0, NULL }
+};
+
+// This is a template of typical code logic of python class initialization
+// with C++ backend. You'll need to adjust it according to details of the
+// actual C++ class.
+int
+ at CPPCLASS@_init(s_ at CPPCLASS@* self, PyObject* args) {
+ try {
+ if (PyArg_ParseTuple(args, "REPLACE ME")) {
+ // YOU'LL NEED SOME VALIDATION, PREPARATION, ETC, HERE.
+ self->cppobj = new @CPPCLASS@(/*NECESSARY PARAMS*/);
+ return (0);
+ }
+ } catch (const exception& ex) {
+ const string ex_what = "Failed to construct @CPPCLASS@ object: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (-1);
+ } catch (...) {
+ PyErr_SetString(po_IscException,
+ "Unexpected exception in constructing @CPPCLASS@");
+ return (-1);
+ }
+
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to @CPPCLASS@ constructor");
+
+ return (-1);
+}
+
+// This is a template of typical code logic of python object destructor.
+// In many cases you can use it without modification, but check that carefully.
+void
+ at CPPCLASS@_destroy(s_ at CPPCLASS@* const self) {
+ delete self->cppobj;
+ self->cppobj = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+// This should be able to be used without modification as long as the
+// underlying C++ class has toText().
+PyObject*
+ at CPPCLASS@_toText(const s_ at CPPCLASS@* const self) {
+ try {
+ // toText() could throw, so we need to catch any exceptions below.
+ return (Py_BuildValue("s", self->cppobj->toText().c_str()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to convert @CPPCLASS@ object to text: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "converting @CPPCLASS@ object to text");
+ }
+ return (NULL);
+}
+
+PyObject*
+ at CPPCLASS@_str(PyObject* self) {
+ // Simply call the to_text method we already defined
+ return (PyObject_CallMethod(self, const_cast<char*>("to_text"),
+ const_cast<char*>("")));
+}
+
+PyObject*
+ at CPPCLASS@_richcmp(const s_ at CPPCLASS@* const self,
+ const s_ at CPPCLASS@* const other,
+ const int op)
+{
+ bool c = false;
+
+ // Check for null and if the types match. If different type,
+ // simply return False
+ if (other == NULL || (self->ob_type != other->ob_type)) {
+ Py_RETURN_FALSE;
+ }
+
+ // Only equals and not equals here, unorderable type
+ switch (op) {
+ case Py_LT:
+ PyErr_SetString(PyExc_TypeError, "Unorderable type; @CPPCLASS@");
+ return (NULL);
+ case Py_LE:
+ PyErr_SetString(PyExc_TypeError, "Unorderable type; @CPPCLASS@");
+ return (NULL);
+ case Py_EQ:
+ c = (*self->cppobj == *other->cppobj);
+ break;
+ case Py_NE:
+ c = (*self->cppobj != *other->cppobj);
+ break;
+ case Py_GT:
+ PyErr_SetString(PyExc_TypeError, "Unorderable type; @CPPCLASS@");
+ return (NULL);
+ case Py_GE:
+ PyErr_SetString(PyExc_TypeError, "Unorderable type; @CPPCLASS@");
+ return (NULL);
+ }
+ if (c) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+} // end of unnamed namespace
+
+namespace isc {
+namespace @MODULE@ {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_ at CPPCLASS@
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject @cppclass at _type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp. at CPPCLASS@",
+ sizeof(s_ at CPPCLASS@), // tp_basicsize
+ 0, // tp_itemsize
+ reinterpret_cast<destructor>(@CPPCLASS at _destroy), // tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ // THIS MAY HAVE TO BE CHANGED TO NULL:
+ @CPPCLASS at _str, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "The @CPPCLASS@ class objects is...(COMPLETE THIS)",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ // THIS MAY HAVE TO BE CHANGED TO NULL:
+ reinterpret_cast<richcmpfunc>(@CPPCLASS at _richcmp), // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ @CPPCLASS at _methods, // tp_methods
+ NULL, // tp_members
+ NULL, // tp_getset
+ NULL, // tp_base
+ NULL, // tp_dict
+ NULL, // tp_descr_get
+ NULL, // tp_descr_set
+ 0, // tp_dictoffset
+ reinterpret_cast<initproc>(@CPPCLASS at _init), // tp_init
+ NULL, // tp_alloc
+ PyType_GenericNew, // tp_new
+ NULL, // tp_free
+ NULL, // tp_is_gc
+ NULL, // tp_bases
+ NULL, // tp_mro
+ NULL, // tp_cache
+ NULL, // tp_subclasses
+ NULL, // tp_weaklist
+ NULL, // tp_del
+ 0 // tp_version_tag
+};
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_ at CPPCLASS@(PyObject* mod) {
+ // We initialize the static description object with PyType_Ready(),
+ // then add it to the module. This is not just a check! (leaving
+ // this out results in segmentation faults)
+ if (PyType_Ready(&@cppclass at _type) < 0) {
+ return (false);
+ }
+ void* p = &@cppclass at _type;
+ if (PyModule_AddObject(mod, "@CPPCLASS@", static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+ Py_INCREF(&@cppclass at _type);
+
+ // The following template is the typical procedure for installing class
+ // variables. If the class doesn't have a class variable, remove the
+ // entire try-catch clauses.
+ try {
+ // Constant class variables
+ installClassVariable(@cppclass at _type, "REPLACE_ME",
+ Py_BuildValue("REPLACE ME"));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpected failure in @CPPCLASS@ initialization: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in @CPPCLASS@ initialization");
+ return (false);
+ }
+
+ return (true);
+}
+
+PyObject*
+create at CPPCLASS@Object(const @CPPCLASS@& source) {
+ @CPPCLASS at Container container =
+ PyObject_New(s_ at CPPCLASS@, &@cppclass at _type);
+ container.set(new @CPPCLASS@(source));
+ return (container.release());
+}
+} // namespace python
+} // namespace @MODULE@
+} // namespace isc
diff --git a/src/lib/util/python/wrapper_template.h b/src/lib/util/python/wrapper_template.h
new file mode 100644
index 0000000..d68a658
--- /dev/null
+++ b/src/lib/util/python/wrapper_template.h
@@ -0,0 +1,59 @@
+// Copyright (C) @YEAR@ 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 __PYTHON_ at CPPCLASS@_H
+#define __PYTHON_ at CPPCLASS@_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace @MODULE@ {
+class @CPPCLASS@;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_ at CPPCLASS@ : public PyObject {
+public:
+ s_ at CPPCLASS@();
+ @CPPCLASS@* cppobj;
+};
+
+extern PyTypeObject @cppclass at _type;
+
+bool initModulePart_ at CPPCLASS@(PyObject* mod);
+
+// Note: this utility function works only when @CPPCLASS@ is a copy
+// constructable.
+// And, it would only be useful when python binding needs to create this
+// object frequently. Otherwise, it would (or should) probably better to
+// remove the declaration and definition of this function.
+//
+/// This is A simple shortcut to create a python @CPPCLASS@ object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called with in a try block
+/// followed by necessary setup for python exception.
+PyObject* create at CPPCLASS@Object(const @CPPCLASS@& source);
+
+} // namespace python
+} // namespace @MODULE@
+} // namespace isc
+#endif // __PYTHON_ at CPPCLASS@_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/pyunittests/Makefile.am b/src/lib/util/pyunittests/Makefile.am
new file mode 100644
index 0000000..63ccf2a
--- /dev/null
+++ b/src/lib/util/pyunittests/Makefile.am
@@ -0,0 +1,17 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+pyexec_LTLIBRARIES = pyunittests_util.la
+pyunittests_util_la_SOURCES = pyunittests_util.cc
+pyunittests_util_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+pyunittests_util_la_LDFLAGS = $(PYTHON_LDFLAGS)
+# Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be
+# placed after -Wextra defined in AM_CXXFLAGS
+pyunittests_util_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
+
+# Python prefers .so, while some OSes (specifically MacOS) use a different
+# suffix for dynamic objects. -module is necessary to work this around.
+pyunittests_util_la_LDFLAGS += -module
+pyunittests_util_la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
+pyunittests_util_la_LIBADD += $(PYTHON_LIB)
diff --git a/src/lib/util/pyunittests/pyunittests_util.cc b/src/lib/util/pyunittests/pyunittests_util.cc
new file mode 100644
index 0000000..d266c84
--- /dev/null
+++ b/src/lib/util/pyunittests/pyunittests_util.cc
@@ -0,0 +1,84 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <Python.h>
+
+#include <stdint.h>
+
+// see util/time_utilities.h
+namespace isc {
+namespace util {
+namespace detail {
+extern int64_t (*gettimeFunction)();
+}
+}
+}
+
+namespace {
+int64_t fake_current_time;
+
+int64_t
+getFakeTime() {
+ return (fake_current_time);
+}
+
+PyObject*
+fixCurrentTime(PyObject*, PyObject* args) {
+ PyObject* maybe_none;
+ if (PyArg_ParseTuple(args, "L", &fake_current_time)) {
+ isc::util::detail::gettimeFunction = getFakeTime;
+ } else if (PyArg_ParseTuple(args, "O", &maybe_none) &&
+ maybe_none == Py_None) {
+ isc::util::detail::gettimeFunction = NULL;
+ } else {
+ PyErr_SetString(PyExc_TypeError, "Invalid arguments to "
+ "pyunittests_util.fix_current_time");
+ return (NULL);
+ }
+
+ PyErr_Clear();
+ Py_RETURN_NONE;
+}
+
+PyMethodDef PyUnittestsUtilMethods[] = {
+ { "fix_current_time", fixCurrentTime, METH_VARARGS,
+ "Fix the current system time at the specified (fake) value.\n\n"
+ "This is useful for testing modules that depend on the current time.\n"
+ "Note that it only affects C++ modules that use gettimeWrapper() "
+ "defined in libutil, which allows a hook for testing.\n"
+ "If an integer (signed 64bit) is given, the current time will be fixed "
+ "to that value; if None is specified (which is the default) the use of "
+ "faked time will be canceled."
+ },
+ { NULL, NULL, 0, NULL}
+};
+
+PyModuleDef pyunittests_util = {
+ { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
+ "pyunittests_util",
+ "This module is a collection of utilities useful for testing "
+ "the BIND 10 C++ binding modules.",
+ -1,
+ PyUnittestsUtilMethods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+} // end of unnamed namespace
+
+PyMODINIT_FUNC
+PyInit_pyunittests_util(void) {
+ return (PyModule_Create(&pyunittests_util));
+}
diff --git a/src/lib/util/random/qid_gen.cc b/src/lib/util/random/qid_gen.cc
new file mode 100644
index 0000000..43041ad
--- /dev/null
+++ b/src/lib/util/random/qid_gen.cc
@@ -0,0 +1,57 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// qid_gen defines a generator for query id's
+//
+// We probably want to merge this with the weighted random in the nsas
+// (and other parts where we need randomness, perhaps another thing
+// for a general libutil?)
+
+#include <util/random/qid_gen.h>
+
+#include <sys/time.h>
+
+namespace isc {
+namespace util {
+namespace random {
+
+QidGenerator qid_generator_instance;
+
+QidGenerator&
+QidGenerator::getInstance() {
+ return (qid_generator_instance);
+}
+
+QidGenerator::QidGenerator() : dist_(0, 65535),
+ vgen_(generator_, dist_)
+{
+ seed();
+}
+
+void
+QidGenerator::seed() {
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ generator_.seed((tv.tv_sec * 1000000) + tv.tv_usec);
+}
+
+uint16_t
+QidGenerator::generateQid() {
+ return (vgen_());
+}
+
+
+} // namespace random
+} // namespace util
+} // namespace isc
diff --git a/src/lib/util/random/qid_gen.h b/src/lib/util/random/qid_gen.h
new file mode 100644
index 0000000..1af43c1
--- /dev/null
+++ b/src/lib/util/random/qid_gen.h
@@ -0,0 +1,87 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// qid_gen defines a generator for query id's
+//
+// We probably want to merge this with the weighted random in the nsas
+// (and other parts where we need randomness, perhaps another thing
+// for a general libutil?)
+
+#ifndef __QID_GEN_H
+#define __QID_GEN_H
+
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <boost/random/variate_generator.hpp>
+
+namespace isc {
+namespace util {
+namespace random {
+
+/// This class generates Qids for outgoing queries
+///
+/// It is implemented as a singleton; the public way to access it
+/// is to call getInstance()->generateQid().
+///
+/// It automatically seeds it with the current time when it is first
+/// used.
+class QidGenerator {
+public:
+ /// \brief Returns the singleton instance of the QidGenerator
+ ///
+ /// Returns a reference to the singleton instance of the generator
+ static QidGenerator& getInstance();
+
+ /// \brief Default constructor
+ ///
+ /// It is recommended that getInstance is used rather than creating
+ /// separate instances of this class.
+ ///
+ /// The constructor automatically seeds the generator with the
+ /// current time.
+ QidGenerator();
+
+ /// Generate a Qid
+ ///
+ /// \return A random Qid
+ uint16_t generateQid();
+
+ /// \brief Seeds the QidGenerator (based on the current time)
+ ///
+ /// This is automatically called by the constructor
+ void seed();
+
+private:
+ // "Mersenne Twister: A 623-dimensionally equidistributed
+ // uniform pseudo-random number generator", Makoto Matsumoto and
+ // Takuji Nishimura, ACM Transactions on Modeling and Computer
+ // Simulation: Special Issue on Uniform Random Number Generation,
+ // Vol. 8, No. 1, January 1998, pp. 3-30.
+ //
+ // mt19937 is an implementation of one of the pseudo random
+ // generators described in this paper.
+ boost::mt19937 generator_;
+
+ // For qid's we want a uniform distribution
+ boost::uniform_int<> dist_;
+
+ boost::variate_generator<boost::mt19937&, boost::uniform_int<> > vgen_;
+};
+
+
+} // namespace random
+} // namespace util
+} // namespace isc
+
+#endif // __QID_GEN_H
diff --git a/src/lib/util/random/random_number_generator.h b/src/lib/util/random/random_number_generator.h
new file mode 100644
index 0000000..485ea7a
--- /dev/null
+++ b/src/lib/util/random/random_number_generator.h
@@ -0,0 +1,208 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __NSAS_RANDOM_NUMBER_GENERATOR_H
+#define __NSAS_RANDOM_NUMBER_GENERATOR_H
+
+#include <algorithm>
+#include <cmath>
+#include <numeric>
+
+#include <exceptions/exceptions.h>
+
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <boost/random/uniform_real.hpp>
+#include <boost/random/variate_generator.hpp>
+
+namespace isc {
+namespace util {
+namespace random {
+
+class InvalidLimits : public isc::BadValue {
+public:
+ InvalidLimits(const char* file, size_t line, const char* what) :
+ isc::BadValue(file, line, what) {}
+};
+
+class SumNotOne : public isc::BadValue {
+public:
+ SumNotOne(const char* file, size_t line, const char* what) :
+ isc::BadValue(file, line, what) {}
+};
+
+class InvalidProbValue : public isc::BadValue {
+public:
+ InvalidProbValue(const char* file, size_t line, const char* what) :
+ isc::BadValue(file, line, what) {}
+};
+
+
+
+/// \brief Uniform random integer generator
+///
+/// Generate uniformly distributed integers in range of [min, max]
+class UniformRandomIntegerGenerator{
+public:
+ /// \brief Constructor
+ ///
+ /// \param min The minimum number in the range
+ /// \param max The maximum number in the range
+ UniformRandomIntegerGenerator(int min, int max):
+ min_(std::min(min, max)), max_(std::max(min, max)),
+ dist_(min_, max_), generator_(rng_, dist_)
+ {
+ // To preserve the restriction of the underlying uniform_int class (and
+ // to retain compatibility with earlier versions of the class), we will
+ // abort if the minimum and maximum given are the wrong way round.
+ if (min > max) {
+ isc_throw(InvalidLimits, "minimum limit is greater than maximum "
+ "when initializing UniformRandomIntegerGenerator");
+ }
+
+ // Init with the current time
+ rng_.seed(time(NULL));
+ }
+
+ /// \brief Generate uniformly distributed integer
+ int operator()() { return generator_(); }
+private:
+ /// Hide default and copy constructor
+ UniformRandomIntegerGenerator();///< Default constructor
+ UniformRandomIntegerGenerator(const UniformRandomIntegerGenerator&); ///< Copy constructor
+
+ int min_; ///< The minimum integer that can generate
+ int max_; ///< The maximum integer that can generate
+ boost::uniform_int<> dist_; ///< Distribute uniformly.
+ boost::mt19937 rng_; ///< Mersenne Twister: A 623-dimensionally equidistributed uniform pseudo-random number generator
+ boost::variate_generator<boost::mt19937&, boost::uniform_int<> > generator_; ///< Uniform generator
+};
+
+/// \brief Weighted random integer generator
+///
+/// Generate random integers according different probabilities
+class WeightedRandomIntegerGenerator {
+public:
+ /// \brief Constructor
+ ///
+ /// \param probabilities The probabies for all the integers, the probability must be
+ /// between 0 and 1.0, the sum of probabilities must be equal to 1.
+ /// For example, if the probabilities contains the following values:
+ /// 0.5 0.3 0.2, the 1st integer will be generated more frequently than the
+ /// other integers and the probability is proportional to its value.
+ /// \param min The minimum integer that generated, other integers will be
+ /// min, min + 1, ..., min + probabilities.size() - 1
+ WeightedRandomIntegerGenerator(const std::vector<double>& probabilities,
+ size_t min = 0):
+ dist_(0, 1.0), uniform_real_gen_(rng_, dist_), min_(min)
+ {
+ // The probabilities must be valid. Checking is quite an expensive
+ // operation, so is only done in a debug build.
+ assert(areProbabilitiesValid(probabilities));
+
+ // Calculate the partial sum of probabilities
+ std::partial_sum(probabilities.begin(), probabilities.end(),
+ std::back_inserter(cumulative_));
+ // Init with the current time
+ rng_.seed(time(NULL));
+ }
+
+ /// \brief Default constructor
+ ///
+ WeightedRandomIntegerGenerator():
+ dist_(0, 1.0), uniform_real_gen_(rng_, dist_), min_(0)
+ {
+ }
+
+ /// \brief Reset the probabilities
+ ///
+ /// Change the weights of each integers
+ /// \param probabilities The probabies for all the integers
+ /// \param min The minimum integer that generated
+ void reset(const std::vector<double>& probabilities, size_t min = 0)
+ {
+ // The probabilities must be valid.
+ assert(areProbabilitiesValid(probabilities));
+
+ // Reset the cumulative sum
+ cumulative_.clear();
+
+ // Calculate the partial sum of probabilities
+ std::partial_sum(probabilities.begin(), probabilities.end(),
+ std::back_inserter(cumulative_));
+
+ // Reset the minimum integer
+ min_ = min;
+ }
+
+ /// \brief Generate weighted random integer
+ size_t operator()()
+ {
+ return std::lower_bound(cumulative_.begin(), cumulative_.end(), uniform_real_gen_())
+ - cumulative_.begin() + min_;
+ }
+
+private:
+ /// \brief Check the validation of probabilities vector
+ ///
+ /// The probability must be in range of [0, 1.0] and the sum must be equal
+ /// to 1.0. Empty probabilities are also valid.
+ ///
+ /// Checking the probabilities is quite an expensive operation, so it is
+ /// only done during a debug build (via a call through assert()). However,
+ /// instead of letting assert() call abort(), if this method encounters an
+ /// error, an exception is thrown. This makes unit testing somewhat easier.
+ ///
+ /// \param probabilities Vector of probabilities.
+ bool areProbabilitiesValid(const std::vector<double>& probabilities) const
+ {
+ typedef std::vector<double>::const_iterator Iterator;
+ double sum = probabilities.empty() ? 1.0 : 0.0;
+ for(Iterator it = probabilities.begin(); it != probabilities.end(); ++it){
+ //The probability must be in [0, 1.0]
+ if(*it < 0.0 || *it > 1.0) {
+ isc_throw(InvalidProbValue,
+ "probability must be in the range 0..1");
+ }
+
+ sum += *it;
+ }
+
+ double epsilon = 0.0001;
+ // The sum must be equal to 1
+ if (std::fabs(sum - 1.0) >= epsilon) {
+ isc_throw(SumNotOne, "Sum of probabilities is not equal to 1");
+ }
+
+ return true;
+ }
+
+ std::vector<double> cumulative_; ///< Partial sum of the probabilities
+ boost::mt19937 rng_; ///< Mersenne Twister: A 623-dimensionally equidistributed uniform pseudo-random number generator
+ boost::uniform_real<> dist_; ///< Uniformly distributed real numbers
+
+ // Shortcut typedef
+ // This typedef is placed directly before its use, as the sunstudio
+ // compiler could not handle it being anywhere else (don't know why)
+ typedef boost::variate_generator<boost::mt19937&, boost::uniform_real<> > UniformRealGenerator;
+ UniformRealGenerator uniform_real_gen_; ///< Uniformly distributed random real numbers generator
+
+ size_t min_; ///< The minimum integer that will be generated
+};
+
+} // namespace random
+} // namespace util
+} // namespace isc
+
+#endif//__NSAS_RANDOM_NUMBER_GENERATOR_H
diff --git a/src/lib/util/strutil.cc b/src/lib/util/strutil.cc
new file mode 100644
index 0000000..161f9ac
--- /dev/null
+++ b/src/lib/util/strutil.cc
@@ -0,0 +1,137 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <numeric>
+
+#include <string.h>
+#include <util/strutil.h>
+
+using namespace std;
+
+namespace isc {
+namespace util {
+namespace str {
+
+// Normalize slashes
+
+void
+normalizeSlash(std::string& name) {
+ if (!name.empty()) {
+ size_t pos = 0;
+ while ((pos = name.find('\\', pos)) != std::string::npos) {
+ name[pos] = '/';
+ }
+ }
+}
+
+// Trim String
+
+string
+trim(const string& instring) {
+ static const char* blanks = " \t\n";
+
+ string retstring = "";
+ if (!instring.empty()) {
+
+ // Search for first non-blank character in the string
+ size_t first = instring.find_first_not_of(blanks);
+ if (first != string::npos) {
+
+ // String not all blanks, so look for last character
+ size_t last = instring.find_last_not_of(blanks);
+
+ // Extract the trimmed substring
+ retstring = instring.substr(first, (last - first + 1));
+ }
+ }
+
+ return (retstring);
+}
+
+// Tokenise string. As noted in the header, this is locally written to avoid
+// another dependency on a Boost library.
+
+vector<string>
+tokens(const std::string& text, const std::string& delim) {
+ vector<string> result;
+
+ // Search for the first non-delimiter character
+ size_t start = text.find_first_not_of(delim);
+ while (start != string::npos) {
+
+ // Non-delimiter found, look for next delimiter
+ size_t end = text.find_first_of(delim, start);
+ if (end != string::npos) {
+
+ // Delimiter found, so extract string & search for start of next
+ // non-delimiter segment.
+ result.push_back(text.substr(start, (end - start)));
+ start = text.find_first_not_of(delim, end);
+
+ } else {
+
+ // End of string found, extract rest of string and flag to exit
+ result.push_back(text.substr(start));
+ start = string::npos;
+ }
+ }
+
+ return (result);
+}
+
+// Local function to pass to accumulate() for summing up string lengths.
+
+namespace {
+
+size_t
+lengthSum(string::size_type curlen, const string& cur_string) {
+ return (curlen + cur_string.size());
+}
+
+}
+
+// Provide printf-style formatting.
+
+std::string
+format(const std::string& format, const std::vector<std::string>& args) {
+
+ static const string flag = "%s";
+
+ // Initialize return string. To speed things up, we'll reserve an
+ // appropriate amount of space - current string size, plus length of all
+ // the argument strings, less two characters for each argument (the %s in
+ // the format string is being replaced).
+ string result;
+ size_t length = accumulate(args.begin(), args.end(), format.size(),
+ lengthSum) - (args.size() * flag.size());
+ result.reserve(length);
+
+ // Iterate through replacing all tokens
+ result = format;
+ size_t tokenpos = 0; // Position of last token replaced
+ int i = 0; // Index into argument array
+
+ while ((i < args.size()) && (tokenpos != string::npos)) {
+ tokenpos = result.find(flag, tokenpos);
+ if (tokenpos != string::npos) {
+ result.replace(tokenpos, flag.size(), args[i++]);
+ }
+ }
+
+ return (result);
+}
+
+} // namespace str
+} // namespace util
+} // namespace isc
diff --git a/src/lib/util/strutil.h b/src/lib/util/strutil.h
new file mode 100644
index 0000000..e044c15
--- /dev/null
+++ b/src/lib/util/strutil.h
@@ -0,0 +1,147 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __STRUTIL_H
+#define __STRUTIL_H
+
+#include <algorithm>
+#include <cctype>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace util {
+namespace str {
+
+/// \brief A Set of C++ Utilities for Manipulating Strings
+
+/// \brief Normalize Backslash
+///
+/// Only relevant to Windows, this replaces all "\" in a string with "/" and
+/// returns the result. On other systems it is a no-op. Note that Windows does
+/// recognise file names with the "\" replaced by "/" (at least in system calls,
+/// if not the command line).
+///
+/// \param name Name to be substituted
+void normalizeSlash(std::string& name);
+
+
+/// \brief Trim Leading and Trailing Spaces
+///
+/// Returns a copy of the input string but with any leading or trailing spaces
+/// or tabs removed.
+///
+/// \param instring Input string to modify
+///
+/// \return String with leading and trailing spaces removed
+std::string trim(const std::string& instring);
+
+
+/// \brief Split String into Tokens
+///
+/// Splits a string into tokens (the tokens being delimited by one or more of
+/// the delimiter characters) and returns the tokens in a vector array. Note
+/// that adjacent delimiters are considered to be a single delimiter.
+///
+/// Special cases are:
+/// -# The empty string is considered to be zero tokens.
+/// -# A string comprising nothing but delimiters is considered to be zero
+/// tokens.
+///
+/// The reasoning behind this is that the string can be thought of as having
+/// invisible leading and trailing delimiter characters. Therefore both cases
+/// reduce to a set of contiguous delimiters, which are considered a single
+/// delimiter (so getting rid of the string).
+///
+/// We could use Boost for this, but this (simple) function eliminates one
+/// dependency in the code.
+///
+/// \param text String to be split. Passed by value as the internal copy is
+/// altered during the processing.
+/// \param delim Delimiter characters
+///
+/// \return Vector of tokens.
+std::vector<std::string> tokens(const std::string& text,
+ const std::string& delim = std::string(" \t\n"));
+
+
+/// \brief Uppercase Character
+///
+/// Used in uppercase() to pass as an argument to std::transform(). The
+/// function std::toupper() can't be used as it takes an "int" as its argument;
+/// this confuses the template expansion mechanism because dereferencing a
+/// string::iterator returns a char.
+///
+/// \param chr Character to be upper-cased.
+///
+/// \return Uppercase version of the argument
+inline char toUpper(char chr) {
+ return (static_cast<char>(std::toupper(static_cast<int>(chr))));
+}
+
+
+/// \brief Uppercase String
+///
+/// A convenience function to uppercase a string.
+///
+/// \param text String to be upper-cased.
+inline void uppercase(std::string& text) {
+ std::transform(text.begin(), text.end(), text.begin(),
+ isc::util::str::toUpper);
+}
+
+/// \brief Lowercase Character
+///
+/// Used in lowercase() to pass as an argument to std::transform(). The
+/// function std::tolower() can't be used as it takes an "int" as its argument;
+/// this confuses the template expansion mechanism because dereferencing a
+/// string::iterator returns a char.
+///
+/// \param chr Character to be lower-cased.
+///
+/// \return Lowercase version of the argument
+inline char toLower(char chr) {
+ return (static_cast<char>(std::tolower(static_cast<int>(chr))));
+}
+
+/// \brief Lowercase String
+///
+/// A convenience function to lowercase a string
+///
+/// \param text String to be lower-cased.
+inline void lowercase(std::string& text) {
+ std::transform(text.begin(), text.end(), text.begin(),
+ isc::util::str::toLower);
+}
+
+
+/// \brief Apply Formatting
+///
+/// Given a printf-style format string containing only "%s" place holders
+/// (others are ignored) and a vector of strings, this produces a single string
+/// with the placeholders replaced.
+///
+/// \param format Format string
+/// \param args Vector of argument strings
+///
+/// \return Resultant string
+std::string format(const std::string& format,
+ const std::vector<std::string>& args);
+
+
+} // namespace str
+} // namespace util
+} // namespace isc
+
+#endif // __STRUTIL_H
diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am
new file mode 100644
index 0000000..47243f8
--- /dev/null
+++ b/src/lib/util/tests/Makefile.am
@@ -0,0 +1,43 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += base32hex_unittest.cc
+run_unittests_SOURCES += base64_unittest.cc
+run_unittests_SOURCES += buffer_unittest.cc
+run_unittests_SOURCES += fd_share_tests.cc
+run_unittests_SOURCES += fd_tests.cc
+run_unittests_SOURCES += filename_unittest.cc
+run_unittests_SOURCES += hex_unittest.cc
+run_unittests_SOURCES += io_utilities_unittest.cc
+run_unittests_SOURCES += lru_list_unittest.cc
+run_unittests_SOURCES += qid_gen_unittest.cc
+run_unittests_SOURCES += random_number_generator_unittest.cc
+run_unittests_SOURCES += sha1_unittest.cc
+run_unittests_SOURCES += strutil_unittest.cc
+run_unittests_SOURCES += time_utilities_unittest.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
+run_unittests_LDADD += \
+ $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/util/tests/base32hex_unittest.cc b/src/lib/util/tests/base32hex_unittest.cc
new file mode 100644
index 0000000..fa4a290
--- /dev/null
+++ b/src/lib/util/tests/base32hex_unittest.cc
@@ -0,0 +1,164 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+
+#include <cctype>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+
+#include <util/encode/base32hex.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::util::encode;
+
+namespace {
+
+typedef pair<string, string> StringPair;
+
+class Base32HexTest : public ::testing::Test {
+protected:
+ Base32HexTest() : encoding_chars("0123456789ABCDEFGHIJKLMNOPQRSTUV=") {
+ // test vectors from RFC4648
+ test_sequence.push_back(StringPair("", ""));
+ test_sequence.push_back(StringPair("f", "CO======"));
+ test_sequence.push_back(StringPair("fo", "CPNG===="));
+ test_sequence.push_back(StringPair("foo", "CPNMU==="));
+ test_sequence.push_back(StringPair("foob", "CPNMUOG="));
+ test_sequence.push_back(StringPair("fooba", "CPNMUOJ1"));
+ test_sequence.push_back(StringPair("foobar", "CPNMUOJ1E8======"));
+
+ // the same data, encoded using lower case chars (testable only
+ // for the decode side)
+ test_sequence_lower.push_back(StringPair("f", "co======"));
+ test_sequence_lower.push_back(StringPair("fo", "cpng===="));
+ test_sequence_lower.push_back(StringPair("foo", "cpnmu==="));
+ test_sequence_lower.push_back(StringPair("foob", "cpnmuog="));
+ test_sequence_lower.push_back(StringPair("fooba", "cpnmuoj1"));
+ test_sequence_lower.push_back(StringPair("foobar",
+ "cpnmuoj1e8======"));
+ }
+ vector<StringPair> test_sequence;
+ vector<StringPair> test_sequence_lower;
+ vector<uint8_t> decoded_data;
+ const string encoding_chars;
+};
+
+void
+decodeCheck(const string& input_string, vector<uint8_t>& output,
+ const string& expected)
+{
+ decodeBase32Hex(input_string, output);
+ EXPECT_EQ(expected, string(output.begin(), output.end()));
+}
+
+TEST_F(Base32HexTest, decode) {
+ for (vector<StringPair>::const_iterator it = test_sequence.begin();
+ it != test_sequence.end();
+ ++it) {
+ decodeCheck((*it).second, decoded_data, (*it).first);
+ }
+
+ // whitespace should be allowed
+ decodeCheck("CP NM\tUOG=", decoded_data, "foob");
+ decodeCheck("CPNMU===\n", decoded_data, "foo");
+ decodeCheck(" CP NM\tUOG=", decoded_data, "foob");
+ decodeCheck(" ", decoded_data, "");
+
+ // Incomplete input
+ EXPECT_THROW(decodeBase32Hex("CPNMUOJ", decoded_data), BadValue);
+
+ // invalid number of padding characters
+ EXPECT_THROW(decodeBase32Hex("CPNMU0==", decoded_data), BadValue);
+ EXPECT_THROW(decodeBase32Hex("CO0=====", decoded_data), BadValue);
+ EXPECT_THROW(decodeBase32Hex("CO=======", decoded_data), // too many ='s
+ BadValue);
+
+ // intermediate padding isn't allowed
+ EXPECT_THROW(decodeBase32Hex("CPNMUOG=CPNMUOG=", decoded_data), BadValue);
+
+ // Non canonical form isn't allowed.
+ // P => 25(11001), so the padding byte would be 01000000
+ EXPECT_THROW(decodeBase32Hex("0P======", decoded_data), BadValue);
+}
+
+TEST_F(Base32HexTest, decodeLower) {
+ for (vector<StringPair>::const_iterator it = test_sequence_lower.begin();
+ it != test_sequence_lower.end();
+ ++it) {
+ decodeCheck((*it).second, decoded_data, (*it).first);
+ }
+}
+
+TEST_F(Base32HexTest, encode) {
+ for (vector<StringPair>::const_iterator it = test_sequence.begin();
+ it != test_sequence.end();
+ ++it) {
+ decoded_data.assign((*it).first.begin(), (*it).first.end());
+ EXPECT_EQ((*it).second, encodeBase32Hex(decoded_data));
+ }
+}
+
+// For Base32Hex we use handmade mappings, so it's prudent to test the
+// entire mapping table explicitly.
+TEST_F(Base32HexTest, decodeMap) {
+ string input(8, '0'); // input placeholder
+
+ // We're going to populate an input string with only the last character
+ // not equal to the zero character ('0') for each valid base32hex encoding
+ // character. Decoding that input should result in a data stream with
+ // the last byte equal to the numeric value represented by the that
+ // character. For example, we'll generate and confirm the following:
+ // "00000000" => should be 0 (as a 40bit integer)
+ // "00000001" => should be 1 (as a 40bit integer)
+ // ...
+ // "0000000V" => should be 31 (as a 40bit integer)
+ // We also check the use of an invalid character for the last character
+ // surely fails. '=' should be accepted as a valid padding in this
+ // context; space characters shouldn't be allowed in this context.
+
+ for (int i = 0; i < 256; ++i) {
+ input[7] = i;
+
+ const char ch = toupper(i);
+ const size_t pos = encoding_chars.find(ch);
+ if (pos == string::npos) {
+ EXPECT_THROW(decodeBase32Hex(input, decoded_data), BadValue);
+ } else {
+ decodeBase32Hex(input, decoded_data);
+ if (ch == '=') {
+ EXPECT_EQ(4, decoded_data.size());
+ } else {
+ EXPECT_EQ(5, decoded_data.size());
+ EXPECT_EQ(pos, decoded_data[4]);
+ }
+ }
+ }
+}
+
+TEST_F(Base32HexTest, encodeMap) {
+ for (int i = 0; i < 32; ++i) {
+ decoded_data.assign(4, 0);
+ decoded_data.push_back(i);
+ EXPECT_EQ(encoding_chars[i], encodeBase32Hex(decoded_data)[7]);
+ }
+}
+
+}
diff --git a/src/lib/util/tests/base64_unittest.cc b/src/lib/util/tests/base64_unittest.cc
new file mode 100644
index 0000000..b0c926d
--- /dev/null
+++ b/src/lib/util/tests/base64_unittest.cc
@@ -0,0 +1,99 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+
+#include <util/encode/base64.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::util::encode;
+
+namespace {
+
+typedef pair<string, string> StringPair;
+
+class Base64Test : public ::testing::Test {
+protected:
+ Base64Test()
+ {
+ // test vectors from RFC4648
+ test_sequence.push_back(StringPair("", ""));
+ test_sequence.push_back(StringPair("f", "Zg=="));
+ test_sequence.push_back(StringPair("fo", "Zm8="));
+ test_sequence.push_back(StringPair("foo", "Zm9v"));
+ test_sequence.push_back(StringPair("foob", "Zm9vYg=="));
+ test_sequence.push_back(StringPair("fooba", "Zm9vYmE="));
+ test_sequence.push_back(StringPair("foobar", "Zm9vYmFy"));
+ }
+ vector<StringPair> test_sequence;
+ vector<uint8_t> decoded_data;
+};
+
+void
+decodeCheck(const string& input_string, vector<uint8_t>& output,
+ const string& expected)
+{
+ decodeBase64(input_string, output);
+ EXPECT_EQ(expected, string(output.begin(), output.end()));
+}
+
+TEST_F(Base64Test, decode) {
+ for (vector<StringPair>::const_iterator it = test_sequence.begin();
+ it != test_sequence.end();
+ ++it) {
+ decodeCheck((*it).second, decoded_data, (*it).first);
+ }
+
+ // whitespace should be allowed
+ decodeCheck("Zm 9v\tYmF\ny", decoded_data, "foobar");
+ decodeCheck("Zm9vYg==", decoded_data, "foob");
+ decodeCheck("Zm9vYmE=\n", decoded_data, "fooba");
+ decodeCheck(" Zm9vYmE=\n", decoded_data, "fooba");
+ decodeCheck(" ", decoded_data, "");
+ decodeCheck("\n\t", decoded_data, "");
+
+ // incomplete input
+ EXPECT_THROW(decodeBase64("Zm9vYmF", decoded_data), BadValue);
+
+ // only up to 2 padding characters are allowed
+ EXPECT_THROW(decodeBase64("A===", decoded_data), BadValue);
+ EXPECT_THROW(decodeBase64("A= ==", decoded_data), BadValue);
+
+ // intermediate padding isn't allowed
+ EXPECT_THROW(decodeBase64("YmE=YmE=", decoded_data), BadValue);
+
+ // Non canonical form isn't allowed.
+ // Z => 25(011001), m => 38(100110), 9 => 60(111101), so the padding
+ // byte would be 0100 0000.
+ EXPECT_THROW(decodeBase64("Zm9=", decoded_data), BadValue);
+ // Same for the 1st padding byte. This would make it 01100000.
+ EXPECT_THROW(decodeBase64("Zm==", decoded_data), BadValue);
+}
+
+TEST_F(Base64Test, encode) {
+ for (vector<StringPair>::const_iterator it = test_sequence.begin();
+ it != test_sequence.end();
+ ++it) {
+ decoded_data.assign((*it).first.begin(), (*it).first.end());
+ EXPECT_EQ((*it).second, encodeBase64(decoded_data));
+ }
+}
+}
diff --git a/src/lib/util/tests/buffer_unittest.cc b/src/lib/util/tests/buffer_unittest.cc
new file mode 100644
index 0000000..0cd1823
--- /dev/null
+++ b/src/lib/util/tests/buffer_unittest.cc
@@ -0,0 +1,242 @@
+// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc;
+
+namespace {
+
+using isc::util::InputBuffer;
+using isc::util::OutputBuffer;
+
+class BufferTest : public ::testing::Test {
+protected:
+ BufferTest() : ibuffer(testdata, sizeof(testdata)), obuffer(0),
+ expected_size(0)
+ {
+ data16 = (2 << 8) | 3;
+ data32 = (4 << 24) | (5 << 16) | (6 << 8) | 7;
+ memset(vdata, 0, sizeof(testdata));
+ }
+
+ InputBuffer ibuffer;
+ OutputBuffer obuffer;
+ static const uint8_t testdata[5];
+ uint8_t vdata[sizeof(testdata)];
+ size_t expected_size;
+ uint16_t data16;
+ uint32_t data32;
+};
+
+const uint8_t BufferTest::testdata[5] = {1, 2, 3, 4, 5};
+
+TEST_F(BufferTest, inputBufferRead) {
+ EXPECT_EQ(5, ibuffer.getLength());
+ EXPECT_EQ(1, ibuffer.readUint8());
+ EXPECT_EQ(1, ibuffer.getPosition());
+ data16 = ibuffer.readUint16();
+ EXPECT_EQ((2 << 8) | 3, data16);
+ EXPECT_EQ(3, ibuffer.getPosition());
+ ibuffer.setPosition(1);
+ EXPECT_EQ(1, ibuffer.getPosition());
+ data32 = ibuffer.readUint32();
+ EXPECT_EQ((2 << 24) | (3 << 16) | (4 << 8) | 5, data32);
+ ibuffer.setPosition(0);
+ memset(vdata, 0, sizeof(vdata));
+ ibuffer.readData(vdata, sizeof(vdata));
+ EXPECT_EQ(0, memcmp(vdata, testdata, sizeof(testdata)));
+}
+
+TEST_F(BufferTest, inputBufferException) {
+ EXPECT_THROW(ibuffer.setPosition(6), isc::util::InvalidBufferPosition);
+
+ ibuffer.setPosition(sizeof(testdata));
+ EXPECT_THROW(ibuffer.readUint8(), isc::util::InvalidBufferPosition);
+
+ ibuffer.setPosition(sizeof(testdata) - 1);
+ EXPECT_THROW(ibuffer.readUint16(), isc::util::InvalidBufferPosition);
+
+ ibuffer.setPosition(sizeof(testdata) - 3);
+ EXPECT_THROW(ibuffer.readUint32(), isc::util::InvalidBufferPosition);
+
+ ibuffer.setPosition(sizeof(testdata) - 4);
+ EXPECT_THROW(ibuffer.readData(vdata, sizeof(vdata)),
+ isc::util::InvalidBufferPosition);
+}
+
+TEST_F(BufferTest, outputBufferExtend) {
+ EXPECT_EQ(0, obuffer.getCapacity());
+ EXPECT_EQ(0, obuffer.getLength());
+ obuffer.writeUint8(10);
+ EXPECT_LT(0, obuffer.getCapacity());
+ EXPECT_EQ(1, obuffer.getLength());
+}
+
+TEST_F(BufferTest, outputBufferWrite) {
+ const uint8_t* cp;
+
+ obuffer.writeUint8(1);
+ expected_size += sizeof(uint8_t);
+ EXPECT_EQ(expected_size, obuffer.getLength());
+ cp = static_cast<const uint8_t*>(obuffer.getData());
+ EXPECT_EQ(1, *cp);
+
+ obuffer.writeUint16(data16);
+ expected_size += sizeof(data16);
+ cp = static_cast<const uint8_t*>(obuffer.getData());
+ EXPECT_EQ(expected_size, obuffer.getLength());
+ EXPECT_EQ(2, *(cp + 1));
+ EXPECT_EQ(3, *(cp + 2));
+
+ obuffer.writeUint32(data32);
+ expected_size += sizeof(data32);
+ cp = static_cast<const uint8_t*>(obuffer.getData());
+ EXPECT_EQ(expected_size, obuffer.getLength());
+ EXPECT_EQ(4, *(cp + 3));
+ EXPECT_EQ(5, *(cp + 4));
+ EXPECT_EQ(6, *(cp + 5));
+ EXPECT_EQ(7, *(cp + 6));
+
+ obuffer.writeData(testdata, sizeof(testdata));
+ expected_size += sizeof(testdata);
+ EXPECT_EQ(expected_size, obuffer.getLength());
+ cp = static_cast<const uint8_t*>(obuffer.getData());
+ EXPECT_EQ(0, memcmp(cp + 7, testdata, sizeof(testdata)));
+}
+
+TEST_F(BufferTest, outputBufferWriteat) {
+ obuffer.writeUint32(data32);
+ expected_size += sizeof(data32);
+
+ // overwrite 2nd byte
+ obuffer.writeUint8At(4, 1);
+ EXPECT_EQ(expected_size, obuffer.getLength()); // length shouldn't change
+ const uint8_t* cp = static_cast<const uint8_t*>(obuffer.getData());
+ EXPECT_EQ(4, *(cp + 1));
+
+ // overwrite 2nd and 3rd bytes
+ obuffer.writeUint16At(data16, 1);
+ EXPECT_EQ(expected_size, obuffer.getLength()); // length shouldn't change
+ cp = static_cast<const uint8_t*>(obuffer.getData());
+ EXPECT_EQ(2, *(cp + 1));
+ EXPECT_EQ(3, *(cp + 2));
+
+ // overwrite 3rd and 4th bytes
+ obuffer.writeUint16At(data16, 2);
+ EXPECT_EQ(expected_size, obuffer.getLength());
+ cp = static_cast<const uint8_t*>(obuffer.getData());
+ EXPECT_EQ(2, *(cp + 2));
+ EXPECT_EQ(3, *(cp + 3));
+
+ EXPECT_THROW(obuffer.writeUint8At(data16, 5),
+ isc::util::InvalidBufferPosition);
+ EXPECT_THROW(obuffer.writeUint8At(data16, 4),
+ isc::util::InvalidBufferPosition);
+ EXPECT_THROW(obuffer.writeUint16At(data16, 3),
+ isc::util::InvalidBufferPosition);
+ EXPECT_THROW(obuffer.writeUint16At(data16, 4),
+ isc::util::InvalidBufferPosition);
+ EXPECT_THROW(obuffer.writeUint16At(data16, 5),
+ isc::util::InvalidBufferPosition);
+}
+
+TEST_F(BufferTest, outputBufferSkip) {
+ obuffer.skip(4);
+ EXPECT_EQ(4, obuffer.getLength());
+
+ obuffer.skip(2);
+ EXPECT_EQ(6, obuffer.getLength());
+}
+
+TEST_F(BufferTest, outputBufferTrim) {
+ obuffer.writeData(testdata, sizeof(testdata));
+ EXPECT_EQ(5, obuffer.getLength());
+
+ obuffer.trim(1);
+ EXPECT_EQ(4, obuffer.getLength());
+
+ obuffer.trim(2);
+ EXPECT_EQ(2, obuffer.getLength());
+
+ EXPECT_THROW(obuffer.trim(3), OutOfRange);
+}
+
+TEST_F(BufferTest, outputBufferReadat) {
+ obuffer.writeData(testdata, sizeof(testdata));
+ for (int i = 0; i < sizeof(testdata); i ++) {
+ EXPECT_EQ(testdata[i], obuffer[i]);
+ }
+ EXPECT_THROW(obuffer[sizeof(testdata)], isc::util::InvalidBufferPosition);
+}
+
+TEST_F(BufferTest, outputBufferClear) {
+ obuffer.writeData(testdata, sizeof(testdata));
+ obuffer.clear();
+ EXPECT_EQ(0, obuffer.getLength());
+}
+
+TEST_F(BufferTest, outputBufferCopy) {
+ obuffer.writeData(testdata, sizeof(testdata));
+
+ EXPECT_NO_THROW({
+ OutputBuffer copy(obuffer);
+ ASSERT_EQ(sizeof(testdata), copy.getLength());
+ for (int i = 0; i < sizeof(testdata); i ++) {
+ EXPECT_EQ(testdata[i], copy[i]);
+ if (i + 1 < sizeof(testdata)) {
+ obuffer.writeUint16At(0, i);
+ }
+ EXPECT_EQ(testdata[i], copy[i]);
+ }
+ obuffer.clear();
+ ASSERT_EQ(sizeof(testdata), copy.getLength());
+ });
+}
+
+TEST_F(BufferTest, outputBufferAssign) {
+ OutputBuffer another(0);
+ another.clear();
+ obuffer.writeData(testdata, sizeof(testdata));
+
+ EXPECT_NO_THROW({
+ another = obuffer;
+ ASSERT_EQ(sizeof(testdata), another.getLength());
+ for (int i = 0; i < sizeof(testdata); i ++) {
+ EXPECT_EQ(testdata[i], another[i]);
+ if (i + 1 < sizeof(testdata)) {
+ obuffer.writeUint16At(0, i);
+ }
+ EXPECT_EQ(testdata[i], another[i]);
+ }
+ obuffer.clear();
+ ASSERT_EQ(sizeof(testdata), another.getLength());
+ });
+}
+
+TEST_F(BufferTest, outputBufferZeroSize) {
+ // Some OSes might return NULL on malloc for 0 size, so check it works
+ EXPECT_NO_THROW({
+ OutputBuffer first(0);
+ OutputBuffer copy(first);
+ OutputBuffer second(0);
+ second = first;
+ });
+}
+
+}
diff --git a/src/lib/util/tests/fd_share_tests.cc b/src/lib/util/tests/fd_share_tests.cc
new file mode 100644
index 0000000..cc92e47
--- /dev/null
+++ b/src/lib/util/tests/fd_share_tests.cc
@@ -0,0 +1,74 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/io/fd.h>
+#include <util/io/fd_share.h>
+
+#include <util/unittests/fork.h>
+
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <cstdio>
+
+using namespace isc::util::io;
+using namespace isc::util::unittests;
+
+namespace {
+
+// We test that we can transfer a pipe over other pipe
+TEST(FDShare, transfer) {
+ // Get a pipe and fork
+ int pipes[2];
+ ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, pipes));
+ pid_t sender(fork());
+ ASSERT_NE(-1, sender);
+ if(sender) { // We are in parent
+ // Close the other side of pipe, we want only writible one
+ EXPECT_NE(-1, close(pipes[0]));
+ // Get a process to check data
+ int fd(0);
+ pid_t checker(check_output(&fd, "data", 4));
+ ASSERT_NE(-1, checker);
+ // Now, send the file descriptor, close it and close the pipe
+ EXPECT_NE(-1, send_fd(pipes[1], fd));
+ EXPECT_NE(-1, close(pipes[1]));
+ EXPECT_NE(-1, close(fd));
+ // Check both subprocesses ended well
+ EXPECT_TRUE(process_ok(sender));
+ EXPECT_TRUE(process_ok(checker));
+ } else { // We are in child. We do not use ASSERT here
+ // Close the write end, we only read
+ if(close(pipes[1])) {
+ exit(1);
+ }
+ // Get the file descriptor
+ int fd(recv_fd(pipes[0]));
+ if(fd == -1) {
+ exit(1);
+ }
+ // This pipe is not needed
+ if(close(pipes[0])) {
+ exit(1);
+ }
+ // Send "data" trough the received fd, close it and be done
+ if(!write_data(fd, "data", 4) || close(fd) == -1) {
+ exit(1);
+ }
+ exit(0);
+ }
+}
+
+}
diff --git a/src/lib/util/tests/fd_tests.cc b/src/lib/util/tests/fd_tests.cc
new file mode 100644
index 0000000..6ba2766
--- /dev/null
+++ b/src/lib/util/tests/fd_tests.cc
@@ -0,0 +1,66 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/io/fd.h>
+
+#include <util/unittests/fork.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::util::io;
+using namespace isc::util::unittests;
+
+namespace {
+
+// Make sure the test is large enough and does not fit into one
+// read or write request
+const size_t TEST_DATA_SIZE = 8 * 1024 * 1024;
+
+class FDTest : public ::testing::Test {
+ public:
+ unsigned char *data, *buffer;
+ FDTest() :
+ // We do not care what is inside, we just need it to be the same
+ data(new unsigned char[TEST_DATA_SIZE]),
+ buffer(NULL)
+ { }
+ ~ FDTest() {
+ delete[] data;
+ delete[] buffer;
+ }
+};
+
+// Test we read what was sent
+TEST_F(FDTest, read) {
+ int read_pipe(0);
+ buffer = new unsigned char[TEST_DATA_SIZE];
+ pid_t feeder(provide_input(&read_pipe, data, TEST_DATA_SIZE));
+ ASSERT_GE(feeder, 0);
+ ssize_t received(read_data(read_pipe, buffer, TEST_DATA_SIZE));
+ EXPECT_TRUE(process_ok(feeder));
+ EXPECT_EQ(TEST_DATA_SIZE, received);
+ EXPECT_EQ(0, memcmp(data, buffer, received));
+}
+
+// Test we write the correct thing
+TEST_F(FDTest, write) {
+ int write_pipe(0);
+ pid_t checker(check_output(&write_pipe, data, TEST_DATA_SIZE));
+ ASSERT_GE(checker, 0);
+ EXPECT_TRUE(write_data(write_pipe, data, TEST_DATA_SIZE));
+ EXPECT_EQ(0, close(write_pipe));
+ EXPECT_TRUE(process_ok(checker));
+}
+
+}
diff --git a/src/lib/util/tests/filename_unittest.cc b/src/lib/util/tests/filename_unittest.cc
new file mode 100644
index 0000000..33e6456
--- /dev/null
+++ b/src/lib/util/tests/filename_unittest.cc
@@ -0,0 +1,179 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <util/filename.h>
+
+using namespace isc;
+using namespace isc::util;
+using namespace std;
+
+class FilenameTest : public ::testing::Test {
+protected:
+ FilenameTest()
+ {
+ }
+};
+
+
+// Check that the name can be changed
+
+TEST_F(FilenameTest, SetName) {
+ Filename fname("/a/b/c.d");
+ EXPECT_EQ("/a/b/c.d", fname.fullName());
+
+ fname.setName("test.txt");
+ EXPECT_EQ("test.txt", fname.fullName());
+}
+
+
+// Check that the components are split correctly. This is a check of the
+// private member split() method.
+
+TEST_F(FilenameTest, Components) {
+
+ // Complete name
+ Filename fname("/alpha/beta/gamma.delta");
+ EXPECT_EQ("/alpha/beta/", fname.directory());
+ EXPECT_EQ("gamma", fname.name());
+ EXPECT_EQ(".delta", fname.extension());
+
+ // Directory only
+ fname.setName("/gamma/delta/");
+ EXPECT_EQ("/gamma/delta/", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // Filename only
+ fname.setName("epsilon");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("epsilon", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // Extension only
+ fname.setName(".zeta");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ(".zeta", fname.extension());
+
+ // Missing directory
+ fname.setName("eta.theta");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("eta", fname.name());
+ EXPECT_EQ(".theta", fname.extension());
+
+ // Missing filename
+ fname.setName("/iota/.kappa");
+ EXPECT_EQ("/iota/", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ(".kappa", fname.extension());
+
+ // Missing extension
+ fname.setName("lambda/mu/nu");
+ EXPECT_EQ("lambda/mu/", fname.directory());
+ EXPECT_EQ("nu", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // Check that the decomposition can occur in the presence of leading and
+ // trailing spaces
+ fname.setName(" lambda/mu/nu\t ");
+ EXPECT_EQ("lambda/mu/", fname.directory());
+ EXPECT_EQ("nu", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // Empty string
+ fname.setName("");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // ... and just spaces
+ fname.setName(" ");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // Check corner cases - where separators are present, but strings are
+ // absent.
+ fname.setName("/");
+ EXPECT_EQ("/", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ fname.setName(".");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ(".", fname.extension());
+
+ fname.setName("/.");
+ EXPECT_EQ("/", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ(".", fname.extension());
+
+ // Note that the space is a valid filename here; only leading and trailing
+ // spaces should be trimmed.
+ fname.setName("/ .");
+ EXPECT_EQ("/", fname.directory());
+ EXPECT_EQ(" ", fname.name());
+ EXPECT_EQ(".", fname.extension());
+
+ fname.setName(" / . ");
+ EXPECT_EQ("/", fname.directory());
+ EXPECT_EQ(" ", fname.name());
+ EXPECT_EQ(".", fname.extension());
+}
+
+// Check that the expansion with a default works.
+
+TEST_F(FilenameTest, ExpandWithDefault) {
+ Filename fname("a.b");
+
+ // These tests also check that the trimming of the default component is
+ // done properly.
+ EXPECT_EQ("/c/d/a.b", fname.expandWithDefault(" /c/d/ "));
+ EXPECT_EQ("/c/d/a.b", fname.expandWithDefault("/c/d/e.f"));
+ EXPECT_EQ("a.b", fname.expandWithDefault("e.f"));
+
+ fname.setName("/a/b/c");
+ EXPECT_EQ("/a/b/c.d", fname.expandWithDefault(".d"));
+ EXPECT_EQ("/a/b/c.d", fname.expandWithDefault("x.d"));
+ EXPECT_EQ("/a/b/c.d", fname.expandWithDefault("/s/t/u.d"));
+ EXPECT_EQ("/a/b/c", fname.expandWithDefault("/s/t/u"));
+
+ fname.setName(".h");
+ EXPECT_EQ("/a/b/c.h", fname.expandWithDefault("/a/b/c.msg"));
+}
+
+// Check that we can use this as a default in expanding a filename
+
+TEST_F(FilenameTest, UseAsDefault) {
+
+ Filename fname("a.b");
+
+ // These tests also check that the trimming of the default component is
+ // done properly.
+ EXPECT_EQ("/c/d/a.b", fname.useAsDefault(" /c/d/ "));
+ EXPECT_EQ("/c/d/e.f", fname.useAsDefault("/c/d/e.f"));
+ EXPECT_EQ("e.f", fname.useAsDefault("e.f"));
+
+ fname.setName("/a/b/c");
+ EXPECT_EQ("/a/b/c.d", fname.useAsDefault(".d"));
+ EXPECT_EQ("/a/b/x.d", fname.useAsDefault("x.d"));
+ EXPECT_EQ("/s/t/u.d", fname.useAsDefault("/s/t/u.d"));
+ EXPECT_EQ("/s/t/u", fname.useAsDefault("/s/t/u"));
+ EXPECT_EQ("/a/b/c", fname.useAsDefault(""));
+}
diff --git a/src/lib/util/tests/hex_unittest.cc b/src/lib/util/tests/hex_unittest.cc
new file mode 100644
index 0000000..52bccea
--- /dev/null
+++ b/src/lib/util/tests/hex_unittest.cc
@@ -0,0 +1,120 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+
+#include <vector>
+#include <string>
+
+#include <exceptions/exceptions.h>
+
+#include <util/encode/hex.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::util::encode;
+
+namespace {
+const string hex_txt("DEADBEEFDECADE");
+const string hex_txt_space("DEAD BEEF DECADE");
+const string hex_txt_lower("deadbeefdecade");
+
+class HexTest : public ::testing::Test {
+protected:
+ HexTest() : encoding_chars("0123456789ABCDEF") {}
+ vector<uint8_t> decoded_data;
+ const string encoding_chars;
+};
+
+TEST_F(HexTest, encodeHex) {
+ std::vector<uint8_t> data;
+
+ data.push_back(0xde);
+ data.push_back(0xad);
+ data.push_back(0xbe);
+ data.push_back(0xef);
+ data.push_back(0xde);
+ data.push_back(0xca);
+ data.push_back(0xde);
+ EXPECT_EQ(hex_txt, encodeHex(data));
+}
+
+void
+compareData(const std::vector<uint8_t>& data) {
+ EXPECT_EQ(0xde, data[0]);
+ EXPECT_EQ(0xad, data[1]);
+ EXPECT_EQ(0xbe, data[2]);
+ EXPECT_EQ(0xef, data[3]);
+ EXPECT_EQ(0xde, data[4]);
+ EXPECT_EQ(0xca, data[5]);
+ EXPECT_EQ(0xde, data[6]);
+}
+
+TEST_F(HexTest, decodeHex) {
+ std::vector<uint8_t> result;
+
+ decodeHex(hex_txt, result);
+ compareData(result);
+
+ // lower case hex digits should be accepted
+ result.clear();
+ decodeHex(hex_txt_lower, result);
+ compareData(result);
+
+ // white space should be ignored
+ result.clear();
+ decodeHex(hex_txt_space, result);
+ compareData(result);
+
+ // Bogus input: should fail
+ result.clear();
+ EXPECT_THROW(decodeHex("1x", result), BadValue);
+
+ // Bogus input: encoded string must have an even number of characters.
+ result.clear();
+ EXPECT_THROW(decodeHex("dea", result), BadValue);
+}
+
+// For Hex encode/decode we use handmade mappings, so it's prudent to test the
+// entire mapping table explicitly.
+TEST_F(HexTest, decodeMap) {
+ string input("00"); // input placeholder
+
+ // See Base32HexTest.decodeMap for details of the following tests.
+ for (int i = 0; i < 256; ++i) {
+ input[1] = i;
+
+ const char ch = toupper(i);
+ const size_t pos = encoding_chars.find(ch);
+ if (pos == string::npos) {
+ EXPECT_THROW(decodeHex(input, decoded_data), BadValue);
+ } else {
+ decodeHex(input, decoded_data);
+ EXPECT_EQ(1, decoded_data.size());
+ EXPECT_EQ(pos, decoded_data[0]);
+ }
+ }
+}
+
+TEST_F(HexTest, encodeMap) {
+ for (int i = 0; i < 16; ++i) {
+ decoded_data.clear();
+ decoded_data.push_back(i);
+ EXPECT_EQ(encoding_chars[i], encodeHex(decoded_data)[1]);
+ }
+}
+
+}
diff --git a/src/lib/util/tests/io_utilities_unittest.cc b/src/lib/util/tests/io_utilities_unittest.cc
new file mode 100644
index 0000000..4aad560
--- /dev/null
+++ b/src/lib/util/tests/io_utilities_unittest.cc
@@ -0,0 +1,73 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// \brief Test of asiolink utilties
+///
+/// Tests the fuctionality of the asiolink utilities code by comparing them
+/// with the equivalent methods in isc::dns::[Input/Output]Buffer.
+
+#include <cstddef>
+
+#include <gtest/gtest.h>
+
+#include <util/buffer.h>
+#include <util/io_utilities.h>
+
+using namespace isc::util;
+
+TEST(asioutil, readUint16) {
+
+ // Reference buffer
+ uint8_t data[2];
+ InputBuffer buffer(data, sizeof(data));
+
+ // Avoid possible compiler warnings by only setting uint8_t variables to
+ // uint8_t values.
+ uint8_t i8 = 0;
+ uint8_t j8 = 0;
+ for (int i = 0; i < (2 << 8); ++i, ++i8) {
+ for (int j = 0; j < (2 << 8); ++j, ++j8) {
+ data[0] = i8;
+ data[1] = j8;
+ buffer.setPosition(0);
+ EXPECT_EQ(buffer.readUint16(), readUint16(data));
+ }
+ }
+}
+
+
+TEST(asioutil, writeUint16) {
+
+ // Reference buffer
+ OutputBuffer buffer(2);
+ uint8_t test[2];
+
+ // Avoid possible compiler warnings by only setting uint16_t variables to
+ // uint16_t values.
+ uint16_t i16 = 0;
+ for (uint32_t i = 0; i < (2 << 16); ++i, ++i16) {
+
+ // Write the reference data
+ buffer.clear();
+ buffer.writeUint16(i16);
+
+ // ... and the test data
+ writeUint16(i16, test);
+
+ // ... and compare
+ const uint8_t* ref = static_cast<const uint8_t*>(buffer.getData());
+ EXPECT_EQ(ref[0], test[0]);
+ EXPECT_EQ(ref[1], test[1]);
+ }
+}
diff --git a/src/lib/util/tests/lru_list_unittest.cc b/src/lib/util/tests/lru_list_unittest.cc
new file mode 100644
index 0000000..bfb3b4d
--- /dev/null
+++ b/src/lib/util/tests/lru_list_unittest.cc
@@ -0,0 +1,428 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <stdint.h>
+#include <iostream>
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+#include <gtest/gtest.h>
+#include <boost/lexical_cast.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include <util/lru_list.h>
+
+using namespace std;
+
+namespace isc {
+namespace util {
+
+/// \brief Invalid Iterator
+///
+/// Thrown if an attempt was made to access the iterator - the pointer into
+/// the LRU list where this element is located - when it is marked as invalid.
+class InvalidLruIterator : public isc::Exception {
+public:
+ InvalidLruIterator(const char* file, size_t line, const char* what) :
+ Exception(file, line, what)
+ {}
+};
+
+template <typename T>
+class TestEntryT : public boost::enable_shared_from_this <T> {
+public:
+
+ /// \brief Constructor
+ ///
+ /// \param name Name that will be used for the object. This will form
+ /// part of the key.
+ /// \param class_code Class associated with the object.
+ TestEntryT() : valid_(false)
+ {}
+
+ /// \brief Virtual Destructor
+ virtual ~TestEntryT()
+ {}
+
+ /// \brief Sets the iterator of the object
+ ///
+ /// Sets the iterator of an object and, as a side effect, marks it as valid.
+ ///
+ /// \param iterator Iterator of this element in the list
+ virtual void setLruIterator(typename LruList<T>::iterator iterator) {
+ iterator_ = iterator;
+ valid_ = true;
+ }
+
+ /// \brief Return Iterator
+ ///
+ /// \return iterator Iterator of this element in the list.
+ ///
+ /// \exception InvalidLruIterator Thrown if the iterator is not valid.
+ virtual typename LruList<T>::iterator getLruIterator() const {
+ /*if (! valid_) {
+ isc_throw(InvalidLruIterator,
+ "pointer of element into LRU list was not valid");
+ }*/
+ return iterator_;
+ }
+
+ /// \brief Iterator Valid
+ ///
+ /// \return true if the stored iterator is valid.
+ virtual bool iteratorValid() const {
+ return valid_;
+ }
+
+ /// \brief Invalidate Iterator
+ ///
+ /// Marks the iterator as invalid; it can oly be set valid again by a call
+ /// to setLruIterator.
+ virtual void invalidateIterator() {
+ valid_ = false;
+ }
+
+private:
+ typename LruList<T>::iterator iterator_; ///< Handle into the LRU List
+ bool valid_; ///< true if handle is valid
+};
+
+class TestEntry : public TestEntryT<TestEntry> {
+public:
+ TestEntry(std::string name, const int & code) :
+ name_(name), code_(code)
+ {}
+
+ /// \brief Get the Name
+ ///
+ /// \return Name given to this object
+ virtual std::string getName() const {
+ return name_;
+ }
+
+ /// \brief Set the Name
+ ///
+ /// \param name New name of the object
+ virtual void setName(const std::string& name) {
+ name_ = name;
+ }
+
+ /// \brief Get the Class
+ ///
+ /// \return Class code assigned to this object
+ virtual const int& getCode() const {
+ return code_;
+ }
+
+ /// \brief Set the Class
+ ///
+ /// \param code New code of the object
+ virtual void setCode(const int& code) {
+ code_ = code;
+ }
+
+private:
+ std::string name_; ///< Name of the object
+ int code_; ///< Class of the object
+
+};
+
+/// \brief Dropped Functor Class
+///
+/// Functor object is called when an object is dropped from the LRU list.
+/// To prove that it has run, this function does nothing more than set the
+/// MS bit on the 16-bit code value.
+class Dropped : public LruList<TestEntry>::Dropped {
+public:
+ virtual void operator()(TestEntry* entry) const {
+ entry->setCode(entry->getCode() | 0x8000);
+ }
+};
+
+
+/// \brief Text Fixture Class
+class LruListTest : public ::testing::Test {
+protected:
+ LruListTest() :
+ entry1_(new TestEntry("alpha", 1)),
+ entry2_(new TestEntry("beta", 3)),
+ entry3_(new TestEntry("gamma", 4)),
+ entry4_(new TestEntry("delta", 1)),
+ entry5_(new TestEntry("epsilon", 4)),
+ entry6_(new TestEntry("zeta", 3)),
+ entry7_(new TestEntry("eta", 1))
+ {}
+
+ virtual ~LruListTest()
+ {}
+
+ boost::shared_ptr<TestEntry> entry1_;
+ boost::shared_ptr<TestEntry> entry2_;
+ boost::shared_ptr<TestEntry> entry3_;
+ boost::shared_ptr<TestEntry> entry4_;
+ boost::shared_ptr<TestEntry> entry5_;
+ boost::shared_ptr<TestEntry> entry6_;
+ boost::shared_ptr<TestEntry> entry7_;
+};
+
+
+// Test of the constructor
+TEST_F(LruListTest, Constructor) {
+ LruList<TestEntry> lru(100);
+ EXPECT_EQ(100, lru.getMaxSize());
+ EXPECT_EQ(0, lru.size());
+}
+
+// Test of Get/Set the maximum number of entrys
+TEST_F(LruListTest, GetSet) {
+ LruList<TestEntry> lru(100);
+ EXPECT_EQ(100, lru.getMaxSize());
+ lru.setMaxSize(42);
+ EXPECT_EQ(42, lru.getMaxSize());
+}
+
+// Test that adding an entry really does add an entry
+TEST_F(LruListTest, Add) {
+ LruList<TestEntry> lru(100);
+ EXPECT_EQ(0, lru.size());
+
+ lru.add(entry1_);
+ EXPECT_EQ(1, lru.size());
+
+ lru.add(entry2_);
+ EXPECT_EQ(2, lru.size());
+}
+
+// Test that removing an entry really does remove it.
+TEST_F(LruListTest, Remove) {
+ LruList<TestEntry> lru(100);
+ EXPECT_EQ(0, lru.size());
+
+ EXPECT_FALSE(entry1_->iteratorValid());
+ lru.add(entry1_);
+ EXPECT_TRUE(entry1_->iteratorValid());
+ EXPECT_EQ(1, lru.size());
+
+ EXPECT_FALSE(entry2_->iteratorValid());
+ lru.add(entry2_);
+ EXPECT_TRUE(entry2_->iteratorValid());
+ EXPECT_EQ(2, lru.size());
+
+ lru.remove(entry1_);
+ EXPECT_FALSE(entry1_->iteratorValid());
+ EXPECT_EQ(1, lru.size());
+}
+
+// Check that adding a new entry to a limited size list does delete the
+// oldest entry from the list.
+TEST_F(LruListTest, SizeLimit) {
+ LruList<TestEntry> lru(3);
+ EXPECT_EQ(0, lru.size());
+
+ // Add first entry and check that the shared pointer's reference count
+ // has increased. There will be two references: one from the "entry1_"
+ // member in the test fixture class, and one from the list.
+ EXPECT_EQ(1, entry1_.use_count());
+ lru.add(entry1_);
+ EXPECT_EQ(2, entry1_.use_count());
+ EXPECT_EQ(1, lru.size());
+
+ // Same for entry 2.
+ EXPECT_EQ(1, entry2_.use_count());
+ lru.add(entry2_);
+ EXPECT_EQ(2, entry2_.use_count());
+ EXPECT_EQ(2, lru.size());
+
+ // Same for entry 3.
+ EXPECT_EQ(1, entry3_.use_count());
+ lru.add(entry3_);
+ EXPECT_EQ(2, entry3_.use_count());
+ EXPECT_EQ(3, lru.size());
+
+ // Adding entry 4 should remove entry 1 from the list. This will
+ // delete the list's shared pointer to the entry and will therefore
+ // drop the reference count back to one (from the "entry1_" member in
+ // the text fixture class).
+ EXPECT_EQ(2, entry1_.use_count());
+ EXPECT_EQ(1, entry4_.use_count());
+ lru.add(entry4_);
+ EXPECT_EQ(1, entry1_.use_count());
+ EXPECT_EQ(2, entry4_.use_count());
+ EXPECT_EQ(3, lru.size());
+
+ // Adding entry 5 should remove entry 2 from the list.
+ EXPECT_EQ(2, entry2_.use_count());
+ EXPECT_EQ(1, entry5_.use_count());
+ lru.add(entry5_);
+ EXPECT_EQ(1, entry2_.use_count());
+ EXPECT_EQ(2, entry5_.use_count());
+ EXPECT_EQ(3, lru.size());
+}
+
+// Check that "touching" an entry adds it to the back of the list.
+TEST_F(LruListTest, Touch) {
+
+ // Create the list
+ LruList<TestEntry> lru(3);
+ EXPECT_EQ(0, lru.size());
+ lru.add(entry1_);
+ lru.add(entry2_);
+ lru.add(entry3_);
+
+ // Check the reference counts of the entrys and the list size
+ EXPECT_EQ(2, entry1_.use_count());
+ EXPECT_EQ(2, entry2_.use_count());
+ EXPECT_EQ(2, entry3_.use_count());
+ EXPECT_EQ(1, entry4_.use_count());
+ EXPECT_EQ(1, entry5_.use_count());
+ EXPECT_EQ(1, entry6_.use_count());
+ EXPECT_EQ(1, entry7_.use_count());
+ EXPECT_EQ(3, lru.size());
+
+ // "Touch" the first entry
+ lru.touch(entry1_);
+
+ // Adding two more entries should not remove the touched entry.
+ lru.add(entry4_);
+ lru.add(entry5_);
+
+ // Check the status of the entrys and the list.
+ EXPECT_EQ(2, entry1_.use_count());
+ EXPECT_EQ(1, entry2_.use_count());
+ EXPECT_EQ(1, entry3_.use_count());
+ EXPECT_EQ(2, entry4_.use_count());
+ EXPECT_EQ(2, entry5_.use_count());
+ EXPECT_EQ(1, entry6_.use_count());
+ EXPECT_EQ(1, entry7_.use_count());
+ EXPECT_EQ(3, lru.size());
+
+ // Now touch the entry agin to move it to the back of the list.
+ // This checks that the iterator stored in the entry as a result of the
+ // last touch operation is valid.
+ lru.touch(entry1_);
+
+ // Check this by adding two more entrys and checking reference counts
+ // to see what is stored.
+ lru.add(entry6_);
+ lru.add(entry7_);
+
+ EXPECT_EQ(2, entry1_.use_count());
+ EXPECT_EQ(1, entry2_.use_count());
+ EXPECT_EQ(1, entry3_.use_count());
+ EXPECT_EQ(1, entry4_.use_count());
+ EXPECT_EQ(1, entry5_.use_count());
+ EXPECT_EQ(2, entry6_.use_count());
+ EXPECT_EQ(2, entry7_.use_count());
+ EXPECT_EQ(3, lru.size());
+}
+
+// Dropped functor tests: tests that the function object is called when an
+// object expires from the list.
+TEST_F(LruListTest, Dropped) {
+
+ // Create an object with an expiration handler.
+ LruList<TestEntry> lru(3, new Dropped());
+
+ // Fill the list
+ lru.add(entry1_);
+ lru.add(entry2_);
+ lru.add(entry3_);
+
+ EXPECT_EQ(1, entry1_->getCode());
+ EXPECT_EQ(3, entry2_->getCode());
+
+ // Add another entry and check that the handler runs.
+ EXPECT_EQ(0, (entry1_->getCode() & 0x8000));
+ lru.add(entry4_);
+ EXPECT_NE(0, (entry1_->getCode() & 0x8000));
+
+ EXPECT_EQ(0, (entry2_->getCode() & 0x8000));
+ lru.add(entry5_);
+ EXPECT_NE(0, (entry2_->getCode() & 0x8000));
+
+ // Delete an entry and check that the handler does not run.
+ EXPECT_EQ(0, (entry3_->getCode() & 0x8000));
+ lru.remove(entry3_);
+ EXPECT_EQ(0, (entry3_->getCode() & 0x8000));
+}
+
+// Clear functor tests: tests whether all the elements in
+// the list are dropped properly and the size of list is
+// set to 0.
+TEST_F(LruListTest, Clear) {
+ // Create an object with an expiration handler.
+ LruList<TestEntry> lru(3, new Dropped());
+
+ // Fill the list
+ lru.add(entry1_);
+ lru.add(entry2_);
+ lru.add(entry3_);
+
+ EXPECT_EQ(1, entry1_->getCode());
+ EXPECT_EQ(3, entry2_->getCode());
+ EXPECT_EQ(4, entry3_->getCode());
+
+ EXPECT_EQ(0, (entry1_->getCode() & 0x8000));
+ EXPECT_EQ(0, (entry2_->getCode() & 0x8000));
+ EXPECT_EQ(0, (entry3_->getCode() & 0x8000));
+
+ // Clear the lru list, and check the drop handler run
+ lru.clear();
+ EXPECT_NE(0, (entry1_->getCode() & 0x8000));
+ EXPECT_NE(0, (entry2_->getCode() & 0x8000));
+ EXPECT_NE(0, (entry3_->getCode() & 0x8000));
+
+ EXPECT_EQ(0, lru.size());
+}
+
+// Miscellaneous tests - pathological conditions
+TEST_F(LruListTest, Miscellaneous) {
+
+ // Zero size list should not allow entrys to be added
+ LruList<TestEntry> lru_1(0);
+ lru_1.add(entry1_);
+ EXPECT_EQ(0, lru_1.size());
+ EXPECT_EQ(1, entry1_.use_count());
+
+ // Removing an uninserted entry should not affect the list.
+ LruList<TestEntry> lru_2(100);
+ lru_2.add(entry1_);
+ lru_2.add(entry2_);
+ lru_2.add(entry3_);
+ EXPECT_EQ(3, lru_2.size());
+
+ lru_2.remove(entry4_);
+ EXPECT_EQ(2, entry1_.use_count());
+ EXPECT_EQ(2, entry2_.use_count());
+ EXPECT_EQ(2, entry3_.use_count());
+ EXPECT_EQ(1, entry4_.use_count());
+ EXPECT_EQ(1, entry5_.use_count());
+ EXPECT_EQ(3, lru_2.size());
+
+ // Touching an uninserted entry should not affect the list.
+ lru_2.touch(entry5_);
+ EXPECT_EQ(2, entry1_.use_count());
+ EXPECT_EQ(2, entry2_.use_count());
+ EXPECT_EQ(2, entry3_.use_count());
+ EXPECT_EQ(1, entry4_.use_count());
+ EXPECT_EQ(1, entry5_.use_count());
+ EXPECT_EQ(3, lru_2.size());
+}
+
+} // namespace nsas
+} // namespace isc
diff --git a/src/lib/util/tests/qid_gen_unittest.cc b/src/lib/util/tests/qid_gen_unittest.cc
new file mode 100644
index 0000000..60c81ae
--- /dev/null
+++ b/src/lib/util/tests/qid_gen_unittest.cc
@@ -0,0 +1,60 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+
+/// \brief Test of QidGenerator
+///
+
+#include <gtest/gtest.h>
+
+#include <util/random/qid_gen.h>
+
+using namespace isc::util::random;
+
+// Tests the operation of the Qid generator
+
+// Check that getInstance returns a singleton
+TEST(QidGenerator, singleton) {
+ QidGenerator& g1 = QidGenerator::getInstance();
+ QidGenerator& g2 = QidGenerator::getInstance();
+
+ EXPECT_TRUE(&g1 == &g2);
+}
+
+TEST(QidGenerator, generate) {
+ // We'll assume that boost's generator is 'good enough', and won't
+ // do full statistical checking here. Let's just call it the xkcd
+ // test (http://xkcd.com/221/), and check if three consecutive
+ // generates are not all the same.
+ uint16_t one, two, three;
+ QidGenerator& gen = QidGenerator::getInstance();
+ one = gen.generateQid();
+ two = gen.generateQid();
+ three = gen.generateQid();
+ ASSERT_FALSE((one == two) && (one == three));
+}
diff --git a/src/lib/util/tests/random_number_generator_unittest.cc b/src/lib/util/tests/random_number_generator_unittest.cc
new file mode 100644
index 0000000..23d5b88
--- /dev/null
+++ b/src/lib/util/tests/random_number_generator_unittest.cc
@@ -0,0 +1,311 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <gtest/gtest.h>
+#include <boost/shared_ptr.hpp>
+
+#include <algorithm>
+#include <iostream>
+#include <vector>
+
+#include <util/random/random_number_generator.h>
+
+namespace isc {
+namespace util {
+namespace random {
+
+using namespace std;
+
+/// \brief Test Fixture Class for uniform random number generator
+///
+/// The hard part for this test is how to test that the number is random?
+/// and how to test that the number is uniformly distributed?
+/// Or maybe we can trust the boost implementation
+class UniformRandomIntegerGeneratorTest : public ::testing::Test {
+public:
+ UniformRandomIntegerGeneratorTest():
+ gen_(min_, max_)
+ {
+ }
+ virtual ~UniformRandomIntegerGeneratorTest(){}
+
+ int gen() { return gen_(); }
+ int max() const { return max_; }
+ int min() const { return min_; }
+
+private:
+ UniformRandomIntegerGenerator gen_;
+
+ const static int min_ = 1;
+ const static int max_ = 10;
+};
+
+// Some validation tests will incur performance penalty, so the tests are
+// made only in "debug" version with assert(). But if NDEBUG is defined
+// the tests will be failed since assert() is non-op in non-debug version.
+// The "#ifndef NDEBUG" is added to make the tests be performed only in
+// non-debug environment.
+// Note: the death test is not supported by all platforms. We need to
+// compile tests using it selectively.
+#if !defined(NDEBUG)
+// Test of the constructor
+TEST_F(UniformRandomIntegerGeneratorTest, Constructor) {
+ // The range must be min<=max
+ ASSERT_THROW(UniformRandomIntegerGenerator(3, 2), InvalidLimits);
+}
+#endif
+
+// Test of the generated integers are in the range [min, max]
+TEST_F(UniformRandomIntegerGeneratorTest, IntegerRange) {
+ vector<int> numbers;
+
+ // Generate a lot of random integers
+ for (int i = 0; i < max()*10; ++i) {
+ numbers.push_back(gen());
+ }
+
+ // Remove the duplicated values
+ sort(numbers.begin(), numbers.end());
+ vector<int>::iterator it = unique(numbers.begin(), numbers.end());
+
+ // make sure the numbers are in range [min, max]
+ ASSERT_EQ(it - numbers.begin(), max() - min() + 1);
+}
+
+/// \brief Test Fixture Class for weighted random number generator
+class WeightedRandomIntegerGeneratorTest : public ::testing::Test {
+public:
+ WeightedRandomIntegerGeneratorTest()
+ { }
+
+ virtual ~WeightedRandomIntegerGeneratorTest()
+ { }
+};
+
+// Test of the weighted random number generator constructor
+TEST_F(WeightedRandomIntegerGeneratorTest, Constructor) {
+ vector<double> probabilities;
+
+ // If no probabilities is provided, the smallest integer will always be generated
+ WeightedRandomIntegerGenerator gen(probabilities, 123);
+ for (int i = 0; i < 100; ++i) {
+ ASSERT_EQ(gen(), 123);
+ }
+
+/// Some validation tests will incur performance penalty, so the tests are
+/// made only in "debug" version with assert(). But if NDEBUG is defined
+/// the tests will be failed since assert() is non-op in non-debug version.
+/// The "#ifndef NDEBUG" is added to make the tests be performed only in
+/// non-debug environment.
+#if !defined(NDEBUG)
+ //The probability must be >= 0
+ probabilities.push_back(-0.1);
+ probabilities.push_back(1.1);
+ ASSERT_THROW(WeightedRandomIntegerGenerator gen2(probabilities),
+ InvalidProbValue);
+
+ //The probability must be <= 1.0
+ probabilities.clear();
+ probabilities.push_back(0.1);
+ probabilities.push_back(1.1);
+ ASSERT_THROW(WeightedRandomIntegerGenerator gen3(probabilities),
+ InvalidProbValue);
+
+ //The sum must be equal to 1.0
+ probabilities.clear();
+ probabilities.push_back(0.2);
+ probabilities.push_back(0.9);
+ ASSERT_THROW(WeightedRandomIntegerGenerator gen4(probabilities), SumNotOne);
+
+ //The sum must be equal to 1.0
+ probabilities.clear();
+ probabilities.push_back(0.3);
+ probabilities.push_back(0.2);
+ probabilities.push_back(0.1);
+ ASSERT_THROW(WeightedRandomIntegerGenerator gen5(probabilities), SumNotOne);
+#endif
+}
+
+// Test the randomization of the generator
+TEST_F(WeightedRandomIntegerGeneratorTest, WeightedRandomization) {
+ const int repeats = 100000;
+ // We repeat the simulation for N=repeats times
+ // for each probability p, its average is mu = N*p
+ // variance sigma^2 = N * p * (1-p)
+ // sigma = sqrt(N*2/9)
+ // we should make sure that mu - 4sigma < count < mu + 4sigma
+ // which means for 99.99366% of the time this should be true
+ {
+ double p = 0.5;
+ vector<double> probabilities;
+ probabilities.push_back(p);
+ probabilities.push_back(p);
+
+ // Uniformly generated integers
+ WeightedRandomIntegerGenerator gen(probabilities);
+ int c1 = 0;
+ int c2 = 0;
+ for (int i = 0; i < repeats; ++i){
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ }
+ }
+ double mu = repeats * p;
+ double sigma = sqrt(repeats * p * (1 - p));
+ ASSERT_TRUE(fabs(c1 - mu) < 4*sigma);
+ ASSERT_TRUE(fabs(c2 - mu) < 4*sigma);
+ }
+
+ {
+ vector<double> probabilities;
+ int c1 = 0;
+ int c2 = 0;
+ double p1 = 0.2;
+ double p2 = 0.8;
+ probabilities.push_back(p1);
+ probabilities.push_back(p2);
+ WeightedRandomIntegerGenerator gen(probabilities);
+ for (int i = 0; i < repeats; ++i) {
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ }
+ }
+ double mu1 = repeats * p1;
+ double mu2 = repeats * p2;
+ double sigma1 = sqrt(repeats * p1 * (1 - p1));
+ double sigma2 = sqrt(repeats * p2 * (1 - p2));
+ ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+ ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+ }
+
+ {
+ vector<double> probabilities;
+ int c1 = 0;
+ int c2 = 0;
+ double p1 = 0.8;
+ double p2 = 0.2;
+ probabilities.push_back(p1);
+ probabilities.push_back(p2);
+ WeightedRandomIntegerGenerator gen(probabilities);
+ for (int i = 0; i < repeats; ++i) {
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ }
+ }
+ double mu1 = repeats * p1;
+ double mu2 = repeats * p2;
+ double sigma1 = sqrt(repeats * p1 * (1 - p1));
+ double sigma2 = sqrt(repeats * p2 * (1 - p2));
+ ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+ ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+ }
+
+ {
+ vector<double> probabilities;
+ int c1 = 0;
+ int c2 = 0;
+ int c3 = 0;
+ double p1 = 0.5;
+ double p2 = 0.25;
+ double p3 = 0.25;
+ probabilities.push_back(p1);
+ probabilities.push_back(p2);
+ probabilities.push_back(p3);
+ WeightedRandomIntegerGenerator gen(probabilities);
+ for (int i = 0; i < repeats; ++i){
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ } else if (n == 2) {
+ ++c3;
+ }
+ }
+ double mu1 = repeats * p1;
+ double mu2 = repeats * p2;
+ double mu3 = repeats * p3;
+ double sigma1 = sqrt(repeats * p1 * (1 - p1));
+ double sigma2 = sqrt(repeats * p2 * (1 - p2));
+ double sigma3 = sqrt(repeats * p3 * (1 - p3));
+ ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+ ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+ ASSERT_TRUE(fabs(c3 - mu3) < 4*sigma3);
+ }
+}
+
+// Test the reset function of generator
+TEST_F(WeightedRandomIntegerGeneratorTest, ResetProbabilities) {
+ const int repeats = 100000;
+ vector<double> probabilities;
+ int c1 = 0;
+ int c2 = 0;
+ double p1 = 0.8;
+ double p2 = 0.2;
+ probabilities.push_back(p1);
+ probabilities.push_back(p2);
+ WeightedRandomIntegerGenerator gen(probabilities);
+ for (int i = 0; i < repeats; ++i) {
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ }
+ }
+ double mu1 = repeats * p1;
+ double mu2 = repeats * p2;
+ double sigma1 = sqrt(repeats * p1 * (1 - p1));
+ double sigma2 = sqrt(repeats * p2 * (1 - p2));
+ ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+ ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+
+ // Reset the probabilities
+ probabilities.clear();
+ c1 = c2 = 0;
+ p1 = 0.2;
+ p2 = 0.8;
+ probabilities.push_back(p1);
+ probabilities.push_back(p2);
+ gen.reset(probabilities);
+ for (int i = 0; i < repeats; ++i) {
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ }
+ }
+ mu1 = repeats * p1;
+ mu2 = repeats * p2;
+ sigma1 = sqrt(repeats * p1 * (1 - p1));
+ sigma2 = sqrt(repeats * p2 * (1 - p2));
+ ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+ ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+}
+
+} // namespace random
+} // namespace util
+} // namespace isc
diff --git a/src/lib/util/tests/run_unittests.cc b/src/lib/util/tests/run_unittests.cc
new file mode 100644
index 0000000..a2181cf
--- /dev/null
+++ b/src/lib/util/tests/run_unittests.cc
@@ -0,0 +1,23 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return (isc::util::unittests::run_all());
+}
diff --git a/src/lib/util/tests/sha1_unittest.cc b/src/lib/util/tests/sha1_unittest.cc
new file mode 100644
index 0000000..6d62349
--- /dev/null
+++ b/src/lib/util/tests/sha1_unittest.cc
@@ -0,0 +1,109 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+#include <string>
+
+#include <util/hash/sha1.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+
+namespace isc {
+namespace util {
+namespace hash {
+class Sha1Test : public ::testing::Test {
+protected:
+ Sha1Test() {}
+};
+
+// Tests copied from RFC 3174
+TEST_F(Sha1Test, Test1) {
+ SHA1Context sha;
+ uint8_t digest[SHA1_HASHSIZE];
+ uint8_t expected[SHA1_HASHSIZE] = {
+ 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e,
+ 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d
+ };
+
+ EXPECT_EQ(0, SHA1Reset(&sha));
+ EXPECT_EQ(0, SHA1Input(&sha, (const uint8_t *) "abc", 3));
+ EXPECT_EQ(0, SHA1Result(&sha, digest));
+ for (int i = 0; i < SHA1_HASHSIZE; i++) {
+ EXPECT_EQ(digest[i], expected[i]);
+ }
+}
+
+TEST_F(Sha1Test, Test2) {
+ SHA1Context sha;
+ string test("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
+ uint8_t digest[SHA1_HASHSIZE];
+ uint8_t expected[SHA1_HASHSIZE] = {
+ 0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, 0x6e, 0xba, 0xae,
+ 0x4a, 0xa1, 0xf9, 0x51, 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1
+ };
+
+ EXPECT_EQ(0, SHA1Reset(&sha));
+ EXPECT_EQ(0, SHA1Input(&sha, (const uint8_t *) test.c_str(),
+ test.length()));
+ EXPECT_EQ(0, SHA1Result(&sha, digest));
+ for (int i = 0; i < SHA1_HASHSIZE; i++) {
+ EXPECT_EQ(digest[i], expected[i]);
+ }
+}
+
+TEST_F(Sha1Test, Test3) {
+ SHA1Context sha;
+ uint8_t digest[SHA1_HASHSIZE];
+ uint8_t expected[SHA1_HASHSIZE] = {
+ 0x34, 0xaa, 0x97, 0x3c, 0xd4, 0xc4, 0xda, 0xa4, 0xf6, 0x1e,
+ 0xeb, 0x2b, 0xdb, 0xad, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6f
+ };
+
+ EXPECT_EQ(0, SHA1Reset(&sha));
+ for (int i = 0; i < 1000000; i++) {
+ EXPECT_EQ(0, SHA1Input(&sha, (const uint8_t *) "a", 1));
+ }
+ EXPECT_EQ(0, SHA1Result(&sha, digest));
+ for (int i = 0; i < SHA1_HASHSIZE; i++) {
+ EXPECT_EQ(digest[i], expected[i]);
+ }
+}
+
+TEST_F(Sha1Test, Test4) {
+ SHA1Context sha;
+ string test("01234567012345670123456701234567"
+ "01234567012345670123456701234567");
+ uint8_t digest[SHA1_HASHSIZE];
+ uint8_t expected[SHA1_HASHSIZE] = {
+ 0xde, 0xa3, 0x56, 0xa2, 0xcd, 0xdd, 0x90, 0xc7, 0xa7, 0xec,
+ 0xed, 0xc5, 0xeb, 0xb5, 0x63, 0x93, 0x4f, 0x46, 0x04, 0x52
+ };
+
+ EXPECT_EQ(0, SHA1Reset(&sha));
+ for (int i = 0; i < 10; i++) {
+ EXPECT_EQ(0, SHA1Input(&sha, (const uint8_t *) test.c_str(),
+ test.length()));
+ }
+ EXPECT_EQ(0, SHA1Result(&sha, digest));
+ for (int i = 0; i < SHA1_HASHSIZE; i++) {
+ EXPECT_EQ(digest[i], expected[i]);
+ }
+}
+
+} // namespace hash
+} // namespace util
+} // namespace isc
+
diff --git a/src/lib/util/tests/strutil_unittest.cc b/src/lib/util/tests/strutil_unittest.cc
new file mode 100644
index 0000000..cd3a9ca
--- /dev/null
+++ b/src/lib/util/tests/strutil_unittest.cc
@@ -0,0 +1,215 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <util/strutil.h>
+
+using namespace isc;
+using namespace isc::util;
+using namespace std;
+
+class StringUtilTest : public ::testing::Test {
+protected:
+ StringUtilTest()
+ {
+ }
+};
+
+
+// Check for slash replacement
+
+TEST_F(StringUtilTest, Slash) {
+
+ string instring = "";
+ isc::util::str::normalizeSlash(instring);
+ EXPECT_EQ("", instring);
+
+ instring = "C:\\A\\B\\C.D";
+ isc::util::str::normalizeSlash(instring);
+ EXPECT_EQ("C:/A/B/C.D", instring);
+
+ instring = "// \\ //";
+ isc::util::str::normalizeSlash(instring);
+ EXPECT_EQ("// / //", instring);
+}
+
+// Check that leading and trailing space trimming works
+
+TEST_F(StringUtilTest, Trim) {
+
+ // Empty and full string.
+ EXPECT_EQ("", isc::util::str::trim(""));
+ EXPECT_EQ("abcxyz", isc::util::str::trim("abcxyz"));
+
+ // Trim right-most blanks
+ EXPECT_EQ("ABC", isc::util::str::trim("ABC "));
+ EXPECT_EQ("ABC", isc::util::str::trim("ABC\t\t \n\t"));
+
+ // Left-most blank trimming
+ EXPECT_EQ("XYZ", isc::util::str::trim(" XYZ"));
+ EXPECT_EQ("XYZ", isc::util::str::trim("\t\t \tXYZ"));
+
+ // Right and left, with embedded spaces
+ EXPECT_EQ("MN \t OP", isc::util::str::trim("\t\tMN \t OP \t"));
+}
+
+// Check tokenization. Note that ASSERT_EQ is used to check the size of the
+// returned vector; if not as expected, the following references may be invalid
+// so should not be used.
+
+TEST_F(StringUtilTest, Tokens) {
+ vector<string> result;
+
+ // Default delimiters
+
+ // Degenerate cases
+ result = isc::util::str::tokens(""); // Empty string
+ EXPECT_EQ(0, result.size());
+
+ result = isc::util::str::tokens(" \n "); // String is all delimiters
+ EXPECT_EQ(0, result.size());
+
+ result = isc::util::str::tokens("abc"); // String has no delimiters
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("abc"), result[0]);
+
+ // String containing leading and/or trailing delimiters, no embedded ones.
+ result = isc::util::str::tokens("\txyz"); // One leading delimiter
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ result = isc::util::str::tokens("\t \nxyz"); // Multiple leading delimiters
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ result = isc::util::str::tokens("xyz\n"); // One trailing delimiter
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ result = isc::util::str::tokens("xyz \t"); // Multiple trailing
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ result = isc::util::str::tokens("\t xyz \n"); // Leading and trailing
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ // Embedded delimiters
+ result = isc::util::str::tokens("abc\ndef"); // 2 tokens, one separator
+ ASSERT_EQ(2, result.size());
+ EXPECT_EQ(string("abc"), result[0]);
+ EXPECT_EQ(string("def"), result[1]);
+
+ result = isc::util::str::tokens("abc\t\t\ndef"); // 2 tokens, 3 separators
+ ASSERT_EQ(2, result.size());
+ EXPECT_EQ(string("abc"), result[0]);
+ EXPECT_EQ(string("def"), result[1]);
+
+ result = isc::util::str::tokens("abc\n \tdef\t\tghi");
+ ASSERT_EQ(3, result.size()); // Multiple tokens, many delims
+ EXPECT_EQ(string("abc"), result[0]);
+ EXPECT_EQ(string("def"), result[1]);
+ EXPECT_EQ(string("ghi"), result[2]);
+
+ // Embedded and non-embedded delimiters
+
+ result = isc::util::str::tokens("\t\t \nabc\n \tdef\t\tghi \n\n");
+ ASSERT_EQ(3, result.size()); // Multiple tokens, many delims
+ EXPECT_EQ(string("abc"), result[0]);
+ EXPECT_EQ(string("def"), result[1]);
+ EXPECT_EQ(string("ghi"), result[2]);
+
+ // Non-default delimiter
+ result = isc::util::str::tokens("alpha/beta/ /gamma//delta/epsilon/", "/");
+ ASSERT_EQ(6, result.size());
+ EXPECT_EQ(string("alpha"), result[0]);
+ EXPECT_EQ(string("beta"), result[1]);
+ EXPECT_EQ(string(" "), result[2]);
+ EXPECT_EQ(string("gamma"), result[3]);
+ EXPECT_EQ(string("delta"), result[4]);
+ EXPECT_EQ(string("epsilon"), result[5]);
+
+ // Non-default delimiters (plural)
+ result = isc::util::str::tokens("+*--alpha*beta+ -gamma**delta+epsilon-+**",
+ "*+-");
+ ASSERT_EQ(6, result.size());
+ EXPECT_EQ(string("alpha"), result[0]);
+ EXPECT_EQ(string("beta"), result[1]);
+ EXPECT_EQ(string(" "), result[2]);
+ EXPECT_EQ(string("gamma"), result[3]);
+ EXPECT_EQ(string("delta"), result[4]);
+ EXPECT_EQ(string("epsilon"), result[5]);
+}
+
+// Changing case
+
+TEST_F(StringUtilTest, ChangeCase) {
+ string mixed("abcDEFghiJKLmno123[]{=+--+]}");
+ string upper("ABCDEFGHIJKLMNO123[]{=+--+]}");
+ string lower("abcdefghijklmno123[]{=+--+]}");
+
+ string test = mixed;
+ isc::util::str::lowercase(test);
+ EXPECT_EQ(lower, test);
+
+ test = mixed;
+ isc::util::str::uppercase(test);
+ EXPECT_EQ(upper, test);
+}
+
+// Formatting
+
+TEST_F(StringUtilTest, Formatting) {
+
+ vector<string> args;
+ args.push_back("arg1");
+ args.push_back("arg2");
+ args.push_back("arg3");
+
+ string format1 = "This is a string with no tokens";
+ EXPECT_EQ(format1, isc::util::str::format(format1, args));
+
+ string format2 = ""; // Empty string
+ EXPECT_EQ(format2, isc::util::str::format(format2, args));
+
+ string format3 = " "; // Empty string
+ EXPECT_EQ(format3, isc::util::str::format(format3, args));
+
+ string format4 = "String with %d non-string tokens %lf";
+ EXPECT_EQ(format4, isc::util::str::format(format4, args));
+
+ string format5 = "String with %s correct %s number of tokens %s";
+ string result5 = "String with arg1 correct arg2 number of tokens arg3";
+ EXPECT_EQ(result5, isc::util::str::format(format5, args));
+
+ string format6 = "String with %s too %s few tokens";
+ string result6 = "String with arg1 too arg2 few tokens";
+ EXPECT_EQ(result6, isc::util::str::format(format6, args));
+
+ string format7 = "String with %s too %s many %s tokens %s !";
+ string result7 = "String with arg1 too arg2 many arg3 tokens %s !";
+ EXPECT_EQ(result7, isc::util::str::format(format7, args));
+
+ string format8 = "String with embedded%s%s%stokens";
+ string result8 = "String with embeddedarg1arg2arg3tokens";
+ EXPECT_EQ(result8, isc::util::str::format(format8, args));
+
+ // Handle an empty vector
+ args.clear();
+ string format9 = "%s %s";
+ EXPECT_EQ(format9, isc::util::str::format(format9, args));
+}
diff --git a/src/lib/util/tests/time_utilities_unittest.cc b/src/lib/util/tests/time_utilities_unittest.cc
new file mode 100644
index 0000000..9261ea0
--- /dev/null
+++ b/src/lib/util/tests/time_utilities_unittest.cc
@@ -0,0 +1,161 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <time.h>
+
+#include <util/time_utilities.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc::util;
+
+// See time_utilities.cc
+namespace isc {
+namespace util {
+namespace detail {
+extern int64_t (*gettimeFunction)();
+}
+}
+}
+
+namespace {
+
+class DNSSECTimeTest : public ::testing::Test {
+protected:
+ ~DNSSECTimeTest() {
+ detail::gettimeFunction = NULL;
+ }
+};
+
+TEST_F(DNSSECTimeTest, fromText) {
+ // In most cases (in practice) the 32-bit and 64-bit versions should
+ // behave identically, so we'll mainly test the 32-bit version, which
+ // will be more commonly used in actual code (because many of the wire
+ // format time field are 32-bit). The subtle cases where these two
+ // return different values will be tested at the end of this test case.
+
+ // These are bogus and should be rejected
+ EXPECT_THROW(timeFromText32("2011 101120000"), InvalidTime);
+ EXPECT_THROW(timeFromText32("201101011200-0"), InvalidTime);
+
+ // Short length (or "decimal integer" version of representation;
+ // it's valid per RFC4034, but is not supported in this implementation)
+ EXPECT_THROW(timeFromText32("20100223"), InvalidTime);
+
+ // Leap year checks
+ EXPECT_THROW(timeFromText32("20110229120000"), InvalidTime);
+ EXPECT_THROW(timeFromText32("21000229120000"), InvalidTime);
+ EXPECT_NO_THROW(timeFromText32("20000229120000"));
+ EXPECT_NO_THROW(timeFromText32("20120229120000"));
+
+ // unusual case: this implementation allows SS=60 for "leap seconds"
+ EXPECT_NO_THROW(timeFromText32("20110101120060"));
+
+ // Out of range parameters
+ EXPECT_THROW(timeFromText32("19100223214617"), InvalidTime); // YY<1970
+ EXPECT_THROW(timeFromText32("20110001120000"), InvalidTime); // MM=00
+ EXPECT_THROW(timeFromText32("20111301120000"), InvalidTime); // MM=13
+ EXPECT_THROW(timeFromText32("20110100120000"), InvalidTime); // DD=00
+ EXPECT_THROW(timeFromText32("20110132120000"), InvalidTime); // DD=32
+ EXPECT_THROW(timeFromText32("20110431120000"), InvalidTime); // 'Apr31'
+ EXPECT_THROW(timeFromText32("20110101250000"), InvalidTime); // HH=25
+ EXPECT_THROW(timeFromText32("20110101126000"), InvalidTime); // mm=60
+ EXPECT_THROW(timeFromText32("20110101120061"), InvalidTime); // SS=61
+
+ // Feb 7, 06:28:15 UTC 2106 is the possible maximum time that can be
+ // represented as an unsigned 32bit integer without overflow.
+ EXPECT_EQ(4294967295LU, timeFromText32("21060207062815"));
+
+ // After that, timeFromText32() should start returning the second count
+ // modulo 2^32.
+ EXPECT_EQ(0, timeFromText32("21060207062816"));
+ EXPECT_EQ(10, timeFromText32("21060207062826"));
+
+ // On the other hand, the 64-bit version should return monotonically
+ // increasing counters.
+ EXPECT_EQ(4294967296LL, timeFromText64("21060207062816"));
+ EXPECT_EQ(4294967306LL, timeFromText64("21060207062826"));
+}
+
+// This helper templated function tells timeToText32 a faked current time.
+// The template parameter is that faked time in the form of int64_t seconds
+// since epoch.
+template <int64_t NOW>
+int64_t
+testGetTime() {
+ return (NOW);
+}
+
+// Seconds since epoch for the year 10K eve. Commonly used in some tests
+// below.
+const uint64_t YEAR10K_EVE = 253402300799LL;
+
+TEST_F(DNSSECTimeTest, toText) {
+ // Check a basic case with the default (normal) gettimeFunction
+ // based on the "real current time".
+ // Note: this will fail after year 2078, but at that point we won't use
+ // this program anyway:-)
+ EXPECT_EQ("20100311233000", timeToText32(1268350200));
+
+ // Set the current time to: Feb 18 09:04:14 UTC 2012 (an arbitrary choice
+ // in the range of the first half of uint32 since epoch).
+ detail::gettimeFunction = testGetTime<1329555854LL>;
+
+ // Test the "year 2038" problem.
+ // Check the result of toText() for "INT_MIN" in int32_t. It's in the
+ // 68-year range from the faked current time, so the result should be
+ // in year 2038, instead of 1901.
+ EXPECT_EQ("20380119031408", timeToText64(0x80000000L));
+ EXPECT_EQ("20380119031408", timeToText32(0x80000000L));
+
+ // A controversial case: what should we do with "-1"? It's out of range
+ // in future, but according to RFC time before epoch doesn't seem to be
+ // considered "in-range" either. Our toText() implementation handles
+ // this range as a special case and always treats them as future time
+ // until year 2038. This won't be a real issue in practice, though,
+ // since such too large values won't be used in actual deployment by then.
+ EXPECT_EQ("21060207062815", timeToText32(0xffffffffL));
+
+ // After the singular point of year 2038, the first half of uint32 can
+ // point to a future time.
+ // Set the current time to: Apr 1 00:00:00 UTC 2038:
+ detail::gettimeFunction = testGetTime<2153692800LL>;
+ // then time "10" is Feb 7 06:28:26 UTC 2106
+ EXPECT_EQ("21060207062826", timeToText32(10));
+ // in 64-bit, it's 2^32 + 10
+ EXPECT_EQ("21060207062826", timeToText64(0x10000000aLL));
+
+ // After year 2106, the upper half of uint32 can point to past time
+ // (as it should).
+ detail::gettimeFunction = testGetTime<0x10000000aLL>;
+ EXPECT_EQ("21060207062815", timeToText32(0xffffffffL));
+
+ // Try very large time value. Actually it's the possible farthest time
+ // that can be represented in the form of YYYYMMDDHHmmSS.
+ EXPECT_EQ("99991231235959", timeToText64(YEAR10K_EVE));
+ detail::gettimeFunction = testGetTime<YEAR10K_EVE - 10>;
+ EXPECT_EQ("99991231235959", timeToText32(4294197631LU));
+}
+
+TEST_F(DNSSECTimeTest, overflow) {
+ // Jan 1, Year 10,000.
+ EXPECT_THROW(timeToText64(253402300800LL), InvalidTime);
+ detail::gettimeFunction = testGetTime<YEAR10K_EVE - 10>;
+ EXPECT_THROW(timeToText32(4294197632LU), InvalidTime);
+}
+
+}
diff --git a/src/lib/util/time_utilities.cc b/src/lib/util/time_utilities.cc
new file mode 100644
index 0000000..9303ab5
--- /dev/null
+++ b/src/lib/util/time_utilities.cc
@@ -0,0 +1,207 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+
+#include <sys/time.h>
+
+#include <string>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+#include <stdio.h>
+#include <time.h>
+
+#include <exceptions/exceptions.h>
+
+#include <util/time_utilities.h>
+
+using namespace std;
+
+namespace {
+int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+inline bool
+isLeap(const int y) {
+ return ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0);
+}
+
+unsigned int
+yearSecs(const int year) {
+ return ((isLeap(year) ? 366 : 365 ) * 86400);
+}
+
+unsigned int
+monthSecs(const int month, const int year) {
+ return ((days[month] + ((month == 1 && isLeap(year)) ? 1 : 0 )) * 86400);
+}
+}
+
+namespace isc {
+namespace util {
+
+string
+timeToText64(uint64_t value) {
+ struct tm tm;
+ unsigned int secs;
+
+ // We cannot rely on gmtime() because time_t may not be of 64 bit
+ // integer. The following conversion logic is borrowed from BIND 9.
+ tm.tm_year = 70;
+ while ((secs = yearSecs(tm.tm_year + 1900)) <= value) {
+ value -= secs;
+ ++tm.tm_year;
+ if (tm.tm_year + 1900 > 9999) {
+ isc_throw(InvalidTime,
+ "Time value out of range (year > 9999): " <<
+ tm.tm_year + 1900);
+ }
+ }
+ tm.tm_mon = 0;
+ while ((secs = monthSecs(tm.tm_mon, tm.tm_year + 1900)) <= value) {
+ value -= secs;
+ tm.tm_mon++;
+ }
+ tm.tm_mday = 1;
+ while (86400 <= value) {
+ value -= 86400;
+ ++tm.tm_mday;
+ }
+ tm.tm_hour = 0;
+ while (3600 <= value) {
+ value -= 3600;
+ ++tm.tm_hour;
+ }
+ tm.tm_min = 0;
+ while (60 <= value) {
+ value -= 60;
+ ++tm.tm_min;
+ }
+ tm.tm_sec = value; // now t < 60, so this substitution is safe.
+
+ ostringstream oss;
+ oss << setfill('0')
+ << setw(4) << tm.tm_year + 1900
+ << setw(2) << tm.tm_mon + 1
+ << setw(2) << tm.tm_mday
+ << setw(2) << tm.tm_hour
+ << setw(2) << tm.tm_min
+ << setw(2) << tm.tm_sec;
+ return (oss.str());
+}
+
+// timeToText32() below uses the current system time. To test it with
+// unusual current time values we introduce the following function pointer;
+// when it's non NULL, we call it to get the (normally faked) current time.
+// Otherwise we use the standard gettimeofday(2). This hook is specifically
+// intended for testing purposes, so, even if it's visible outside of this
+// library, it's not even declared in a header file.
+namespace detail {
+int64_t (*gettimeFunction)() = NULL;
+
+int64_t
+gettimeWrapper() {
+ if (gettimeFunction != NULL) {
+ return (gettimeFunction());
+ }
+
+ struct timeval now;
+ gettimeofday(&now, NULL);
+
+ return (static_cast<int64_t>(now.tv_sec));
+}
+}
+
+string
+timeToText32(const uint32_t value) {
+ // We first adjust the time to the closest epoch based on the current time.
+ // Note that the following variables must be signed in order to handle
+ // time until year 2038 correctly.
+ const int64_t start = detail::gettimeWrapper() - 0x7fffffff;
+ int64_t base = 0;
+ int64_t t;
+ while ((t = (base + value)) < start) {
+ base += 0x100000000LL;
+ }
+
+ // Then convert it to text.
+ return (timeToText64(t));
+}
+
+namespace {
+const size_t DATE_LEN = 14; // YYYYMMDDHHmmSS
+
+inline void
+checkRange(const int min, const int max, const int value,
+ const string& valname)
+{
+ if ((value >= min) && (value <= max)) {
+ return;
+ }
+ isc_throw(InvalidTime, "Invalid " << valname << "value: " << value);
+}
+}
+
+uint64_t
+timeFromText64(const string& time_txt) {
+ // Confirm the source only consists digits. sscanf() allows some
+ // minor exceptions.
+ for (int i = 0; i < time_txt.length(); ++i) {
+ if (!isdigit(time_txt.at(i))) {
+ isc_throw(InvalidTime, "Couldn't convert non-numeric time value: "
+ << time_txt);
+ }
+ }
+
+ int year, month, day, hour, minute, second;
+ if (time_txt.length() != DATE_LEN ||
+ sscanf(time_txt.c_str(), "%4d%2d%2d%2d%2d%2d",
+ &year, &month, &day, &hour, &minute, &second) != 6)
+ {
+ isc_throw(InvalidTime, "Couldn't convert time value: " << time_txt);
+ }
+
+ checkRange(1970, 9999, year, "year");
+ checkRange(1, 12, month, "month");
+ checkRange(1, days[month - 1] + ((month == 2 && isLeap(year)) ? 1 : 0),
+ day, "day");
+ checkRange(0, 23, hour, "hour");
+ checkRange(0, 59, minute, "minute");
+ checkRange(0, 60, second, "second"); // 60 == leap second.
+
+ uint64_t timeval = second + (60 * minute) + (3600 * hour) +
+ ((day - 1) * 86400);
+ for (int m = 0; m < (month - 1); ++m) {
+ timeval += days[m] * 86400;
+ }
+ if (isLeap(year) && month > 2) {
+ timeval += 86400;
+ }
+ for (int y = 1970; y < year; ++y) {
+ timeval += ((isLeap(y) ? 366 : 365 ) * 86400);
+ }
+
+ return (timeval);
+}
+
+uint32_t
+timeFromText32(const string& time_txt) {
+ // The implicit conversion from uint64_t to uint32_t should just work here,
+ // because we only need to drop higher 32 bits.
+ return (timeFromText64(time_txt));
+}
+
+}
+}
diff --git a/src/lib/util/time_utilities.h b/src/lib/util/time_utilities.h
new file mode 100644
index 0000000..a53089d
--- /dev/null
+++ b/src/lib/util/time_utilities.h
@@ -0,0 +1,173 @@
+// Copyright (C) 2009 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 __TIME_UTILITIES_H
+#define __TIME_UTILITIES_H 1
+
+#include <string>
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#include <exceptions/exceptions.h>
+
+//
+// Note: this helper module isn't specific to the DNS protocol per se.
+// We should probably move this to somewhere else, possibly in some common
+// utility area.
+//
+
+namespace isc {
+namespace util {
+
+///
+/// \brief A standard DNS (or ISC) module exception that is thrown if
+/// a time conversion function encounters bad input
+///
+class InvalidTime : public Exception {
+public:
+ InvalidTime(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+namespace detail {
+/// Return the current time in seconds
+///
+/// This function returns the "current" time in seconds from epoch
+/// (00:00:00 January 1, 1970) as a 64-bit signed integer. The return
+/// value can represent a point of time before epoch as a negative number.
+///
+/// This function is provided to help test time conscious implementations
+/// such as DNSSEC and TSIG signatures. It is difficult to test them with
+/// an unusual or a specifically chosen "current" via system-provided
+/// library functions to get time. This function acts as a straightforward
+/// wrapper of such a library function, but provides test code with a hook
+/// to return an arbitrary time value: if \c isc::util::detail::gettimeFunction
+/// is set to a pointer of function that returns 64-bit signed integer,
+/// \c gettimeWrapper() calls that function instead of the system library.
+///
+/// This hook variable is specifically intended for testing purposes, so,
+/// even if it's visible outside of this library, it's not even declared in a
+/// header file.
+///
+/// If the implementation doesn't need to be tested with faked current time,
+/// it should simply use the system supplied library function instead of
+/// this one.
+int64_t gettimeWrapper();
+}
+
+///
+/// \name DNSSEC time conversion functions.
+///
+/// These functions convert between times represented in seconds (in integer)
+/// since epoch and those in the textual form used in the RRSIG records.
+/// For integers we provide both 32-bit and 64-bit versions.
+/// The RRSIG expiration and inception fields are both 32-bit unsigned
+/// integers, so 32-bit versions would be more useful for protocol operations.
+/// However, with 32-bit integers we need to take into account wrap-around
+/// points and compare values using the serial number arithmetic as specified
+/// in RFC4034, which would be more error prone. We therefore provide 64-bit
+/// versions, too.
+///
+/// The timezone is always UTC for these functions.
+//@{
+/// Convert textual DNSSEC time to integer, 64-bit version.
+///
+/// The textual form must only consist of digits and be in the form of
+/// YYYYMMDDHHmmSS, where:
+/// - YYYY must be between 1970 and 9999
+/// - MM must be between 01 and 12
+/// - DD must be between 01 and 31 and must be a valid day for the month
+/// represented in 'MM'. For example, if MM is 04, DD cannot be 31.
+/// DD can be 29 when MM is 02 only when YYYY is a leap year.
+/// - HH must be between 00 and 23
+/// - mm must be between 00 and 59
+/// - SS must be between 00 and 60
+///
+/// For all fields the range includes the begin and end values. Note that
+/// 60 is allowed for 'SS', intending a leap second, although in real operation
+/// it's unlikely to be specified.
+///
+/// If the given text is valid, this function converts it to an unsigned
+/// 64-bit number of seconds since epoch (1 January 1970 00:00:00) and returns
+/// the converted value. 64 bits are sufficient to represent all possible
+/// values for the valid format uniquely, so there is no overflow.
+///
+/// \note RFC4034 also defines the textual form of an unsigned decimal integer
+/// for the corresponding time in seconds. This function doesn't support
+/// this form, and if given it throws an exception of class \c InvalidTime.
+///
+/// \exception InvalidTime The given textual representation is invalid.
+///
+/// \param time_txt Textual time in the form of YYYYMMDDHHmmSS
+/// \return Seconds since epoch corresponding to \c time_txt
+uint64_t
+timeFromText64(const std::string& time_txt);
+
+/// Convert textual DNSSEC time to integer, 32-bit version.
+///
+/// This version is the same as \c timeFromText64() except that the return
+/// value is wrapped around to an unsigned 32-bit integer, simply dropping
+/// the upper 32 bits.
+uint32_t
+timeFromText32(const std::string& time_txt);
+
+/// Convert integral DNSSEC time to textual form, 64-bit version.
+///
+/// This function takes an integer that would be seconds since epoch and
+/// converts it in the form of YYYYMMDDHHmmSS. For example, if \c value is
+/// 0, it returns "19700101000000". If the value corresponds to a point
+/// of time on and after year 10,000, which cannot be represented in the
+/// YYYY... form, an exception of class \c InvalidTime will be thrown.
+///
+/// \exception InvalidTime The given time specifies on or after year 10,000.
+/// \exception Other A standard exception, if resource allocation for the
+/// returned text fails.
+///
+/// \param value Seconds since epoch to be converted.
+/// \return Textual representation of \c value in the form of YYYYMMDDHHmmSS.
+std::string
+timeToText64(uint64_t value);
+
+/// Convert integral DNSSEC time to textual form, 32-bit version.
+///
+/// This version is the same as \c timeToText64(), but the time value
+/// is expected to be the lower 32 bits of the full 64-bit value.
+/// These two will be different on and after a certain point of time
+/// in year 2106, so this function internally resolves the ambiguity
+/// using the current system time at the time of function call;
+/// it first identifies the range of [N*2^32 - 2^31, N*2^32 + 2^31)
+/// that contains the current time, and interprets \c value in the context
+/// of that range. It then applies the same process as \c timeToText64().
+///
+/// There is one important exception in this processing, however.
+/// Until 19 Jan 2038 03:14:08 (2^31 seconds since epoch), this range
+/// would contain time before epoch. In order to ensure the returned
+/// value is also a valid input to \c timeFromText, this function uses
+/// a special range [0, 2^32) until that time. As a result, all upper
+/// half of the 32-bit values are treated as a future time. For example,
+/// 2^32-1 (the highest value in 32-bit unsigned integers) will be converted
+/// to "21060207062815", instead of "19691231235959".
+std::string
+timeToText32(const uint32_t value);
+
+//@}
+}
+}
+
+#endif // __DNSSECTIME_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/unittests/Makefile.am b/src/lib/util/unittests/Makefile.am
new file mode 100644
index 0000000..83235f2
--- /dev/null
+++ b/src/lib/util/unittests/Makefile.am
@@ -0,0 +1,22 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+lib_LTLIBRARIES = libutil_unittests.la
+libutil_unittests_la_SOURCES = fork.h fork.cc resolver.h
+libutil_unittests_la_SOURCES += newhook.h newhook.cc
+libutil_unittests_la_SOURCES += testdata.h testdata.cc
+if HAVE_GTEST
+libutil_unittests_la_SOURCES += run_all.h run_all.cc
+libutil_unittests_la_SOURCES += textdata.h
+endif
+
+libutil_unittests_la_CPPFLAGS = $(AM_CPPFLAGS)
+if HAVE_GTEST
+libutil_unittests_la_CPPFLAGS += $(GTEST_INCLUDES)
+endif
+
+libutil_unittests_la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
+libutil_unittests_la_LIBADD += $(top_builddir)/src/lib/util/io/libutil_io.la
+libutil_unittests_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+
+CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/util/unittests/README b/src/lib/util/unittests/README
new file mode 100644
index 0000000..0ed888f
--- /dev/null
+++ b/src/lib/util/unittests/README
@@ -0,0 +1,5 @@
+This directory contains some code that is useful while writing various
+unittest code. It doesn't contain any code that would actually run in
+bind10.
+
+Because this is a test code, we do not test it explicitly.
diff --git a/src/lib/util/unittests/fork.cc b/src/lib/util/unittests/fork.cc
new file mode 100644
index 0000000..3414a3c
--- /dev/null
+++ b/src/lib/util/unittests/fork.cc
@@ -0,0 +1,145 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "fork.h"
+
+#include <util/io/fd.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <cerrno>
+#include <stdlib.h>
+#include <stdio.h>
+
+using namespace isc::util::io;
+
+namespace {
+
+// Just a NOP function to ignore a signal but let it interrupt function.
+void no_handler(int) { }
+
+};
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+bool
+process_ok(pid_t process) {
+ // Create a timeout
+ struct sigaction ignored, original;
+ memset(&ignored, 0, sizeof ignored);
+ ignored.sa_handler = no_handler;
+ if (sigaction(SIGALRM, &ignored, &original)) {
+ return false;
+ }
+ // It is long, but if everything is OK, it'll not happen
+ alarm(10);
+ int status;
+ int result(waitpid(process, &status, 0) == -1);
+ // Cancel the alarm and return the original handler
+ alarm(0);
+ if (sigaction(SIGALRM, &original, NULL)) {
+ return false;
+ }
+ // Check what we found out
+ if (result) {
+ if (errno == EINTR)
+ kill(process, SIGTERM);
+ return false;
+ }
+ return WIFEXITED(status) && WEXITSTATUS(status) == 0;
+}
+
+/*
+ * This creates a pipe, forks and feeds the pipe with given data.
+ * Used to provide the input in non-blocking/asynchronous way.
+ */
+pid_t
+provide_input(int *read_pipe, const void *input, const size_t length)
+{
+ int pipes[2];
+ if (pipe(pipes)) {
+ return -1;
+ }
+ *read_pipe = pipes[0];
+ pid_t pid(fork());
+ if (pid) { // We are in the parent
+ return pid;
+ } else { // This is in the child, just puth the data there
+ close(pipes[0]);
+ if (!write_data(pipes[1], input, length)) {
+ exit(1);
+ } else {
+ close(pipes[1]);
+ exit(0);
+ }
+ }
+}
+
+/*
+ * This creates a pipe, forks and reads the pipe and compares it
+ * with given data. Used to check output of run in asynchronous way.
+ */
+pid_t
+check_output(int *write_pipe, const void *output, const size_t length)
+{
+ int pipes[2];
+ if (pipe(pipes)) {
+ return -1;
+ }
+ *write_pipe = pipes[1];
+ pid_t pid(fork());
+ if (pid) { // We are in parent
+ close(pipes[0]);
+ return pid;
+ } else {
+ close(pipes[1]);
+ // We don't return the memory, but we're in tests and end this process
+ // right away.
+ unsigned char *buffer = new unsigned char[length + 1];
+ // Try to read one byte more to see if the output ends here
+ size_t got_length(read_data(pipes[0], buffer, length + 1));
+ bool ok(true);
+ if (got_length != length) {
+ fprintf(stderr, "Different length (expected %u, got %u)\n",
+ static_cast<unsigned>(length),
+ static_cast<unsigned>(got_length));
+ ok = false;
+ }
+ if(!ok || memcmp(buffer, output, length)) {
+ const unsigned char *output_c(static_cast<const unsigned char *>(
+ output));
+ // If they differ, print what we have
+ for(size_t i(0); i != got_length; ++ i) {
+ fprintf(stderr, "%02hhx", buffer[i]);
+ }
+ fprintf(stderr, "\n");
+ for(size_t i(0); i != length; ++ i) {
+ fprintf(stderr, "%02hhx", output_c[i]);
+ }
+ fprintf(stderr, "\n");
+ exit(1);
+ } else {
+ exit(0);
+ }
+ }
+}
+
+}
+}
+}
diff --git a/src/lib/util/unittests/fork.h b/src/lib/util/unittests/fork.h
new file mode 100644
index 0000000..3331cfa
--- /dev/null
+++ b/src/lib/util/unittests/fork.h
@@ -0,0 +1,52 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __UTIL_UNITTESTS_FORK_H
+#define __UTIL_UNITTESTS_FORK_H 1
+
+#include <unistd.h>
+
+/**
+ * @file fork.h
+ * @short Help functions to fork the test case process.
+ * Various functions to fork a process and feed some data to pipe, check
+ * its output and such lives here.
+ */
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+/**
+ * @short Checks that a process terminates correctly.
+ * Waits for a process to terminate (with a short timeout, this should be
+ * used whan the process is about tu terminate) and checks its exit code.
+ *
+ * @return True if the process terminates with 0, false otherwise.
+ * @param process The ID of process to wait for.
+ */
+bool
+process_ok(pid_t process);
+
+pid_t
+provide_input(int *read_pipe, const void *input, const size_t length);
+
+pid_t
+check_output(int *write_pipe, const void *output, const size_t length);
+
+} // End of the namespace
+}
+}
+
+#endif // __UTIL_UNITTESTS_FORK_H
diff --git a/src/lib/util/unittests/newhook.cc b/src/lib/util/unittests/newhook.cc
new file mode 100644
index 0000000..9e545a5
--- /dev/null
+++ b/src/lib/util/unittests/newhook.cc
@@ -0,0 +1,51 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdlib.h>
+
+#include <new>
+#include <stdexcept>
+
+#include "newhook.h"
+
+#ifdef ENABLE_CUSTOM_OPERATOR_NEW
+void*
+operator new(size_t size) throw(std::bad_alloc) {
+ if (isc::util::unittests::force_throw_on_new &&
+ size == isc::util::unittests::throw_size_on_new) {
+ throw std::bad_alloc();
+ }
+ void* p = malloc(size);
+ if (p == NULL) {
+ throw std::bad_alloc();
+ }
+ return (p);
+}
+
+void
+operator delete(void* p) throw() {
+ if (p != NULL) {
+ free(p);
+ }
+}
+#endif
+
+namespace isc {
+namespace util {
+namespace unittests {
+bool force_throw_on_new = false;
+size_t throw_size_on_new = 0;
+}
+}
+}
diff --git a/src/lib/util/unittests/newhook.h b/src/lib/util/unittests/newhook.h
new file mode 100644
index 0000000..7eb8ade
--- /dev/null
+++ b/src/lib/util/unittests/newhook.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __UTIL_UNITTESTS_NEWHOOK_H
+#define __UTIL_UNITTESTS_NEWHOOK_H 1
+
+/**
+ * @file newhook.h
+ * @short Enable the use of special operator new that throws for testing.
+ *
+ * This small utility allows a test case to force the global operator new
+ * to throw for a given size to test a case where memory allocation fails
+ * (which normally doesn't happen). To enable the feature, everything must
+ * be built with defining ENABLE_CUSTOM_OPERATOR_NEW beforehand, and set
+ * \c force_throw_on_new to \c true and \c throw_size_on_new to the size
+ * of data that should trigger the exception, immediately before starting
+ * the specific test that needs the exception.
+ *
+ * Example:
+ * \code #include <util/unittests/newhook.h>
+ * ...
+ * TEST(SomeTest, newException) {
+ * isc::util::unittests::force_throw_on_new = true;
+ * isc::util::unittests::throw_size_on_new = sizeof(Foo);
+ * try {
+ * // this will do 'new Foo()' internally and should throw
+ * createFoo();
+ * isc::util::unittests::force_throw_on_new = false;
+ * ASSERT_FALSE(true) << "Expected throw on new";
+ * } catch (const std::bad_alloc&) {
+ * isc::util::unittests::force_throw_on_new = false;
+ * // do some integrity check, etc, if necessary
+ * }
+ * } \endcode
+ *
+ * Replacing the global operator new (and delete) is a dangerous technique,
+ * and triggering an exception solely based on the allocation size is not
+ * reliable, so this feature is disabled by default two-fold: The
+ * ENABLE_CUSTOM_OPERATOR_NEW build time variable, and run-time
+ * \c force_throw_on_new.
+ */
+
+namespace isc {
+namespace util {
+namespace unittests {
+/// Switch to enable the use of special operator new
+///
+/// This is set to \c false by default.
+extern bool force_throw_on_new;
+
+/// The allocation size that triggers an exception in the special operator new
+///
+/// This is the exact size that causes an exception to be thrown;
+/// for example, if it is set to 100, an attempt of allocating 100 bytes
+/// will result in an exception, but allocation attempt for 101 bytes won't
+/// (unless, of course, memory is really exhausted and allocation really
+/// fails).
+///
+/// The default value is 0. The value of this variable has no meaning
+/// unless the use of the special operator is enabled at build time and
+/// via \c force_throw_on_new.
+extern size_t throw_size_on_new;
+}
+}
+}
+
+#endif // __UTIL_UNITTESTS_NEWHOOK_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/unittests/resolver.h b/src/lib/util/unittests/resolver.h
new file mode 100644
index 0000000..560a892
--- /dev/null
+++ b/src/lib/util/unittests/resolver.h
@@ -0,0 +1,193 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef UTIL_UNITTEST_RESOLVER_H
+#define UTIL_UNITTEST_RESOLVER_H
+
+/// \file resolver.h
+/// \brief Fake resolver
+
+#include <map>
+#include <dns/rrset.h>
+#include <dns/question.h>
+#include <dns/message.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+#include <dns/rrttl.h>
+#include <resolve/resolver_interface.h>
+#include <gtest/gtest.h>
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+/// \brief Put rrset into a message as an answer
+inline static isc::dns::MessagePtr
+createResponseMessage(isc::dns::RRsetPtr answer_rrset)
+{
+ isc::dns::MessagePtr response(new isc::dns::Message(
+ isc::dns::Message::RENDER));
+ response->setOpcode(isc::dns::Opcode::QUERY());
+ response->setRcode(isc::dns::Rcode::NOERROR());
+ response->addRRset(isc::dns::Message::SECTION_ANSWER, answer_rrset);
+ return response;
+}
+
+/// \brief Mock resolver
+///
+/// This class pretends to be a resolver. However, it only stores the
+/// requests and can answer them right away by prepared answers. It doesn't
+/// do any real work and is intended for testing purposes.
+class TestResolver : public isc::resolve::ResolverInterface {
+ private:
+ bool checkIndex(size_t index) {
+ return (requests.size() > index);
+ }
+
+ typedef std::map<isc::dns::Question, isc::dns::RRsetPtr>
+ PresetAnswers;
+ PresetAnswers answers_;
+ public:
+ typedef std::pair<isc::dns::QuestionPtr, CallbackPtr> Request;
+ /// \brief List of requests the tested class sent trough resolve
+ std::vector<Request> requests;
+
+ /// \brief Destructor
+ ///
+ /// This is important. All callbacks in the requests vector must be
+ /// called to remove them from internal loops. Without this, destroying
+ /// the NSAS object will leave memory assigned.
+ ~TestResolver() {
+ for (size_t i = 0; i < requests.size(); ++i) {
+ requests[i].second->failure();
+ }
+ }
+
+ /// \brief Testing version of resolve
+ ///
+ /// If there's a prepared answer (provided by addPresetAnswer), it
+ /// answers it right away. Otherwise it just stores the request in
+ /// the requests member so it can be examined later.
+ virtual void resolve(const isc::dns::QuestionPtr& q,
+ const CallbackPtr& c)
+ {
+ PresetAnswers::iterator it(answers_.find(*q));
+ if (it == answers_.end()) {
+ requests.push_back(Request(q, c));
+ } else {
+ if (it->second) {
+ c->success(createResponseMessage(it->second));
+ } else {
+ c->failure();
+ }
+ }
+ }
+
+ /// \brief Add a preset answer.
+ ///
+ /// Add a preset answer. If shared_ptr() is passed (eg. NULL),
+ /// it will generate failure. If the question is not preset,
+ /// it goes to requests and you can answer later.
+ void addPresetAnswer(const isc::dns::Question& question,
+ isc::dns::RRsetPtr answer)
+ {
+ answers_[question] = answer;
+ }
+
+ /// \brief Thrown if the query at the given index does not exist.
+ class NoSuchRequest : public std::exception { };
+
+ /// \brief Thrown if the answer does not match the query
+ class DifferentRequest : public std::exception { };
+
+ /// \brief Provides the question of request on given answer
+ isc::dns::QuestionPtr operator[](size_t index) {
+ if (index >= requests.size()) {
+ throw NoSuchRequest();
+ }
+ return (requests[index].first);
+ }
+ /// \brief Test it asks for IP addresses
+ /// Looks if the two provided requests in resolver are A and AAAA.
+ /// Sorts them so index1 is A.
+ ///
+ /// Returns false if there aren't enough elements
+ bool asksIPs(const isc::dns::Name& name, size_t index1,
+ size_t index2)
+ {
+ size_t max = (index1 < index2) ? index2 : index1;
+ if (!checkIndex(max)) {
+ return false;
+ }
+ EXPECT_EQ(name, (*this)[index1]->getName());
+ EXPECT_EQ(name, (*this)[index2]->getName());
+ EXPECT_EQ(isc::dns::RRClass::IN(), (*this)[index1]->getClass());
+ EXPECT_EQ(isc::dns::RRClass::IN(), (*this)[index2]->getClass());
+ // If they are the other way around, swap
+ if ((*this)[index1]->getType() == isc::dns::RRType::AAAA() &&
+ (*this)[index2]->getType() == isc::dns::RRType::A())
+ {
+ TestResolver::Request tmp((*this).requests[index1]);
+ (*this).requests[index1] =
+ (*this).requests[index2];
+ (*this).requests[index2] = tmp;
+ }
+ // Check the correct addresses
+ EXPECT_EQ(isc::dns::RRType::A(), (*this)[index1]->getType());
+ EXPECT_EQ(isc::dns::RRType::AAAA(), (*this)[index2]->getType());
+ return (true);
+ }
+
+ /// \brief Answer a request
+ /// Sends a simple answer to a query.
+ /// 1) Provide index of a query and the address(es) to pass.
+ /// 2) Provide index of query and components of address to pass.
+ void answer(size_t index, isc::dns::RRsetPtr& set) {
+ if (index >= requests.size()) {
+ throw NoSuchRequest();
+ }
+ requests[index].second->success(createResponseMessage(set));
+ }
+
+ void answer(size_t index, const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ const isc::dns::rdata::Rdata& rdata, size_t TTL = 100)
+ {
+ isc::dns::RRsetPtr set(new isc::dns::RRset(name,
+ isc::dns::RRClass::IN(),
+ type,
+ isc::dns::RRTTL(TTL)));
+ set->addRdata(rdata);
+ answer(index, set);
+ }
+ /// \Answer the query at index by list of nameservers
+ void provideNS(size_t index, isc::dns::RRsetPtr nameservers)
+ {
+ if (index >= requests.size()) {
+ throw NoSuchRequest();
+ }
+ if (requests[index].first->getName() != nameservers->getName() ||
+ requests[index].first->getType() != isc::dns::RRType::NS())
+ {
+ throw DifferentRequest();
+ }
+ requests[index].second->success(createResponseMessage(nameservers));
+ }
+};
+
+}
+}
+}
+
+#endif
diff --git a/src/lib/util/unittests/run_all.cc b/src/lib/util/unittests/run_all.cc
new file mode 100644
index 0000000..5f50f77
--- /dev/null
+++ b/src/lib/util/unittests/run_all.cc
@@ -0,0 +1,95 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdlib.h>
+
+#include <iostream>
+#include <iomanip>
+
+#include <gtest/gtest.h>
+#include <exceptions/exceptions.h>
+#include <util/unittests/run_all.h>
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+int
+run_all() {
+ int ret = 0;
+
+ // The catching of exceptions generated in tests is controlled by the
+ // B10TEST_CATCH_EXCEPTION environment variable. Setting this to
+ // 1 enables the cacthing of exceptions; setting it to 0 disables it.
+ // Anything else causes a message to be printed to stderr and the default
+ // taken. (The default is to catch exceptions if compiling with clang
+ // and false if not.)
+#ifdef __clang__
+ bool catch_exception = true;
+#else
+ bool catch_exception = false;
+#endif
+
+ const char* b10test_catch_exception = getenv("B10TEST_CATCH_EXCEPTION");
+ if (b10test_catch_exception != NULL) {
+ if (strcmp(b10test_catch_exception, "1") == 0) {
+ catch_exception = true;
+ } else if (strcmp(b10test_catch_exception, "0") == 0) {
+ catch_exception = false;
+ } else {
+ std::cerr << "***ERROR: B10TEST_CATCH_EXCEPTION is '"
+ << b10test_catch_exception
+ << "': allowed values are '1' or '0'.\n"
+ << " The default value of "
+ << (catch_exception ?
+ "1 (exception catching enabled)":
+ "0 (exception catching disabled)")
+ << " will be used.\n";
+ }
+ }
+
+ // Actually run the code
+ if (catch_exception) {
+ try {
+ ret = RUN_ALL_TESTS();
+ } catch (const isc::Exception& ex) {
+ // Could output more information with typeid(), but there is no
+ // guarantee that all compilers will support it without an explicit
+ // flag on the command line.
+ std::cerr << "*** Exception derived from isc::exception thrown:\n"
+ << " file: " << ex.getFile() << "\n"
+ << " line: " << ex.getLine() << "\n"
+ << " what: " << ex.what() << std::endl;
+ throw;
+ } catch (const std::exception& ex) {
+ std::cerr << "*** Exception derived from std::exception thrown:\n"
+ << " what: " << ex.what() << std::endl;
+ throw;
+ }
+ } else {
+ // This is a separate path for the case where the exception is not
+ // being caught. Although the other code path re-throws the exception
+ // after catching it, there is no guarantee that the state of the
+ // stack is preserved - a compiler might have unwound the stack to
+ // the point at which the exception is caught. This would prove
+ // awkward if trying to debug the program using a debugger.
+ ret = RUN_ALL_TESTS();
+ }
+
+ return (ret);
+}
+
+} // namespace unittests
+} // namespace util
+} // namespace isc
diff --git a/src/lib/util/unittests/run_all.h b/src/lib/util/unittests/run_all.h
new file mode 100644
index 0000000..94c7cb0
--- /dev/null
+++ b/src/lib/util/unittests/run_all.h
@@ -0,0 +1,52 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+
+
+#ifndef __RUN_ALL_H
+#define __RUN_ALL_H
+
+// Avoid need for user to include this header file.
+
+#include <gtest/gtest.h>
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+/// \brief Run All Tests
+///
+/// A wrapper for the Google Test RUN_ALL_TESTS() macro, this calls the macro
+/// but wraps the call in a try...catch block if the environment variable
+/// B10TEST_CATCH_EXCEPTION is defined, and calls the macro directly if not.
+///
+/// The catch block catches exceptions of types isc::Exception and
+/// std::exception and prints some information about them to stderr. (In the
+/// case of isc::Exception, this includes the file and line number from which
+/// the exception was raised.) It then re-throws the exception.
+///
+/// See: https://lists.isc.org/pipermail/bind10-dev/2011-January/001867.html
+/// for some context.
+///
+/// \return Return value from RUN_ALL_TESTS().
+
+int run_all();
+
+} // namespace unittests
+} // namespace util
+} // namespace isc
+
+
+
+#endif // __RUN_ALL_H
diff --git a/src/lib/util/unittests/testdata.cc b/src/lib/util/unittests/testdata.cc
new file mode 100644
index 0000000..2148d31
--- /dev/null
+++ b/src/lib/util/unittests/testdata.cc
@@ -0,0 +1,61 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+#include <stdexcept>
+#include <fstream>
+#include <vector>
+
+#include "testdata.h"
+
+using namespace std;
+
+namespace {
+vector<string>&
+getDataPaths() {
+ static vector<string> data_path;
+ return (data_path);
+}
+}
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+void
+addTestDataPath(const string& path) {
+ getDataPaths().push_back(path);
+}
+
+void
+openTestData(const char* const datafile, ifstream& ifs) {
+ vector<string>::const_iterator it = getDataPaths().begin();
+ for (; it != getDataPaths().end(); ++it) {
+ string data_path = *it;
+ if (data_path.empty() || *data_path.rbegin() != '/') {
+ data_path.push_back('/');
+ }
+ ifs.open((data_path + datafile).c_str(), ios_base::in);
+ if (!ifs.fail()) {
+ return;
+ }
+ }
+
+ throw runtime_error("failed to open data file in data paths: " +
+ string(datafile));
+}
+
+}
+}
+}
diff --git a/src/lib/util/unittests/testdata.h b/src/lib/util/unittests/testdata.h
new file mode 100644
index 0000000..03bd83a
--- /dev/null
+++ b/src/lib/util/unittests/testdata.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __UTIL_UNITTESTS_TESTDATA_H
+#define __UTIL_UNITTESTS_TESTDATA_H 1
+
+/**
+ * @file testdata.h
+ * @short Manipulating test data files.
+ *
+ * This utility defines functions that help test case handle test data
+ * stored in a file.
+ */
+
+namespace isc {
+namespace util {
+namespace unittests {
+/// Add a path (directory) that \c openTestData() will search for test data
+/// files.
+void addTestDataPath(const std::string& path);
+
+/// Open a file specified by 'datafile' using the data paths registered via
+/// addTestDataPath(). On success, ifs will be ready for reading the data
+/// stored in 'datafile'. If the data file cannot be open with any of the
+/// registered paths, a runtime_error exception will be thrown.
+///
+/// \note Care should be taken if you want to reuse the same single \c ifs
+/// for multiple input data. Some standard C++ library implementations retain
+/// the failure bit if the first stream reaches the end of the first file,
+/// and make the second call to \c ifstream::open() fail. The safest way
+/// is to use a different \c ifstream object for a new call to this function;
+/// alternatively make sure you explicitly clear the error bit by calling
+/// \c ifstream::clear() on \c ifs.
+void openTestData(const char* const datafile, std::ifstream& ifs);
+}
+}
+}
+
+#endif // __UTIL_UNITTESTS_TESTDATA_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/unittests/textdata.h b/src/lib/util/unittests/textdata.h
new file mode 100644
index 0000000..3e9b1aa
--- /dev/null
+++ b/src/lib/util/unittests/textdata.h
@@ -0,0 +1,103 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <istream>
+#include <string>
+#include <sstream>
+
+#include <gtest/gtest.h>
+
+#ifndef __UTIL_UNITTESTS_TEXTDATA_H
+#define __UTIL_UNITTESTS_TEXTDATA_H 1
+
+/**
+ * @file textdata.h
+ * @short Utilities for tests with text data.
+ *
+ * This utility provides convenient helper functions for unit tests using
+ * textual data.
+ */
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+/// Line-by-line text comparison.
+///
+/// This templated function takes two standard input streams, extracts
+/// strings from them, and compares the two sets of strings line by line.
+template <typename EXPECTED_STREAM, typename ACTUAL_STREAM>
+void
+matchTextData(EXPECTED_STREAM& expected, ACTUAL_STREAM& actual) {
+ std::string actual_line;
+ std::string expected_line;
+ while (std::getline(actual, actual_line), !actual.eof()) {
+ std::getline(expected, expected_line);
+ if (expected.eof()) {
+ FAIL() << "Redundant line in actual output: " << actual_line;
+ break;
+ }
+ if (actual.bad() || actual.fail() ||
+ expected.bad() || expected.fail()) {
+ throw std::runtime_error("Unexpected error in data streams");
+ }
+ EXPECT_EQ(expected_line, actual_line);
+ }
+ while (std::getline(expected, expected_line), !expected.eof()) {
+ ADD_FAILURE() << "Missing line in actual output: " << expected_line;
+ }
+}
+
+/// Similar to the fully templated version, but takes string for the second
+/// (actual) data.
+///
+/// Due to the nature of textual data, it will often be the case that test
+/// data is given as a string object. This shortcut version helps such cases
+/// so that the test code doesn't have to create a string stream with the
+/// string data just for testing.
+template <typename EXPECTED_STREAM>
+void
+matchTextData(EXPECTED_STREAM& expected, const std::string& actual_text) {
+ std::istringstream iss(actual_text);
+ matchTextData(expected, iss);
+}
+
+/// Same for the previous version, but the first argument is string.
+template <typename ACTUAL_STREAM>
+void
+matchTextData(const std::string& expected_text, ACTUAL_STREAM& actual) {
+ std::istringstream iss(expected_text);
+ matchTextData(iss, actual);
+}
+
+/// Same for the previous two, but takes strings for both expected and
+/// actual data.
+void
+matchTextData(const std::string& expected_text,
+ const std::string& actual_text)
+{
+ std::istringstream expected_is(expected_text);
+ std::istringstream actual_is(actual_text);
+ matchTextData(expected_is, actual_is);
+}
+
+}
+}
+}
+
+#endif // __UTIL_UNITTESTS_TEXTDATA_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/xfr/Makefile.am b/src/lib/xfr/Makefile.am
index 79cee73..d714990 100644
--- a/src/lib/xfr/Makefile.am
+++ b/src/lib/xfr/Makefile.am
@@ -2,7 +2,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CXXFLAGS = $(B10_CXXFLAGS) -Wno-strict-aliasing
+AM_CXXFLAGS = $(B10_CXXFLAGS)
AM_CXXFLAGS += -Wno-unused-parameter # see src/lib/cc/Makefile.am
if USE_CLANGPP
AM_CXXFLAGS += -Wno-error
@@ -11,13 +11,5 @@ endif
CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = libxfr.la
-libxfr_la_SOURCES = xfrout_client.h xfrout_client.cc
-libxfr_la_SOURCES += fd_share.h fd_share.cc
-
-pyexec_LTLIBRARIES = libxfr_python.la
-# Python prefers .so, while some OSes (specifically MacOS) use a different
-# suffix for dynamic objects. -module is necessary to work this around.
-libxfr_python_la_LDFLAGS = -module
-libxfr_python_la_SOURCES = fdshare_python.cc fd_share.cc fd_share.h
-libxfr_python_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
-libxfr_python_la_CXXFLAGS = $(AM_CXXFLAGS)
+libxfr_la_SOURCES = xfrout_client.h xfrout_client.cc
+libxfr_la_LIBADD = $(top_builddir)/src/lib/util/io/libutil_io.la
diff --git a/src/lib/xfr/fd_share.cc b/src/lib/xfr/fd_share.cc
deleted file mode 100644
index 4e1f093..0000000
--- a/src/lib/xfr/fd_share.cc
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <cstring>
-#include <cstdlib>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <stdlib.h> // for malloc and free
-#include <xfr/fd_share.h>
-
-namespace isc {
-namespace xfr {
-
-namespace {
-// Not all OSes support advanced CMSG macros: CMSG_LEN and CMSG_SPACE.
-// In order to ensure as much portability as possible, we provide wrapper
-// functions of these macros.
-// Note that cmsg_space() could run slow on OSes that do not have
-// CMSG_SPACE.
-inline socklen_t
-cmsg_len(const socklen_t len) {
-#ifdef CMSG_LEN
- return (CMSG_LEN(len));
-#else
- // Cast NULL so that any pointer arithmetic performed by CMSG_DATA
- // is correct.
- const uintptr_t hdrlen = (uintptr_t)CMSG_DATA(((struct cmsghdr*)NULL));
- return (hdrlen + len);
-#endif
-}
-
-inline socklen_t
-cmsg_space(const socklen_t len) {
-#ifdef CMSG_SPACE
- return (CMSG_SPACE(len));
-#else
- struct msghdr msg;
- struct cmsghdr* cmsgp;
- // XXX: The buffer length is an ad hoc value, but should be enough
- // in a practical sense.
- char dummybuf[sizeof(struct cmsghdr) + 1024];
-
- memset(&msg, 0, sizeof(msg));
- msg.msg_control = dummybuf;
- msg.msg_controllen = sizeof(dummybuf);
-
- cmsgp = (struct cmsghdr*)dummybuf;
- cmsgp->cmsg_len = cmsg_len(len);
-
- cmsgp = CMSG_NXTHDR(&msg, cmsgp);
- if (cmsgp != NULL) {
- return ((char*)cmsgp - (char*)msg.msg_control);
- } else {
- return (0);
- }
-#endif // CMSG_SPACE
-}
-}
-
-int
-recv_fd(const int sock) {
- struct msghdr msghdr;
- struct iovec iov_dummy;
- unsigned char dummy_data;
-
- iov_dummy.iov_base = &dummy_data;
- iov_dummy.iov_len = sizeof(dummy_data);
- msghdr.msg_name = NULL;
- msghdr.msg_namelen = 0;
- msghdr.msg_iov = &iov_dummy;
- msghdr.msg_iovlen = 1;
- msghdr.msg_flags = 0;
- msghdr.msg_controllen = cmsg_space(sizeof(int));
- msghdr.msg_control = malloc(msghdr.msg_controllen);
- if (msghdr.msg_control == NULL) {
- return (-1);
- }
-
- if (recvmsg(sock, &msghdr, 0) < 0) {
- free(msghdr.msg_control);
- return (XFR_FD_RECEIVE_FAIL);
- }
- const struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr);
- int fd = -1;
- if (cmsg != NULL && cmsg->cmsg_len == cmsg_len(sizeof(int)) &&
- cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
- fd = *(const int*)CMSG_DATA(cmsg);
- }
- free(msghdr.msg_control);
- return (fd);
-}
-
-int
-send_fd(const int sock, const int fd) {
- struct msghdr msghdr;
- struct iovec iov_dummy;
- unsigned char dummy_data = 0;
-
- iov_dummy.iov_base = &dummy_data;
- iov_dummy.iov_len = sizeof(dummy_data);
- msghdr.msg_name = NULL;
- msghdr.msg_namelen = 0;
- msghdr.msg_iov = &iov_dummy;
- msghdr.msg_iovlen = 1;
- msghdr.msg_flags = 0;
- msghdr.msg_controllen = cmsg_space(sizeof(int));
- msghdr.msg_control = malloc(msghdr.msg_controllen);
- if (msghdr.msg_control == NULL) {
- return (-1);
- }
-
- struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr);
- cmsg->cmsg_len = cmsg_len(sizeof(int));
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- *(int*)CMSG_DATA(cmsg) = fd;
-
- const int ret = sendmsg(sock, &msghdr, 0);
- free(msghdr.msg_control);
- return (ret >= 0 ? 0 : -1);
-}
-
-} // End for namespace xfr
-} // End for namespace isc
diff --git a/src/lib/xfr/fd_share.h b/src/lib/xfr/fd_share.h
deleted file mode 100644
index 4ee5fd5..0000000
--- a/src/lib/xfr/fd_share.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef FD_SHARE_H_
-#define FD_SHARE_H_
-
-namespace isc {
-namespace xfr {
-
-/// Failed to receive xfr socket descriptor "fd" on unix domain socket 'sock'
-const int XFR_FD_RECEIVE_FAIL = -2;
-
-// Receive socket descriptor on unix domain socket 'sock'.
-// Returned value is the socket descriptor received.
-// Returned XFR_FD_RECEIVE_FAIL if failed to receive xfr socket descriptor
-// Errors are indicated by a return value of -1.
-int recv_fd(const int sock);
-
-// Send socket descriptor "fd" to server over unix domain socket 'sock',
-// the connection from socket 'sock' to unix domain server should be established first.
-// Errors are indicated by a return value of -1.
-int send_fd(const int sock, const int fd);
-
-} // End for namespace xfr
-} // End for namespace isc
-
-#endif
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/xfr/fdshare_python.cc b/src/lib/xfr/fdshare_python.cc
deleted file mode 100644
index 82b1b6e..0000000
--- a/src/lib/xfr/fdshare_python.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#define PY_SSIZE_T_CLEAN
-#include <Python.h>
-#include <structmember.h>
-
-#include <config.h>
-
-#include <xfr/fd_share.h>
-
-
-static PyObject*
-fdshare_recv_fd(PyObject*, PyObject* args) {
- int sock, fd;
- if (!PyArg_ParseTuple(args, "i", &sock)) {
- return (NULL);
- }
- fd = isc::xfr::recv_fd(sock);
- return (Py_BuildValue("i", fd));
-}
-
-static PyObject*
-fdshare_send_fd(PyObject*, PyObject* args) {
- int sock, fd, result;
- if (!PyArg_ParseTuple(args, "ii", &sock, &fd)) {
- return (NULL);
- }
- result = isc::xfr::send_fd(sock, fd);
- return (Py_BuildValue("i", result));
-}
-
-static PyMethodDef fdshare_Methods[] = {
- {"send_fd", fdshare_send_fd, METH_VARARGS, ""},
- {"recv_fd", fdshare_recv_fd, METH_VARARGS, ""},
- {NULL, NULL, 0, NULL} /* Sentinel */
-};
-
-
-static PyModuleDef bind10_fdshare_python = {
- { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
- "bind10_fdshare",
- "Python bindings for fdshare",
- -1,
- fdshare_Methods,
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-PyMODINIT_FUNC
-PyInit_libxfr_python(void) {
- PyObject* mod = PyModule_Create(&bind10_fdshare_python);
- if (mod == NULL) {
- return (NULL);
- }
-
- PyObject* XFR_FD_RECEIVE_FAIL = Py_BuildValue("i", isc::xfr::XFR_FD_RECEIVE_FAIL);
- if (XFR_FD_RECEIVE_FAIL == NULL) {
- Py_XDECREF(mod);
- return (NULL);
- }
- int ret = PyModule_AddObject(mod, "XFR_FD_RECEIVE_FAIL", XFR_FD_RECEIVE_FAIL);
- if (-1 == ret) {
- Py_XDECREF(XFR_FD_RECEIVE_FAIL);
- Py_XDECREF(mod);
- return (NULL);
- }
-
- return (mod);
-}
-
diff --git a/src/lib/xfr/python_xfr.cc b/src/lib/xfr/python_xfr.cc
deleted file mode 100644
index 52848ad..0000000
--- a/src/lib/xfr/python_xfr.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <boost/python.hpp>
-#include <boost/python/class.hpp>
-#include <boost/python/module.hpp>
-#include <boost/python/def.hpp>
-#include <boost/python/exception_translator.hpp>
-#include <boost/python/return_internal_reference.hpp>
-#include <boost/python/copy_const_reference.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include <xfr/fd_share.h>
-
-using namespace isc::xfr;
-using namespace boost::python;
-
-BOOST_PYTHON_MODULE(bind10_xfr) {
- def("recv_fd", &recv_fd);
- def("send_fd", &send_fd);
-}
diff --git a/src/lib/xfr/xfrout_client.cc b/src/lib/xfr/xfrout_client.cc
index e9e736b..6ab905b 100644
--- a/src/lib/xfr/xfrout_client.cc
+++ b/src/lib/xfr/xfrout_client.cc
@@ -20,10 +20,11 @@
#include <unistd.h>
#include <asio.hpp>
-#include <xfr/fd_share.h>
+#include <util/io/fd_share.h>
#include <xfr/xfrout_client.h>
using namespace std;
+using namespace isc::util::io;
using asio::local::stream_protocol;
namespace isc {
@@ -72,7 +73,7 @@ XfroutClient::sendXfroutRequestInfo(const int tcp_sock,
const void* const msg_data,
const uint16_t msg_len)
{
- if (-1 == send_fd(impl_->socket_.native(), tcp_sock)) {
+ if (send_fd(impl_->socket_.native(), tcp_sock) < 0) {
isc_throw(XfroutError,
"Failed to send the socket file descriptor "
"to xfrout module");
diff --git a/tests/Makefile.am b/tests/Makefile.am
index d4008c0..570c0e2 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1 +1 @@
-SUBDIRS = system
+SUBDIRS = system tools
diff --git a/tests/system/bindctl/tests.sh b/tests/system/bindctl/tests.sh
index 354f349..6923c41 100755
--- a/tests/system/bindctl/tests.sh
+++ b/tests/system/bindctl/tests.sh
@@ -31,7 +31,7 @@ grep 192.0.2.1 dig.out.$n > /dev/null || status=1
if [ $status != 0 ]; then echo "I:failed"; fi
n=`expr $n + 1`
-echo "I:Checking BIND 10 statistics after a pose ($n)"
+echo "I:Checking BIND 10 statistics after a pause ($n)"
# wait for 2sec to make sure b10-stats gets the latest statistics.
# note that we set statistics-interval to 1.
sleep 2
@@ -67,7 +67,7 @@ grep 192.0.2.1 dig.out.$n > /dev/null || status=1
if [ $status != 0 ]; then echo "I:failed"; fi
n=`expr $n + 1`
-echo "I:Rechecking BIND 10 statistics after a pose ($n)"
+echo "I:Rechecking BIND 10 statistics after a pause ($n)"
sleep 2
echo 'Stats show
' | $RUN_BINDCTL \
diff --git a/tests/tools/Makefile.am b/tests/tools/Makefile.am
new file mode 100644
index 0000000..2f07494
--- /dev/null
+++ b/tests/tools/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = badpacket
diff --git a/tests/tools/badpacket/Makefile.am b/tests/tools/badpacket/Makefile.am
new file mode 100644
index 0000000..fcba404
--- /dev/null
+++ b/tests/tools/badpacket/Makefile.am
@@ -0,0 +1,33 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+noinst_PROGRAMS = badpacket
+badpacket_SOURCES = badpacket.cc
+badpacket_SOURCES += command_options.cc command_options.h
+badpacket_SOURCES += header_flags.h
+badpacket_SOURCES += option_info.cc option_info.h
+badpacket_SOURCES += scan.cc scan.h
+badpacket_SOURCES += version.h
+
+badpacket_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_CLANGPP
+badpacket_CXXFLAGS += -Wno-error
+endif
+
+badpacket_LDADD = $(top_builddir)/src/lib/asiodns/libasiodns.la
+badpacket_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+badpacket_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+badpacket_LDADD += $(top_builddir)/src/lib/log/liblog.la
+badpacket_LDADD += $(top_builddir)/src/lib/util/libutil.la
+badpacket_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
diff --git a/tests/tools/badpacket/README b/tests/tools/badpacket/README
new file mode 100644
index 0000000..3f5e801
--- /dev/null
+++ b/tests/tools/badpacket/README
@@ -0,0 +1,53 @@
+"badpacket" is a tool intended to test that a nameserver can cope with
+incorrectly-formatted DNS messages.
+
+This particular incarnation of the tool allows various fields in the DNS
+message to be set to any value (including to values that invalidate the query).
+As well as setting individual values, it is possible to specify ranges; when
+this is done, the tool will send a set of packets so that each combination
+of values is checked.
+
+To illustrate this, consider the following command:
+
+badpacket --address 192.0.2.21 --port 5301 --aa 0-1 --cd 1
+ --rc 0-2 ftp.example.com
+
+(The command has been split across two lines for clarity.)
+
+The address and port flags are self-evident. The other flags specify
+settings for the AA bit (0 and 1), CD bit (always 1) and the RCODE field
+(0, 1, 2). (The remaining fields in the flags word are not specified, so
+will always be zero.) There are six combinations of these values, so six
+packets will sent to the remote server with the following settings:
+
+AA RCODE CD Rest
+0 0 1 0
+0 1 1 0
+0 2 1 0
+1 0 1 0
+1 1 1 0
+1 2 1 0
+
+Each packet will cause a line to be output to stdout, which will have the
+following form:
+
+SUCCESS: (QR:0 OP:0 AA:0 TC:0 RD:0 RA:0 ...
+ (qr:1 op:0 aa:0 tc:0 rd:0 ra:1 ...
+
+(Again the text has been split across two lines for clarity. Also, the full
+list of fields has been truncated.)
+
+Each lines contains a status (SUCCESS indicates that a response was received,
+regardless of the contents of the response), the state of the fields in the
+flags word of the packet sent (in upper-case letters) and the state of the
+fields in the flags word of the response (in lower-case letters).
+
+TODO: At the moment the tool is limited to:
+* Setting values in the flags field.
+* Setting section counts to arbitrary values.
+* Extending or truncating the DNS message size.
+
+Additional options needed are:
+* Add data to sections that should be empty.
+* Deliberately mangle the names placed in the message sections (e.g. by altering
+ the label count fields).
diff --git a/tests/tools/badpacket/badpacket.cc b/tests/tools/badpacket/badpacket.cc
new file mode 100644
index 0000000..86bbc47
--- /dev/null
+++ b/tests/tools/badpacket/badpacket.cc
@@ -0,0 +1,62 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <unistd.h>
+#include <iostream>
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include "command_options.h"
+#include "scan.h"
+
+/// \brief Perform Bad Packet Scan
+///
+/// Scans the server by sending a sequence of (potentially) bad packets and
+/// printing the packet settings and the response received. The principle
+/// raison d'etre for this is to check if a bad packet will cause a crash.
+///
+/// This particular version of the code allows a set of ranges to be set for:
+/// - The flags fields in the message.
+/// - The section count fields, regardless of what is in the section.
+/// - The message size: the message can be truncated or extended.
+///
+/// The program then sends a set of packets corresponding to all combinations
+/// of values in the ranges selected.
+
+// TODO: Extend to cover:
+// - Mangling the QNAME
+// - Adding names to the message sections
+// - Adding EDNS0 RR and magling the fields there
+
+using namespace isc::badpacket;
+
+/// \brief Main Program
+int main(int argc, char* argv[]) {
+
+ try {
+ // Parse command
+ CommandOptions options;
+ options.parse(argc, argv);
+
+ // Send the sequence of messages
+ Scan scanner;
+ scanner.scan(options);
+ } catch (isc::Exception& e) {
+ std::cout << "ERROR: " << e.what() << "\n";
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/tests/tools/badpacket/command_options.cc b/tests/tools/badpacket/command_options.cc
new file mode 100644
index 0000000..eea5ff0
--- /dev/null
+++ b/tests/tools/badpacket/command_options.cc
@@ -0,0 +1,333 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <algorithm>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <boost/lexical_cast.hpp>
+#include <getopt.h>
+
+#include "exceptions/exceptions.h"
+#include "util/strutil.h"
+
+#include "command_options.h"
+#include "option_info.h"
+#include "version.h"
+
+using namespace std;
+using namespace isc;
+
+namespace isc {
+namespace badpacket {
+
+// Reset stored values to the defaults.
+void
+CommandOptions::reset() {
+ address_ = "127.0.0.1";
+ port_ = 53;
+ timeout_ = 500;
+ qname_ = "www.example.com";
+
+ for (int i = 0; i < OptionInfo::SIZE; ++i) {
+ options_[i].minimum = OptionInfo::defval(i);
+ options_[i].maximum = OptionInfo::defval(i);
+ options_[i].present = false;
+ }
+}
+
+/// Parses the command-line options and records the results.
+void
+CommandOptions::parse(int argc, char* const argv[]) {
+
+ // Set up options for processing. The declaration of the string constants
+ // as mutable arrays and putting the string variable into the "longopts"
+ // structure (as opposed to just putting a string literal there) is
+ // occasioned by a version of solaris which declares the first field as
+ // "char*" (instead of the correct "const char*").
+
+ // General options.
+ char HELP[] = {"help"};
+ char VERSION[] = {"version"};
+ char ADDRESS[] = {"address"};
+ char PORT[] = {"port"};
+ char TIMEOUT[] = {"timeout"};
+
+ // Settings for options in the flags field.
+ char QR[] = {"qr"};
+ char OP[] = {"op"};
+ char AA[] = {"aa"};
+ char TC[] = {"tc"};
+ char RD[] = {"rd"};
+ char RA[] = {"ra"};
+ char Z[] = {"z"};
+ char AD[] = {"ad"};
+ char CD[] = {"cd"};
+ char RC[] = {"rc"};
+
+ // Settings for the count fields
+ char QC[] = {"qc"};
+ char AC[] = {"ac"};
+ char UC[] = {"uc"};
+ char DC[] = {"dc"};
+
+ // Message size
+ char MS[] = {"ms"};
+
+ const struct option longopts[] = {
+ {HELP, 0, NULL, 'h'}, // Print usage message and exit
+ {VERSION, 0, NULL, 'v'}, // Print program version and exit
+ {ADDRESS, 1, NULL, 'a'}, // Specify target server address
+ {PORT, 1, NULL, 'p'}, // Specify target port
+ {TIMEOUT, 1, NULL, 't'}, // Time to wait before timing out (ms)
+ {QR, 1, NULL, 'Q'}, // Query/response flag
+ {OP, 1, NULL, 'O'}, // Opcode
+ {AA, 1, NULL, 'A'}, // Authoritative answer
+ {TC, 1, NULL, 'T'}, // Truncated
+ {RD, 1, NULL, 'D'}, // recursion Desired
+ {RA, 1, NULL, 'R'}, // Recursion available
+ {Z, 1, NULL, 'Z'}, // must be Zero (reserved bit)
+ {AD, 1, NULL, 'U'}, // aUthenticated data
+ {CD, 1, NULL, 'C'}, // Checking disabled
+ {RC, 1, NULL, 'E'}, // rEsult code
+ {QC, 1, NULL, 'Y'}, // querY section count
+ {AC, 1, NULL, 'W'}, // ansWer section count
+ {UC, 1, NULL, 'H'}, // autHority section count
+ {DC, 1, NULL, 'I'}, // addItional section count
+ {MS, 1, NULL, 'M'}, // Message size
+ {NULL, 0, NULL, 0 }
+ };
+ const char* shortopts = "hva:p:t:Q:O:A:T:D:R:Z:U:C:E:Y:W:H:I:M:";
+
+
+ // Set record of options to defaults before parsing
+ reset();
+
+ // Process command line
+ int c; // Option being processed
+ optind = 0; // Reset parsing
+ while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
+ switch (c) {
+ case 'h': // --help
+ usage();
+ exit(0);
+
+ case 'v': // --version
+ version();
+ exit(0);
+
+ case 'a': // --address
+ address_ = optarg;
+ break;
+
+ case 'p': // --port
+ port_ = boost::lexical_cast<uint16_t>(string(optarg));
+ break;
+
+ case 't': // --timeout
+ timeout_ = boost::lexical_cast<int>(string(optarg));
+ break;
+
+ case 'Q': // --qr (query/response)
+ case 'O': // --op (operation code)
+ case 'A': // --aa (authoritative answer)
+ case 'T': // --tc (truncated)
+ case 'D': // --rd (recursion desired)
+ case 'R': // --ra (recursion available)
+ case 'Z': // --z (zero: reserved bit)
+ case 'U': // --ad (authenticated data)
+ case 'C': // --cd (checking disabled)
+ case 'E': // --rc (result code)
+ case 'Y': // --qc (query count)
+ case 'W': // --ac (answer count)
+ case 'H': // --uc (authority count)
+ case 'I': // --dc (additional count)
+ case 'M': // --ms (message size)
+ processOptionValue(c, optarg);
+ break;
+
+ default:
+ isc_throw(isc::InvalidParameter,
+ "unknown option given on the command line");
+ }
+ }
+
+ // Pick up a parameter if there is one (and report excess).
+ if (optind < argc) {
+ qname_ = argv[optind++];
+ }
+
+ if (optind < argc) {
+ isc_throw(isc::InvalidParameter,
+ "only a single (optional) parameter may be specified on the command line");
+ }
+}
+
+// Print usage information.
+void
+CommandOptions::usage() {
+ cout << "Usage: badpacket [options] query\n"
+ "\n"
+ "Sends a sequence of DNS messages to the specified nameserver and prints the\n"
+ " results. The packets are valid query packets but certain aspects of the\n"
+ " packets (such as the flags fields, section count fields and message size) can\n"
+ "be set to arbitrary values using the command-line switches.\n"
+ "\n"
+ "In the following list of command-line switches, '<range>' indicates a range of\n"
+ "values specified as either <integer> or <integer1>-<integer2> (e.g. both '42'\n"
+ "and '0-1' would be valid values for range). The program sends a sequence of\n"
+ "messages that contain all combinations of the flag values. For example,\n"
+ "specifying:\n"
+ "\n"
+ "--tc 0-1 --op 1-4 --aa 1 --rd 0-1\n"
+ "\n"
+ "... would send a total of 16 packets which would have all combinations of the\n"
+ "the tc bit set to 0 and 1, the rd bit set to 0 and 1, and the opcode set to all\n"
+ "values between 1 and 4. All other flags fields would be zero except for the aa\n"
+ "bit which would always be 1.\n"
+ "\n"
+ "The long form of the option is given. It can also be specified as a single-\n"
+ "character short-form, which is listed in sqare brackets in the description.\n"
+ "\n"
+ "General options are:\n"
+ "\n"
+ "--help [-h] Prints this message and exits.\n"
+ "--version [-v] Prints the program version number.\n"
+ "--address <address> [-a] Address of nameserver, which defaults to 127.0.0.1\n"
+ "--port <port> [-p] Port to which to send query. Defaults to 53.\n"
+ "--timeout <value> [-t] Timeout value for the query. Specified in ms, it\n"
+ " defaults to 500ms.\n"
+ "\n"
+ "The following options set fields in the outgoing DNS message flags word:\n"
+ "\n"
+ "--qr <range> [-Q] Set query/response bit. Valid <range> is 0-1.\n"
+ "--op <range> [-O] Set opcode. Valid <range> is 0-15.\n"
+ "--aa <range> [-A] Set authoritative answer bit. Valid <range> is 0-1.\n"
+ "--tc <range> [-T] Set truncated bit. Valid <range> is 0-1.\n"
+ "--rd <range> [-D] Set recursion desired bit. Valid <range> is 0-1.\n"
+ "--ra <range> [-D] Set recursion available bit. Valid <range> is 0-1.\n"
+ "--z <range> [-Z] Set zero (reserved) bit. Valid <range> is 0-1.\n"
+ "--ad <range> [-U] Set authenticated data bit. Valid <range> is 0-1.\n"
+ "--cd <range> [-C] Set checking disabled bit. Valid <range> is 0-1.\n"
+ "--rc <range> [-E] Set rcode value. Valid <range> is 0-15\n"
+ "\n"
+ "The following options set the various section counts (independent of what is\n"
+ "actually in the section):\n"
+ "\n"
+ "--qc <range> [-Y] Set the query count. Valid range is 0-65535.\n"
+ "--ac <range> [-W] Set the answer count. Valid range is 0-65535.\n"
+ "--uc <range> [-H] Set the authority count. Valid range is 0-65535.\n"
+ "--dc <range> [-I] Set the additional count. Valid range is 0-65535.\n"
+ "\n"
+ "Other options are:\n"
+ "\n"
+ "--ms <range> [-M] Set the size of the message. If the specified size\n"
+ " smaller than the natural message size, it is truncated.\n"
+ " If longer, the packet is extended with random values.\n"
+ " Valid range is 2 to 65536\n"
+ "\n"
+ "query Name to query for. The query is for an 'IN' A record. If\n"
+ " not given, the name 'www.example.com' is used.\n"
+ "\n"
+ "The output is a single (very long) line containing the settings of the various\n"
+ "fields. The settings for the outgoing packet are reported in uppercase letters\n"
+ "and that of the returned packet in lowercase.\n"
+ ;
+}
+
+// Print version information,
+void
+CommandOptions::version() {
+ cout << BADPACKET_VERSION << "\n";
+}
+
+// Process single flag that can be stored in the options_ member.
+void
+CommandOptions::processOptionValue(int c, const char* value) {
+
+ // Get values for this option.
+ int index = OptionInfo::getIndex(c);
+ const char* name = OptionInfo::name(index);
+ uint32_t minval = OptionInfo::minval(index);
+ uint32_t maxval = OptionInfo::maxval(index);
+
+ // Split the string up into one or two tokens.
+ vector<string> tokens = isc::util::str::tokens(string(value), "-");
+ if ((tokens.size() < 1) || (tokens.size() > 2)) {
+ isc_throw(isc::BadValue, "value given for " << name << " is '" << value <<
+ "': it must be in the form 'int' or 'int1-int2'");
+ }
+
+ // Convert to uint32.
+ try {
+ options_[index].minimum = boost::lexical_cast<uint32_t>(tokens[0]);
+ if (tokens.size() == 2) {
+ options_[index].maximum = boost::lexical_cast<uint32_t>(tokens[1]);
+ } else {
+ options_[index].maximum = options_[index].minimum;
+ }
+ } catch (boost::bad_lexical_cast) {
+ isc_throw(isc::BadValue, "value given for " << name << " is '" << value <<
+ "': it must be in the form 'int' or 'int1-int2'");
+ }
+
+ // Set the limits in the correct order.
+ if (options_[index].minimum > options_[index].maximum) {
+ swap(options_[index].minimum, options_[index].maximum);
+ }
+
+ // Check that tokens lie inside the allowed ranges.
+ if ((tokens.size() == 1) &&
+ ((options_[index].minimum < OptionInfo::minval(index)) || (options_[index].maximum > maxval))) {
+ isc_throw(isc::BadValue, "the value of " << options_[index].minimum <<
+ " given for " << name << " is outside the range of " <<
+ minval << " to " << maxval);
+ } else if (options_[index].minimum < minval) {
+ isc_throw(isc::BadValue, "the lower limit of " << options_[index].minimum <<
+ " given for " << name << " is below the minimum permitted"
+ " value of " << minval);
+ } else if (options_[index].maximum > maxval) {
+ isc_throw(isc::BadValue, "the upper limit of " << options_[index].maximum <<
+ " given for " << name << " is above the maximum permitted"
+ " value of " << maxval);
+ }
+
+ // And finally note that the option was specified on the command line
+ options_[index].present = true;
+}
+
+// Return information about the option - minimum and maximum values and whether
+// it was actually specified. (Note that if it wasn't, the maximum and minimum
+// values will have been set to the default recorded in the OptionInfo class.)
+uint32_t
+CommandOptions::minimum(int index) const {
+ OptionInfo::checkIndex(index);
+ return (options_[index].minimum);
+}
+
+uint32_t
+CommandOptions::maximum(int index) const {
+ OptionInfo::checkIndex(index);
+ return (options_[index].maximum);
+}
+
+bool
+CommandOptions::present(int index) const {
+ OptionInfo::checkIndex(index);
+ return (options_[index].present);
+}
+
+} // namespace badpacket
+} // namespace isc
diff --git a/tests/tools/badpacket/command_options.h b/tests/tools/badpacket/command_options.h
new file mode 100644
index 0000000..fc819e9
--- /dev/null
+++ b/tests/tools/badpacket/command_options.h
@@ -0,0 +1,162 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __COMMAND_OPTIONS_H
+#define __COMMAND_OPTIONS_H
+
+#include <cstdlib>
+#include <stdint.h>
+#include <utility>
+
+#include "option_info.h"
+
+namespace isc {
+namespace badpacket {
+
+/// \brief Command Options
+///
+/// This class is responsible for parsing the command-line and storing the
+/// specified options.
+///
+/// Some of the options perform general control (like setting the address of the
+/// nameserver under test, while the rest set values in the DNS message being
+/// sent. Each of the latter options can be specified as either:
+///
+/// - \c --option value
+/// - \c --option value1-value2
+///
+/// Either way, two values are extracted the low value and the high value (in
+/// the former case, both are the same). The values are stored and can be
+/// returned on request.
+///
+/// For simplicity, the class takes care of the --help and --version flags,
+/// each of which will cause a message to be printed to stdout and the program
+/// to terminate.
+
+class CommandOptions {
+public:
+
+ /// \brief Default Constructor
+ ///
+ /// Set values to defaults.
+ CommandOptions() {
+ reset();
+ }
+
+ /// \brief Return minimum value for option
+ ///
+ /// Applicable only to an option affecting a field in the message, this
+ /// method returns the minimum value that was given on the command line.
+ /// (If only a single value was given, it will be that value returned.)
+ /// If the option was not specified on the command line, the default value
+ /// set in the OptionsInfo class will be returned.
+ ///
+ /// \param index Index of the command-line option.
+ ///
+ /// \return uint32_t holding the minimum value given (or the default if
+ /// the option was not specified on the command line).
+ uint32_t minimum(int index) const;
+
+ /// \brief Return maximum value for option
+ ///
+ /// Applicable only to an option affecting a field in the message, this
+ /// method returns the maximum value that was given on the command line.
+ /// (If only a single value was given, it will be that value returned.)
+ /// If the option was not specified on the command line, the default value
+ /// set in the OptionsInfo class will be returned.
+ ///
+ /// \param index Index of the command-line option.
+ ///
+ /// \return uint32_t holding the maximum value given (or the default if
+ /// the option was not specified on the command line).
+ uint32_t maximum(int index) const;
+
+ /// \brief Reports if option was given on command line
+ ///
+ /// \param index Index of the command-line option.
+ ///
+ /// \return true if the option was present, false if not.
+ bool present(int index) const;
+
+
+ /// \brief Return target address
+ std::string getAddress() const {
+ return address_;
+ }
+
+ /// \brief Return target port
+ uint16_t getPort() const {
+ return port_;
+ }
+
+ /// \brief Return timeout
+ int getTimeout() const {
+ return timeout_;
+ }
+
+ /// \brief Return qname
+ std::string getQname() const {
+ return qname_;
+ }
+
+ /// \brief Reset to defaults
+ ///
+ /// Resets the CommandOptions object to default values.
+ void reset();
+
+ /// \brief Parse command line
+ ///
+ /// Parses the command line and stores the selected options. The parsing
+ /// also handles the --help and --version commands: both of these will cause
+ /// some text to be printed to stdout, after which exit() is called to
+ /// terminate the program.
+ ///
+ /// \param argc Argument count passed to main().
+ /// \param argv Argument value array passed to main().
+ void parse(int argc, char* const argv[]);
+
+ /// \brief Print usage information and exit program
+ void usage();
+
+ /// \brief Print version information and exit program
+ void version();
+
+private:
+ /// \brief Process Option Value
+ ///
+ /// Processes a specific command-line option, interpreting the value and
+ /// placing it in the appropriate location. On error a BadValue exception
+ /// is thrown.
+ ///
+ /// \param c Short form option character from the command line
+ /// \param value Value of the option read from the command line
+ void processOptionValue(int c, const char* value);
+
+ // Member variables
+
+ struct {
+ uint32_t minimum; ///< Minimum value specified
+ uint32_t maximum; ///< Maximum value specified
+ bool present; ///< true if specified on command line
+ } options_[OptionInfo::SIZE]; ///< Information about command options
+ std::string address_; ///< Address to where query is sent
+ uint16_t port_; ///< Target port
+ int timeout_; ///< Timeout for query
+ std::string qname_; ///< Query to make
+};
+
+} // namespace badpacket
+} // namespace isc
+
+#endif // __COMMAND_OPTIONS_H
diff --git a/tests/tools/badpacket/header_flags.h b/tests/tools/badpacket/header_flags.h
new file mode 100644
index 0000000..5d74e75
--- /dev/null
+++ b/tests/tools/badpacket/header_flags.h
@@ -0,0 +1,102 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __HEADER_FLAGS_H
+#define __HEADER_FLAGS_H
+
+#include <exceptions/exceptions.h>
+#include "option_info.h"
+
+namespace isc {
+namespace badpacket {
+
+/// \brief Header Flags
+///
+/// Simple class providing easy conversion between the header flags in a DNS
+/// message and a 16-bit value.
+
+class HeaderFlags {
+public:
+
+ /// \brief Constructor
+ HeaderFlags() {
+ reset();
+ }
+
+ /// \brief Reset values to zero
+ ///
+ /// Clears all flags.
+ void reset() {
+ setValue(0);
+ }
+
+ /// \brief Get header flags as 16-bit value
+ uint16_t getValue() const {
+ return (flags_);
+ }
+
+ /// \brief Set header flags as 16-bit value
+ ///
+ /// \param value 16-bit value to put into object as representing the
+ /// header flags.
+ void setValue(uint16_t value) {
+ flags_ = value;
+ }
+
+ /// \brief Get field
+ ///
+ /// Return the value of a bit field in the flags word.
+ ///
+ /// \param int Index of the bit field in the OptionInfo data structure
+ ///
+ /// \return Value of the field.
+ uint16_t get(int index) const {
+ OptionInfo::checkIndex(index);
+ return ((flags_ & OptionInfo::mask(index)) >> OptionInfo::offset(index));
+ }
+
+ /// \brief Set field
+ ///
+ /// Sets the value of a bit field.
+ ///
+ /// \param int Index of the bit field in the OptionInfo data structure
+ /// \param value Value to set. If the value is more than the field can
+ /// hold, a BadValue exception is thrown.
+ void set(int index, uint16_t value) {
+
+ // Declare an OptionInfo object for brevity and check the index is
+ // valid.
+ OptionInfo o;
+ o.checkIndex(index);
+
+ // Ensure the value is within limits and throw an exception if not. (This
+ // should not really be needed, as the command line parsing should have
+ // checked the limits. But be safe.)
+ if ((value < o.minval(index)) || (value > o.maxval(index))) {
+ isc_throw(isc::BadValue, "value of index " << index << " is out of range");
+ }
+
+ // Clear the field then set it with the value.
+ flags_ &= ~o.mask(index);
+ flags_ |= (value << o.offset(index));
+ }
+
+private:
+ uint16_t flags_; ///< Variable holding field values
+};
+
+} // namespace badpacket
+} // namespace isc
+
+#endif // __HEADER_FLAGS_H
diff --git a/tests/tools/badpacket/option_info.cc b/tests/tools/badpacket/option_info.cc
new file mode 100644
index 0000000..e1c1f78
--- /dev/null
+++ b/tests/tools/badpacket/option_info.cc
@@ -0,0 +1,114 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "option_info.h"
+
+namespace {
+
+// Define the various options for the command switches. This includes both the
+// long form and short form of the switch. Unfortunately this means that the
+// information is duplicated here and where the long options are specified for
+// getopt_long, but this inconvenience is outweighed by the simplified command
+// processing.
+//
+// Fields are:
+// * Short option name
+// * Long option name
+// * Offset of 16-bit word holding datum in DNS message (if applicable)
+// * Bit mask for the data (if applicable)
+// * Offset of the bit field in the word (if applicable)
+// * Default value (this can be ignored if applicable)
+// * Minimum value specified on command line
+// * Maximum value specified on command line
+
+isc::badpacket::OptionInfo::Parameter option_information[] = {
+ {'Q', "qr", 2, 0x8000, 15, 0, 0, 1},
+ {'O', "op", 2, 0x7800, 11, 0, 0, 15},
+ {'A', "aa", 2, 0x0400, 10, 0, 0, 1},
+ {'T', "tc", 2, 0x0200, 9, 0, 0, 1},
+ {'D', "rd", 2, 0x0100, 8, 0, 0, 1},
+ {'R', "ra", 2, 0x0080, 7, 0, 0, 1},
+ {'Z', "z", 2, 0x0040, 6, 0, 0, 1},
+ {'U', "ad", 2, 0x0020, 5, 0, 0, 1},
+ {'C', "cd", 2, 0x0010, 4, 0, 0, 1},
+ {'E', "rc", 2, 0x000F, 0, 0, 0, 15},
+ {'Y', "qc", 4, 0, 0, 1, 0, 0xFFFF},
+ {'W', "ac", 6, 0, 0, 0, 0, 0xFFFF},
+ {'H', "uc", 8, 0, 0, 0, 0, 0xFFFF},
+ {'I', "dc", 10, 0, 0, 0, 0, 0xFFFF},
+ {'M', "ms", 0, 0, 0, 0, 1, 65536}
+};
+
+} // Anonymous namespace
+
+namespace isc {
+namespace badpacket {
+
+// Locate the index of the information in the array from the short switch.
+int
+OptionInfo::getIndex(int c) {
+ for (int i = 0; i < SIZE; ++i) {
+ if (option_information[i].short_form == c) {
+ return (i);
+ }
+ }
+ isc_throw(isc::BadValue, "unknown option: " << c);
+}
+
+// Methods to return values from the array
+
+const char*
+OptionInfo::name(int i) {
+ checkIndex(i);
+ return (option_information[i].long_form);
+}
+
+uint16_t
+OptionInfo::mask(int i) {
+ checkIndex(i);
+ return (option_information[i].mask);
+}
+
+int
+OptionInfo::word(int i) {
+ checkIndex(i);
+ return (option_information[i].word);
+}
+
+int
+OptionInfo::offset(int i) {
+ checkIndex(i);
+ return (option_information[i].offset);
+}
+
+uint32_t
+OptionInfo::minval(int i) {
+ checkIndex(i);
+ return (option_information[i].minval);
+}
+
+uint32_t
+OptionInfo::defval(int i) {
+ checkIndex(i);
+ return (option_information[i].defval);
+}
+
+uint32_t
+OptionInfo::maxval(int i) {
+ checkIndex(i);
+ return (option_information[i].maxval);
+}
+
+} // namespace badpacket
+} // namespace isc
diff --git a/tests/tools/badpacket/option_info.h b/tests/tools/badpacket/option_info.h
new file mode 100644
index 0000000..a340c1d
--- /dev/null
+++ b/tests/tools/badpacket/option_info.h
@@ -0,0 +1,174 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __OPTION_INFO_H
+#define __OPTION_INFO_H
+
+#include <stdint.h>
+#include "exceptions/exceptions.h"
+
+namespace isc {
+namespace badpacket {
+
+/// \brief Option Information
+///
+/// Holds details about the options that can be specified on the command line
+/// that require values and which control data put in the DNS message sent to
+/// the remote system.
+///
+/// Some of the fields are no applicable to all options. For example, some of
+/// the options correspond to fields in the flags word of the DNS message
+/// header, so the information includes details about the position of the fields
+/// and an appropriate bit mask.
+///
+/// Note that the class does not hold values specified on the command line - it
+/// only holds information about the available options.
+
+class OptionInfo {
+public:
+
+ /// \brief Array Indexes
+ ///
+ /// The data for the flags options are held in an array. Although declared
+ /// as an enum, only the numeric values are used as they are the indexes
+ /// into the array.
+ enum Index {
+ FLAGS_START = 0, // Start of flags field codes
+ QR = 0, // Query/response
+ OP = 1, // Opcode
+ AA = 2, // Authoritative answer
+ TC = 3, // Truncated
+ RD = 4, // Recursion desired
+ RA = 5, // Recursion available
+ Z = 6, // Zero (reserved)
+ AD = 7, // Authenticated data
+ CD = 8, // Checking disabled
+ RC = 9, // Response code
+ FLAGS_END = 9, // End of flags field codes
+ COUNT_START = 10, // Start of count fields
+ QC = 10, // Query count
+ AC = 11, // Answer count
+ UC = 12, // Authority count
+ DC = 13, // Additional count
+ COUNT_END = 13, // End of count fields
+ OTHER_START = 14, // Start of other fields
+ MS = 14, // Message size
+ OTHER_END = 14, // End of other fields
+ SIZE = 15 // Number of index values
+ };
+
+ /// \brief Option parameters
+ ///
+ /// Defines a structure that holds information associated with each of the
+ /// flags field command options. Not all members of the structure are
+ /// relevant to all options.
+ struct Parameter {
+ const char short_form; // Short form of the command switch
+ const char* long_form; // Long form of the command switch
+ int word; // Byte offset of word in message header
+ uint16_t mask; // Bit mask of field in the flags word
+ int offset; // Offset of field in flags word
+ uint32_t defval; // Default value
+ uint32_t minval; // Minimum valid value for this field
+ uint32_t maxval; // Maximum valid value for this field
+ };
+
+ /// \brief Return index for command option
+ ///
+ /// Given the short form of a command-line option, return the index in the
+ /// options array corresponding to that option.
+ ///
+ /// \param c The character that is the short form of the command line option.
+ /// An 'int' is used as the value passed will be the return value
+ /// from 'getopt()' (or equivalent) which is an int. If the
+ /// character is not found, an exception will be thrown.
+ ///
+ /// \return A valid index value.
+ static int getIndex(int c);
+
+ /// \brief Return long form of command switch for this field
+ ///
+ /// \param index A valid index (one of the values in the 'Index' enum).
+ ///
+ /// \return The long option name (e.q. "qr" for the Query/Response field).
+ static const char* name(int index);
+
+ /// \brief Return header word offset
+ ///
+ /// Returns the byte offset in the DNS message header of the two-byte word
+ /// holding the data in question.
+ ///
+ /// \param index A valid index (one of the values in the 'Index' enum).
+ ///
+ /// \return The offset in the header for this datum.
+ static int word(int index);
+
+ /// \brief Return mask associated with switch field
+ ///
+ /// \param index A valid index (one of the values in the 'Index' enum).
+ ///
+ /// \return The mask for this particular option in the DNS message flags
+ /// word. The returned value is only valid for options that
+ /// correspond to fields in the flags word.
+ static uint16_t mask(int index);
+
+ /// \brief Return offset associated with option field
+ ///
+ /// \param index A valid index (one of the values in the 'Index' enum).
+ ///
+ /// \return The offset of the field corresponding to this option in the DNS
+ /// message flags field. The returned value is only valid for
+ /// options that correpond to fields in the flags word.
+ static int offset(int index);
+
+ /// \brief Return minimum allowed value of an option
+ ///
+ /// \param index A valid index (one of the values in the 'Index' enum).
+ ///
+ /// \return Minimum allowed value for this option.
+ static uint32_t minval(int index);
+
+ /// \brief Return default value of an option
+ ///
+ /// \param index A valid index (one of the values in the 'Index' enum).
+ ///
+ /// \return Default value for this option
+ static uint32_t defval(int index);
+
+ /// \brief Return maximum allowed value of an option
+ ///
+ /// \param index A valid index (one of the values in the 'Index' enum).
+ ///
+ /// \return Maximum allowed value for this option. If the option is a bit
+ /// in the flags field of the DNS message hearder, this will be 1.
+ static uint32_t maxval(int index);
+
+ /// \brief Check Array Index
+ ///
+ /// Checks the passed field index and throws an exception if it does not
+ /// correspond to one of the valid indexes in the 'Index' enum.
+ ///
+ /// \param index An index value.
+ static void checkIndex(int i) {
+ if ((i < 0) || (i >= SIZE)) {
+ isc_throw(isc::OutOfRange, "option index must be in the range "
+ "0 to " << SIZE);
+ }
+ }
+};
+
+} // namespace badpacket
+} // namespace isc
+
+#endif // __OPTION_INFO_H
diff --git a/tests/tools/badpacket/scan.cc b/tests/tools/badpacket/scan.cc
new file mode 100644
index 0000000..a6e7229
--- /dev/null
+++ b/tests/tools/badpacket/scan.cc
@@ -0,0 +1,312 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <string>
+
+#include <stdlib.h>
+
+#include <config.h>
+
+#include <asio.hpp>
+
+#include <asiolink/io_address.h>
+#include <asiodns/io_fetch.h>
+#include <util/buffer.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/question.h>
+#include <dns/rcode.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <util/strutil.h>
+
+#include "command_options.h"
+#include "header_flags.h"
+#include "scan.h"
+
+using namespace std;
+using namespace isc::asiolink;
+using namespace isc::asiodns;
+using namespace isc::dns;
+using namespace isc::util;
+using namespace isc::util::str;
+
+namespace isc {
+namespace badpacket {
+
+// Perform the scan from start to end on a single question.
+void
+Scan::scan(const CommandOptions& options) {
+
+ // Create the message buffer to use
+ Message message(Message::RENDER);
+ message.setOpcode(Opcode::QUERY());
+ message.setRcode(Rcode::NOERROR());
+ message.addQuestion(Question(Name(options.getQname()), RRClass::IN(),
+ RRType::A()));
+
+ OutputBufferPtr msgbuf(new OutputBuffer(512));
+ MessageRenderer renderer(*msgbuf);
+ message.toWire(renderer);
+
+ iterateFlagsStart(msgbuf, options);
+}
+
+// Iterate through the various settings in the flags fields.
+void
+Scan::iterateFlagsStart(OutputBufferPtr& msgbuf, const CommandOptions& options) {
+ HeaderFlags flags;
+ iterateFlags(msgbuf, options, flags, OptionInfo::FLAGS_START,
+ OptionInfo::FLAGS_END);
+}
+void
+Scan::iterateFlags(OutputBufferPtr& msgbuf, const CommandOptions& options,
+ HeaderFlags& flags, int index, int maxindex)
+{
+ // Is the index valid?
+ if (index <= maxindex) {
+
+ // Index is valid, did the command line specify a range of values for
+ // this field?
+ if (options.present(index)) {
+
+ // It did, so loop between minimum and maximum values given.
+ for (uint32_t i = options.minimum(index);
+ i <= options.maximum(index); ++i) {
+ flags.set(index, i);
+
+ // Recurse to set the next option.
+ iterateFlags(msgbuf, options, flags, (index + 1), maxindex);
+ }
+ } else {
+
+ // Not specified on command line, so set the default value in the
+ // flags and process the next index.
+ flags.set(index, OptionInfo::defval(index));
+ iterateFlags(msgbuf, options, flags, (index + 1), maxindex);
+ }
+ } else {
+
+ // Index is not valid, so we have recursed enough to process all the
+ // flags fields. Set the value in the message header and call the next
+ // stage in the processing.
+ //
+ // (In the next statement, the "word" offset of in the header is the
+ // same for all flags options, so the value for an arbitrary field
+ // (QR) has been used.)
+ msgbuf->writeUint16At(flags.getValue(),
+ OptionInfo::word(OptionInfo::QR));
+ iterateCountStart(msgbuf, options);
+ }
+}
+
+// Iterate through the various count fields
+void
+Scan::iterateCountStart(OutputBufferPtr& msgbuf, const CommandOptions& options)
+{
+ iterateCount(msgbuf, options, OptionInfo::COUNT_START,
+ OptionInfo::COUNT_END);
+}
+
+void
+Scan::iterateCount(OutputBufferPtr& msgbuf, const CommandOptions& options,
+ int index, int maxindex)
+{
+ // If the index is valid, process the iteration over the range for this
+ // flags field.
+ if (index <= maxindex) {
+
+ // Index is valid, did the command line specify a range of values for
+ // this field?
+ if (options.present(index)) {
+
+ // It did, so loop between minimum and maximum values given.
+ for (uint32_t i = options.minimum(index);
+ i <= options.maximum(index); ++i) {
+
+ // Set the value in the message buffer header and recurse to
+ // the next option.
+ msgbuf->writeUint16At(i, OptionInfo::word(index));
+ iterateCount(msgbuf, options, (index + 1), maxindex);
+ }
+ } else {
+
+ // Not specified on command line, so do change anything and process
+ // the next index.
+ iterateCount(msgbuf, options, (index + 1), maxindex);
+ }
+ } else {
+
+ // Index is not valid, so we have recursed enough to process all the
+ // flags fields. Do the next stage of the processing.
+ sizeMessage(msgbuf, options);
+ }
+}
+
+// Alter the message size.
+void
+Scan::sizeMessage(OutputBufferPtr& msgbuf, const CommandOptions& options) {
+
+ if (options.present(OptionInfo::MS)) {
+
+ // Iterate over the range of message sizes
+ for (size_t i = options.minimum(OptionInfo::MS);
+ i <= options.maximum(OptionInfo::MS); ++i) {
+
+ // Copy the data into a new buffer.
+ OutputBufferPtr newbuf(new OutputBuffer(i));
+ newbuf->writeData(msgbuf->getData(), min(msgbuf->getLength(), i));
+
+ // Pad with junk (actually pseudo-random data) if the new buffer is
+ // longer than the old.
+ for (size_t j = newbuf->getLength(); j < i; ++j) {
+ newbuf->writeUint8(static_cast<uint8_t>(rand() & 0xFFU));
+ }
+
+ // ... and process.
+ scanOne(newbuf, options);
+ }
+ } else {
+
+ // No packet size given, just process the message as it.
+ scanOne(msgbuf, options);
+ }
+}
+
+// Perform the message exchange for a single combination command options.
+void
+Scan::scanOne(isc::util::OutputBufferPtr& msgbuf, const CommandOptions& options) {
+ // Store the interpretation of outgoing message.
+ string fields = string("(") + getFields(msgbuf) + string(")");
+
+ // Do the I/O, waiting for a reply
+ OutputBufferPtr replybuf(new OutputBuffer(512));
+ performIO(msgbuf, replybuf, options);
+
+ string status = "";
+ string returned = "";
+ switch (result_) {
+ case IOFetch::SUCCESS:
+ {
+ status = "SUCCESS";
+
+ // Parse the reply and get the fields
+ returned = string("(") + getFields(replybuf) + string(")");
+ lowercase(returned);
+ }
+ break;
+
+ case IOFetch::TIME_OUT:
+ status = "TIMEOUT";
+ break;
+
+ case IOFetch::STOPPED:
+ status = "STOPPED";
+ break;
+
+ default:
+ status = "UNKNOWN";
+ }
+
+ // ... and output the result
+ cout << status << ": " << fields << " " << returned << "\n";
+}
+
+// Get interpretation of the message fields.
+std::string
+Scan::getFields(isc::util::OutputBufferPtr& msgbuf) {
+
+ // Header flags. (Note that all come from the same word in the message, so
+ // using the word offset for the QR flag as the position in the buffer from
+ // which to extract the values is valid.)
+ HeaderFlags flags;
+ InputBuffer inbuf(msgbuf->getData(), msgbuf->getLength());
+ inbuf.setPosition(OptionInfo::word(OptionInfo::QR));
+ flags.setValue(inbuf.readUint16());
+
+ std::ostringstream os;
+ os << std::hex << std::uppercase <<
+ "QR:" << flags.get(OptionInfo::QR) <<
+ " OP:" << flags.get(OptionInfo::OP) <<
+ " AA:" << flags.get(OptionInfo::AA) <<
+ " TC:" << flags.get(OptionInfo::TC) <<
+ " RD:" << flags.get(OptionInfo::RD) <<
+ " RA:" << flags.get(OptionInfo::RA) <<
+ " Z:" << flags.get(OptionInfo::Z) <<
+ " AD:" << flags.get(OptionInfo::AD) <<
+ " CD:" << flags.get(OptionInfo::CD) <<
+ " RC:" << flags.get(OptionInfo::RC);
+
+ // Section count fields.
+ inbuf.setPosition(OptionInfo::word(OptionInfo::QC));
+ os << std::dec << std::uppercase <<
+ " QC:" << inbuf.readUint16();
+
+ inbuf.setPosition(OptionInfo::word(OptionInfo::AC));
+ os << std::dec << std::uppercase <<
+ " AC:" << inbuf.readUint16();
+
+ inbuf.setPosition(OptionInfo::word(OptionInfo::UC));
+ os << std::dec << std::uppercase <<
+ " UC:" << inbuf.readUint16();
+
+ inbuf.setPosition(OptionInfo::word(OptionInfo::DC));
+ os << std::dec << std::uppercase <<
+ " DC:" << inbuf.readUint16();
+
+ // ... and message size.
+ os << std::dec << std::uppercase <<
+ " MS:" << msgbuf->getLength();
+
+ return (os.str());
+}
+
+// Perform the I/O to the nameserver.
+void
+Scan::performIO(OutputBufferPtr& sendbuf, OutputBufferPtr& recvbuf,
+ const CommandOptions& options)
+{
+ // Set up an I/O service for the I/O. This needs to be initialized before
+ // every call as the callback called when the I/O completes stops it.
+ service_.reset(new IOService());
+
+ // The object that will do the I/O
+ IOFetch fetch(IOFetch::UDP, *service_, sendbuf,
+ IOAddress(options.getAddress()), options.getPort(), recvbuf,
+ this, options.getTimeout());
+
+ // Execute the message exchange. The call to run() will return when a
+ // response is received or when the I/O times out.
+ (service_->get_io_service()).post(fetch);
+ service_->run();
+}
+
+// I/O Callback. Called when the message exchange completes or times out.
+void
+Scan::operator()(IOFetch::Result result) {
+
+ // Record the result. This is accessed when deciding what was returned
+ // (if a timeout, nothing was returned).
+ result_ = result;
+
+ // Stop the I/O service. This will cause the call to run() to return.
+ service_->stop();
+}
+
+} // namespace test
+} // namespace isc
diff --git a/tests/tools/badpacket/scan.h b/tests/tools/badpacket/scan.h
new file mode 100644
index 0000000..ca56646
--- /dev/null
+++ b/tests/tools/badpacket/scan.h
@@ -0,0 +1,198 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __SCAN_H
+#define __SCAN_H
+
+#include <stdint.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <config.h>
+
+#include <asiolink/io_service.h>
+#include <asiodns/io_fetch.h>
+#include <util/buffer.h>
+
+#include "command_options.h"
+#include "header_flags.h"
+
+namespace isc {
+namespace badpacket {
+
+/// \brief Field Scan
+///
+/// This class implements a field scan. Given a CommandOptions object, it will
+/// cycle through combinations of the given options, sending and receiving
+/// messages. For each packet exchange, a summary is written to stdout.
+
+class Scan : public isc::asiodns::IOFetch::Callback {
+public:
+
+ /// \brief Constructor
+ Scan() : service_(), result_()
+ {}
+
+ /// \brief Run Scan
+ ///
+ /// Actually performs the scan for the combination of options.
+ ///
+ /// \param options Command-line options
+ void scan(const CommandOptions& options);
+
+ /// \brief Callback
+ ///
+ /// This class is derived from the IOFetch::Callback class as it acts as the
+ /// callback object. When an asynchronous I/I has completed, this method
+ /// will be called.
+ ///
+ /// \param result Result of the asynchronous I/O. Zero implies success.
+ virtual void operator()(isc::asiodns::IOFetch::Result result);
+
+private:
+ /// \brief Iterate over flags fields options
+ ///
+ /// This method relies on the fact that data concerning the settings for
+ /// the fields in the flags word of the DNS message are held at adjacent
+ /// elements in the various data arrays and so can be accessed by a set
+ /// of contiguous index values.
+ ///
+ /// The method is passed an index value and the maximum valid index value.
+ /// If a set of values for the field at the given index was specified on
+ /// the command line, it loops through those values and sets the appropriate
+ /// value in the a copy of the DNS message header flags. It then calls
+ /// itself with an incremented index. If the value was not given, it just
+ /// sets a default value calls itself (with the incremented index). When
+ /// called with an index above the maximum valid index, the header flags
+ /// in the message buffer are set and the next stage of processing called.
+ ///
+ /// In this way, all fields can be cycled through without the need for a
+ /// single function to nest loops very deeply.
+ ///
+ /// \param msgbuf Message that will be sent to the remote nameserver. The
+ /// QID given will be ignored - the value used will be determined by
+ /// the sending code
+ /// \param options Command-line options (the important ones being address,
+ /// port and timeout).
+ /// \param flags Header flags
+ /// \param index Index of the current field to be processed.
+ /// \param maxindex Maximum valid index value
+ void iterateFlags(isc::util::OutputBufferPtr& msgbuf,
+ const CommandOptions& options, HeaderFlags& flags,
+ int index, int maxindex);
+
+ /// \brief Start iterating over flags field options
+ ///
+ /// Kicks off the call to \c iterateFlags by calling it with the initial
+ /// index value.
+ ///
+ /// \param msgbuf Message that will be sent to the remote nameserver. The
+ /// QID given will be ignored - the value used will be determined by
+ /// the sending code
+ /// \param options Command-line options (the important ones being address,
+ /// port and timeout).
+ void iterateFlagsStart(isc::util::OutputBufferPtr& msgbuf,
+ const CommandOptions& options);
+
+ /// \brief Iterate over count fields
+ ///
+ /// In a manner similar to iterateFlags, this iterates over all specified
+ /// values for each count field, recursively calling itself to process the
+ /// next field. When all fields have been processed, it chains to the
+ /// next stage of packet processing.
+ ///
+ /// \param msgbuf Message that will be sent to the remote nameserver. The
+ /// QID given will be ignored - the value used will be determined by
+ /// the sending code
+ /// \param options Command-line options (the important ones being address,
+ /// port and timeout).
+ /// \param index Index of the current field to be processed.
+ /// \param maxindex Maximum valid index value
+ void iterateCount(isc::util::OutputBufferPtr& msgbuf,
+ const CommandOptions& options, int index, int maxindex);
+
+ /// \brief Start iterating over count fields
+ ///
+ /// Kicks off the call to \c iterateCount by calling it with the initial
+ /// index value.
+ ///
+ /// \param msgbuf Message that will be sent to the remote nameserver. The
+ /// QID given will be ignored - the value used will be determined by
+ /// the sending code
+ /// \param options Command-line options (the important ones being address,
+ /// port and timeout).
+ void iterateCountStart(isc::util::OutputBufferPtr& msgbuf,
+ const CommandOptions& options);
+
+ /// \brief Iterate over message sizes
+ ///
+ /// If the message size option is given on the command line, the message
+ /// sent to the remote system is either truncated or extended (with zeroes)
+ /// before being set.
+ ///
+ /// \param msgbuf Message that will be sent to the remote nameserver. The
+ /// QID given will be ignored - the value used will be determined by
+ /// the sending code
+ /// \param options Command-line options (the important ones being address,
+ /// port and timeout).
+ void sizeMessage(isc::util::OutputBufferPtr& msgbuf,
+ const CommandOptions& options);
+
+ /// \brief Scan One Value
+ ///
+ /// Performs one exchange of messages with the remote nameserver, sending
+ /// the specified message.
+ ///
+ /// \param msgbuf Message that will be sent to the remote nameserver. The
+ /// QID given will be ignored - the value used will be determined by
+ /// the sending code
+ /// \param options Command-line options (the important ones being address,
+ /// port and timeout).
+ void scanOne(isc::util::OutputBufferPtr& msgbuf,
+ const CommandOptions& options);
+
+ /// \brief Perform I/O
+ ///
+ /// Performs a single query to the nameserver and reads the response. It
+ /// outputs a summary of the result.
+ ///
+ /// \param sendbuf Buffer sent to the nameserver
+ /// \param recvbuf Buffer to hold reply from the nameserver
+ /// \param options Command-line options
+ void performIO(isc::util::OutputBufferPtr& sendbuf,
+ isc::util::OutputBufferPtr& recvbuf,
+ const CommandOptions& options);
+
+ /// \brief Get Fields
+ ///
+ /// Interprets the fields in a DNS message and converts them to a brief
+ /// textual format.
+ ///
+ /// \param msg Message for which the header is value
+ ///
+ /// \return A string that holds a textual interpretation of all the fields
+ /// in the header.
+ std::string getFields(isc::util::OutputBufferPtr& msgbuf);
+
+ // Member variables
+
+ boost::scoped_ptr<isc::asiolink::IOService> service_;
+ ///< Service to run the scan
+ isc::asiodns::IOFetch::Result result_; ///< Result of the I/O
+};
+
+} // namespace test
+} // namespace isc
+
+#endif // __SCAN_H
diff --git a/tests/tools/badpacket/tests/Makefile.am b/tests/tools/badpacket/tests/Makefile.am
new file mode 100644
index 0000000..2daa664
--- /dev/null
+++ b/tests/tools/badpacket/tests/Makefile.am
@@ -0,0 +1,32 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += command_options_unittest.cc
+run_unittests_SOURCES += option_info_unittest.cc
+run_unittests_SOURCES += header_flags_unittest.cc
+run_unittests_SOURCES += $(top_builddir)/tests/tools/badpacket/command_options.cc
+run_unittests_SOURCES += $(top_builddir)/tests/tools/badpacket/option_info.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/tests/tools/badpacket/tests/command_options_unittest.cc b/tests/tools/badpacket/tests/command_options_unittest.cc
new file mode 100644
index 0000000..014618e
--- /dev/null
+++ b/tests/tools/badpacket/tests/command_options_unittest.cc
@@ -0,0 +1,300 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cstddef>
+#include <string>
+#include <gtest/gtest.h>
+
+#include "../command_options.h"
+
+#include "exceptions/exceptions.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::badpacket;
+
+
+/// \brief Test Fixture Class
+
+class CommandOptionsTest : public virtual ::testing::Test,
+ public virtual CommandOptions
+{
+public:
+
+ /// \brief Default Constructor
+ CommandOptionsTest()
+ {}
+
+ /// \brief Check Non-Limit Options
+ ///
+ /// Checks that the options that are NOT related to the message are set to
+ /// their default values.
+ void checkDefaultOtherValues() {
+ EXPECT_EQ("127.0.0.1", getAddress());
+ EXPECT_EQ(53, getPort());
+ EXPECT_EQ(500, getTimeout());
+ EXPECT_EQ("www.example.com", getQname());
+ }
+
+ /// \brief Checks the minimum and maximum value specified for an option
+ ///
+ /// Checks the values for one of the options whose values are stored in the
+ /// class's options_ array.
+ ///
+ /// \param index Index of the option in the limits_ array
+ /// \param minval Expected minimum value
+ /// \param maxval Expected maximum value
+ void checkValuePair(int index, uint32_t minval = 0, uint32_t maxval = 0) {
+ EXPECT_EQ(minimum(index), minval);
+ EXPECT_EQ(maximum(index), maxval);
+ }
+
+ /// \brief Checks that all options are at default values
+ ///
+ /// Checks that all options have both their maximum and minimum set to the
+ /// default values.
+ ///
+ /// \param except Index not to check. (This allows options not being tested
+ /// to be checked to see that they are at the default value.) As all
+ /// index values are positive, a negative value means check
+ /// everything.
+ void checkDefaultLimitsValues(int except = -1) {
+ for (int i = 0; i < OptionInfo::SIZE; ++i) {
+ if (i != except) {
+ checkValuePair(i, OptionInfo::defval(i),
+ OptionInfo::defval(i));
+ }
+ }
+ }
+
+ /// \brief Check valid command option
+ ///
+ /// Checks that the command line specification of one of the options taking
+ /// a value correctly processes the option.
+ ///
+ /// \param index Option index
+ /// \param optflag Option flag (in the form '--option')
+ /// \param optval Value to be passed to the option.
+ /// \param minval Expected minimum value
+ /// \param maxval Expected maximum value
+ void checkCommandValid(int index, const char* optflag, const char* optval,
+ uint32_t minval, uint32_t maxval) {
+
+ // Set up the command line and parse it.
+ const char* argv[] = {"badpacket", NULL, NULL};
+ argv[1] = optflag;
+ argv[2] = optval;
+ int argc = 3;
+ parse(argc, const_cast<char**>(argv));
+
+ // Check the results. Everything should be at the defaults except for
+ // the specified option, where the minimum and maximum should be as
+ // specified.
+ checkDefaultOtherValues();
+ checkDefaultLimitsValues(index);
+ checkValuePair(index, minval, maxval);
+ }
+
+ /// \brief Check invalid command option
+ ///
+ /// Passed a command with an invalid value, checks that the parsing throws
+ /// a BadValue exception.
+ ///
+ /// \param optflag Option flag (in the form '--option')
+ /// \param optval Value to be passed to the option.
+ void checkCommandInvalid(const char* optflag, const char* optval) {
+
+ // Set up the command line and parse it.
+ const char* argv[] = {"badpacket", NULL, NULL};
+ argv[1] = optflag;
+ argv[2] = optval;
+ int argc = 3;
+ EXPECT_THROW(parse(argc, const_cast<char**>(argv)), isc::BadValue);
+ }
+
+ /// \brief Check one-bit field
+ ///
+ /// Explicitly for those fields in the flags word that are one bit wide,
+ /// perform a series of tests to check that they accept valid values and
+ /// reject invalid ones.
+ ///
+ /// \param index Option index
+ /// \param optflag Option flag (in the form '--option')
+ void checkOneBitField(int index, const char* optflag) {
+ checkCommandValid(index, optflag, "0", 0, 0);
+ checkCommandValid(index, optflag, "1", 1, 1);
+ checkCommandValid(index, optflag, "0-1", 0, 1);
+ checkCommandValid(index, optflag, "1-0", 0, 1);
+ checkCommandInvalid(optflag, "0-3");
+ checkCommandInvalid(optflag, "4");
+ checkCommandInvalid(optflag, "xyz");
+ }
+
+ /// \brief Check four-bit field
+ ///
+ /// Explicitly for those fields in the flags word that are four bits wide,
+ /// perform a series of tests to check that they accept valid values and
+ /// reject invalid ones.
+ ///
+ /// \param index Option index
+ /// \param optflag Option flag (in the form '--option')
+ void checkFourBitField(int index, const char* optflag) {
+ checkCommandValid(index, optflag, "0", 0, 0);
+ checkCommandValid(index, optflag, "15", 15, 15);
+ checkCommandValid(index, optflag, "0-15", 0, 15);
+ checkCommandValid(index, optflag, "15-0", 0, 15);
+ checkCommandInvalid(optflag, "0-17");
+ checkCommandInvalid(optflag, "24");
+ checkCommandInvalid(optflag, "xyz");
+ }
+
+ /// \brief Check sixteen-bit field
+ ///
+ /// Explicitly test the parsing of the fields that can take a 16-bit
+ /// value ranging from 0 to 65535.
+ ///
+ /// \param index Option index
+ /// \param optflag Option flag (in the form '--option')
+ void checkSixteenBitField(int index, const char* optflag) {
+ checkCommandValid(index, optflag, "0", 0, 0);
+ checkCommandValid(index, optflag, "65535", 65535, 65535);
+ checkCommandValid(index, optflag, "0-65535", 0, 65535);
+ checkCommandValid(index, optflag, "65535-0", 0, 65535);
+ checkCommandInvalid(optflag, "0-65536");
+ checkCommandInvalid(optflag, "65537");
+ checkCommandInvalid(optflag, "xyz");
+ }
+};
+
+// Check that each of the non-message options will be recognised
+
+TEST_F(CommandOptionsTest, address) {
+ const char* argv[] = {"badpacket", "--address", "192.0.2.1"};
+ int argc = sizeof(argv) / sizeof(const char*);
+
+ // The conversion is ugly but it simplifies the process of entering the
+ // string constant. The cast throws away the "const"ness of the pointed-to
+ // strings in order to conform to the function signature; however, the
+ // called functions all treat the strings as const.
+ parse(argc, const_cast<char**>(argv));
+ EXPECT_EQ("192.0.2.1", getAddress());
+ EXPECT_EQ(53, getPort());
+ EXPECT_EQ(500, getTimeout());
+ EXPECT_EQ("www.example.com", getQname());
+ checkDefaultLimitsValues();
+}
+
+TEST_F(CommandOptionsTest, port) {
+ const char* argv[] = {"badpacket", "--port", "153"};
+ int argc = sizeof(argv) / sizeof(const char*);
+
+ parse(argc, const_cast<char**>(argv));
+ EXPECT_EQ("127.0.0.1", getAddress());
+ EXPECT_EQ(153, getPort());
+ EXPECT_EQ(500, getTimeout());
+ EXPECT_EQ("www.example.com", getQname());
+ checkDefaultLimitsValues();
+}
+
+TEST_F(CommandOptionsTest, timeout) {
+ const char* argv[] = {"badpacket", "--timeout", "250"};
+ int argc = sizeof(argv) / sizeof(const char*);
+
+ parse(argc, const_cast<char**>(argv));
+ EXPECT_EQ("127.0.0.1", getAddress());
+ EXPECT_EQ(53, getPort());
+ EXPECT_EQ(250, getTimeout());
+ EXPECT_EQ("www.example.com", getQname());
+ checkDefaultLimitsValues();
+}
+
+TEST_F(CommandOptionsTest, parameter) {
+ const char* argv[] = {"badpacket", "ftp.example.net"};
+ int argc = sizeof(argv) / sizeof(const char*);
+
+ parse(argc, const_cast<char**>(argv));
+ EXPECT_EQ("127.0.0.1", getAddress());
+ EXPECT_EQ(53, getPort());
+ EXPECT_EQ(500, getTimeout());
+ EXPECT_EQ("ftp.example.net", getQname());
+ checkDefaultLimitsValues();
+}
+
+// Test options representing the flags fields.
+
+TEST_F(CommandOptionsTest, qr) {
+ checkOneBitField(OptionInfo::QR, "--qr");
+}
+
+TEST_F(CommandOptionsTest, op) {
+ checkFourBitField(OptionInfo::OP, "--op");
+}
+
+TEST_F(CommandOptionsTest, aa) {
+ checkOneBitField(OptionInfo::AA, "--aa");
+}
+
+TEST_F(CommandOptionsTest, tc) {
+ checkOneBitField(OptionInfo::TC, "--tc");
+}
+
+TEST_F(CommandOptionsTest, z) {
+ checkOneBitField(OptionInfo::Z, "--z");
+}
+
+TEST_F(CommandOptionsTest, ad) {
+ checkOneBitField(OptionInfo::AD, "--ad");
+}
+
+TEST_F(CommandOptionsTest, cd) {
+ checkOneBitField(OptionInfo::CD, "--cd");
+}
+
+TEST_F(CommandOptionsTest, rc) {
+ checkFourBitField(OptionInfo::RC, "--rc");
+}
+
+// Section count options
+
+TEST_F(CommandOptionsTest, qc) {
+ checkSixteenBitField(OptionInfo::QC, "--qc");
+}
+
+TEST_F(CommandOptionsTest, ac) {
+ checkSixteenBitField(OptionInfo::AC, "--ac");
+}
+
+TEST_F(CommandOptionsTest, uc) {
+ checkSixteenBitField(OptionInfo::UC, "--uc");
+}
+
+TEST_F(CommandOptionsTest, dc) {
+ checkSixteenBitField(OptionInfo::DC, "--dc");
+}
+
+// ... and the message size option
+
+TEST_F(CommandOptionsTest, ms) {
+ int index = OptionInfo::MS;
+ const char* optflag = "--ms";
+
+ checkCommandValid(index, optflag, "1", 1, 1);
+ checkCommandValid(index, optflag, "65536", 65536, 65536);
+ checkCommandValid(index, optflag, "1-65536", 1, 65536);
+ checkCommandValid(index, optflag, "65536-1", 1, 65536);
+ checkCommandInvalid(optflag, "0");
+ checkCommandInvalid(optflag, "1-65537");
+ checkCommandInvalid(optflag, "65538");
+ checkCommandInvalid(optflag, "xyz");
+}
diff --git a/tests/tools/badpacket/tests/header_flags_unittest.cc b/tests/tools/badpacket/tests/header_flags_unittest.cc
new file mode 100644
index 0000000..5a7a722
--- /dev/null
+++ b/tests/tools/badpacket/tests/header_flags_unittest.cc
@@ -0,0 +1,141 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cstddef>
+#include <stdint.h>
+#include <gtest/gtest.h>
+
+#include "../option_info.h"
+#include "../header_flags.h"
+
+using namespace isc::badpacket;
+
+// Test fixture class
+
+class HeaderFlagsTest : public ::testing::Test {
+public:
+ HeaderFlagsTest() {}
+
+
+ /// \brief Checks that all options are zero
+ ///
+ /// Checks that all flags options are set to zero.
+ ///
+ /// \param flags Flags structure to check.
+ /// \param except Index not to check. (This allows options not being tested
+ /// to be checked to see that they are at the default value.) As all
+ /// index values are positive, a negative value means check
+ /// everything.
+ void checkZero(const HeaderFlags& flags, int except = -1) {
+
+ // Check individual fields
+ for (int i = OptionInfo::FLAGS_START; i < OptionInfo::FLAGS_END; ++i) {
+ if (i != except) {
+ EXPECT_EQ(0, flags.get(i));
+ }
+ }
+
+ // ... and check the composite
+ if (except == -1) {
+ EXPECT_EQ(0, flags.getValue());
+ } else {
+ EXPECT_NE(0, flags.getValue());
+ }
+ }
+
+ /// \brief Check Option
+ ///
+ /// Checks that an option will only set the appropriate bits in the flags
+ /// field.
+ ///
+ /// \param index Index of the flags field to check.
+ /// \param maxval Maximum value of the header field.
+ void checkOption(int index, uint32_t maxval) {
+
+ // Create header flags and check initialized properly.
+ HeaderFlags flags;
+ checkZero(flags);
+
+ // Check we can set field to maximum.
+ flags.set(index, maxval);
+ EXPECT_EQ(maxval, flags.get(index));
+ checkZero(flags, index);
+
+ // Check we can reset it to zero.
+ flags.set(index, 0);
+ checkZero(flags);
+ }
+};
+
+// Set of tests to check that setting a bit only sets that bit and nothing
+// else.
+
+TEST_F(HeaderFlagsTest, fields) {
+ checkOption(OptionInfo::QR, 1);
+ checkOption(OptionInfo::OP, 15);
+ checkOption(OptionInfo::AA, 1);
+ checkOption(OptionInfo::TC, 1);
+ checkOption(OptionInfo::RD, 1);
+ checkOption(OptionInfo::RA, 1);
+ checkOption(OptionInfo::Z, 1);
+ checkOption(OptionInfo::AD, 1);
+ checkOption(OptionInfo::CD, 1);
+ checkOption(OptionInfo::RC, 15);
+}
+
+// Check that the correct bits are set
+
+TEST_F(HeaderFlagsTest, bitValues) {
+ HeaderFlags flags;
+ checkZero(flags);
+
+ flags.set(OptionInfo::QR, 1);
+ EXPECT_EQ(0x8000, flags.getValue());
+
+ flags.set(OptionInfo::QR, 0);
+ flags.set(OptionInfo::OP, 15);
+ EXPECT_EQ(0x7800, flags.getValue());
+
+ flags.set(OptionInfo::OP, 0);
+ flags.set(OptionInfo::AA, 1);
+ EXPECT_EQ(0x0400, flags.getValue());
+
+ flags.set(OptionInfo::AA, 0);
+ flags.set(OptionInfo::TC, 1);
+ EXPECT_EQ(0x0200, flags.getValue());
+
+ flags.set(OptionInfo::TC, 0);
+ flags.set(OptionInfo::RD, 1);
+ EXPECT_EQ(0x0100, flags.getValue());
+
+ flags.set(OptionInfo::RD, 0);
+ flags.set(OptionInfo::RA, 1);
+ EXPECT_EQ(0x0080, flags.getValue());
+
+ flags.set(OptionInfo::RA, 0);
+ flags.set(OptionInfo::Z, 1);
+ EXPECT_EQ(0x0040, flags.getValue());
+
+ flags.set(OptionInfo::Z, 0);
+ flags.set(OptionInfo::AD, 1);
+ EXPECT_EQ(0x0020, flags.getValue());
+
+ flags.set(OptionInfo::AD, 0);
+ flags.set(OptionInfo::CD, 1);
+ EXPECT_EQ(0x0010, flags.getValue());
+
+ flags.set(OptionInfo::CD, 0);
+ flags.set(OptionInfo::RC, 15);
+ EXPECT_EQ(0x000F, flags.getValue());
+}
diff --git a/tests/tools/badpacket/tests/option_info_unittest.cc b/tests/tools/badpacket/tests/option_info_unittest.cc
new file mode 100644
index 0000000..8c061de
--- /dev/null
+++ b/tests/tools/badpacket/tests/option_info_unittest.cc
@@ -0,0 +1,161 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cstddef>
+#include <stdint.h>
+#include <gtest/gtest.h>
+
+#include "../option_info.h"
+
+using namespace isc::badpacket;
+
+
+// Test fixture class
+
+class OptionInfoTest : public ::testing::Test {
+public:
+ OptionInfoTest() {}
+};
+
+
+// Check the values are as expected
+
+TEST_F(OptionInfoTest, FlagValues) {
+ EXPECT_STREQ("qr", OptionInfo::name(OptionInfo::QR));
+ EXPECT_STREQ("qr", OptionInfo::name(OptionInfo::getIndex('Q')));
+ EXPECT_EQ(2, OptionInfo::word(OptionInfo::QR));
+ EXPECT_EQ(0x8000, OptionInfo::mask(OptionInfo::QR));
+ EXPECT_EQ(15, OptionInfo::offset(OptionInfo::QR));
+ EXPECT_EQ(0, OptionInfo::minval(OptionInfo::QR));
+ EXPECT_EQ(0, OptionInfo::defval(OptionInfo::QR));
+ EXPECT_EQ(1, OptionInfo::maxval(OptionInfo::QR));
+
+ EXPECT_STREQ("op", OptionInfo::name(OptionInfo::OP));
+ EXPECT_STREQ("op", OptionInfo::name(OptionInfo::getIndex('O')));
+ EXPECT_EQ(2, OptionInfo::word(OptionInfo::OP));
+ EXPECT_EQ(0x7800, OptionInfo::mask(OptionInfo::OP));
+ EXPECT_EQ(11, OptionInfo::offset(OptionInfo::OP));
+ EXPECT_EQ(0, OptionInfo::minval(OptionInfo::OP));
+ EXPECT_EQ(0, OptionInfo::defval(OptionInfo::OP));
+ EXPECT_EQ(15, OptionInfo::maxval(OptionInfo::OP));
+
+ EXPECT_STREQ("aa", OptionInfo::name(OptionInfo::AA));
+ EXPECT_STREQ("aa", OptionInfo::name(OptionInfo::getIndex('A')));
+ EXPECT_EQ(2, OptionInfo::word(OptionInfo::AA));
+ EXPECT_EQ(0x0400, OptionInfo::mask(OptionInfo::AA));
+ EXPECT_EQ(10, OptionInfo::offset(OptionInfo::AA));
+ EXPECT_EQ(0, OptionInfo::minval(OptionInfo::AA));
+ EXPECT_EQ(0, OptionInfo::defval(OptionInfo::AA));
+ EXPECT_EQ(1, OptionInfo::maxval(OptionInfo::AA));
+
+ EXPECT_STREQ("tc", OptionInfo::name(OptionInfo::TC));
+ EXPECT_STREQ("tc", OptionInfo::name(OptionInfo::getIndex('T')));
+ EXPECT_EQ(2, OptionInfo::word(OptionInfo::TC));
+ EXPECT_EQ(0x0200, OptionInfo::mask(OptionInfo::TC));
+ EXPECT_EQ(9, OptionInfo::offset(OptionInfo::TC));
+ EXPECT_EQ(0, OptionInfo::minval(OptionInfo::TC));
+ EXPECT_EQ(0, OptionInfo::defval(OptionInfo::TC));
+ EXPECT_EQ(1, OptionInfo::maxval(OptionInfo::TC));
+
+ EXPECT_STREQ("rd", OptionInfo::name(OptionInfo::RD));
+ EXPECT_STREQ("rd", OptionInfo::name(OptionInfo::getIndex('D')));
+ EXPECT_EQ(2, OptionInfo::word(OptionInfo::RD));
+ EXPECT_EQ(0x0100, OptionInfo::mask(OptionInfo::RD));
+ EXPECT_EQ(8, OptionInfo::offset(OptionInfo::RD));
+ EXPECT_EQ(0, OptionInfo::minval(OptionInfo::RD));
+ EXPECT_EQ(0, OptionInfo::defval(OptionInfo::RD));
+ EXPECT_EQ(1, OptionInfo::maxval(OptionInfo::RD));
+
+ EXPECT_STREQ("ra", OptionInfo::name(OptionInfo::RA));
+ EXPECT_STREQ("ra", OptionInfo::name(OptionInfo::getIndex('R')));
+ EXPECT_EQ(2, OptionInfo::word(OptionInfo::RA));
+ EXPECT_EQ(0x0080, OptionInfo::mask(OptionInfo::RA));
+ EXPECT_EQ(7, OptionInfo::offset(OptionInfo::RA));
+ EXPECT_EQ(0, OptionInfo::minval(OptionInfo::RA));
+ EXPECT_EQ(0, OptionInfo::defval(OptionInfo::RA));
+ EXPECT_EQ(1, OptionInfo::maxval(OptionInfo::RA));
+
+ EXPECT_STREQ("z", OptionInfo::name(OptionInfo::Z));
+ EXPECT_STREQ("z", OptionInfo::name(OptionInfo::getIndex('Z')));
+ EXPECT_EQ(2, OptionInfo::word(OptionInfo::Z));
+ EXPECT_EQ(0x0040, OptionInfo::mask(OptionInfo::Z));
+ EXPECT_EQ(6, OptionInfo::offset(OptionInfo::Z));
+ EXPECT_EQ(0, OptionInfo::minval(OptionInfo::Z));
+ EXPECT_EQ(0, OptionInfo::defval(OptionInfo::Z));
+ EXPECT_EQ(1, OptionInfo::maxval(OptionInfo::Z));
+
+ EXPECT_STREQ("ad", OptionInfo::name(OptionInfo::AD));
+ EXPECT_STREQ("ad", OptionInfo::name(OptionInfo::getIndex('U')));
+ EXPECT_EQ(2, OptionInfo::word(OptionInfo::AD));
+ EXPECT_EQ(0x0020, OptionInfo::mask(OptionInfo::AD));
+ EXPECT_EQ(5, OptionInfo::offset(OptionInfo::AD));
+ EXPECT_EQ(0, OptionInfo::minval(OptionInfo::AD));
+ EXPECT_EQ(0, OptionInfo::defval(OptionInfo::AD));
+ EXPECT_EQ(1, OptionInfo::maxval(OptionInfo::AD));
+
+ EXPECT_STREQ("cd", OptionInfo::name(OptionInfo::CD));
+ EXPECT_STREQ("cd", OptionInfo::name(OptionInfo::getIndex('C')));
+ EXPECT_EQ(2, OptionInfo::word(OptionInfo::CD));
+ EXPECT_EQ(0x0010, OptionInfo::mask(OptionInfo::CD));
+ EXPECT_EQ(4, OptionInfo::offset(OptionInfo::CD));
+ EXPECT_EQ(0, OptionInfo::minval(OptionInfo::CD));
+ EXPECT_EQ(0, OptionInfo::defval(OptionInfo::CD));
+ EXPECT_EQ(1, OptionInfo::maxval(OptionInfo::CD));
+
+ EXPECT_STREQ("rc", OptionInfo::name(OptionInfo::RC));
+ EXPECT_STREQ("rc", OptionInfo::name(OptionInfo::getIndex('E')));
+ EXPECT_EQ(2, OptionInfo::word(OptionInfo::RC));
+ EXPECT_EQ(0x000F, OptionInfo::mask(OptionInfo::RC));
+ EXPECT_EQ(0, OptionInfo::offset(OptionInfo::RC));
+ EXPECT_EQ(0, OptionInfo::minval(OptionInfo::RC));
+ EXPECT_EQ(0, OptionInfo::defval(OptionInfo::RC));
+ EXPECT_EQ(15, OptionInfo::maxval(OptionInfo::RC));
+}
+
+TEST_F(OptionInfoTest, CountValues) {
+ EXPECT_STREQ("qc", OptionInfo::name(OptionInfo::QC));
+ EXPECT_STREQ("qc", OptionInfo::name(OptionInfo::getIndex('Y')));
+ EXPECT_EQ(4, OptionInfo::word(OptionInfo::QC));
+ EXPECT_EQ(1, OptionInfo::defval(OptionInfo::QC));
+ EXPECT_EQ(0, OptionInfo::minval(OptionInfo::QC));
+ EXPECT_EQ(0xFFFF, OptionInfo::maxval(OptionInfo::QC));
+
+ EXPECT_STREQ("ac", OptionInfo::name(OptionInfo::AC));
+ EXPECT_STREQ("ac", OptionInfo::name(OptionInfo::getIndex('W')));
+ EXPECT_EQ(6, OptionInfo::word(OptionInfo::AC));
+ EXPECT_EQ(0, OptionInfo::defval(OptionInfo::AC));
+ EXPECT_EQ(0, OptionInfo::minval(OptionInfo::AC));
+ EXPECT_EQ(0xFFFF, OptionInfo::maxval(OptionInfo::AC));
+
+ EXPECT_STREQ("uc", OptionInfo::name(OptionInfo::UC));
+ EXPECT_STREQ("uc", OptionInfo::name(OptionInfo::getIndex('H')));
+ EXPECT_EQ(8, OptionInfo::word(OptionInfo::UC));
+ EXPECT_EQ(0, OptionInfo::defval(OptionInfo::UC));
+ EXPECT_EQ(0, OptionInfo::minval(OptionInfo::UC));
+ EXPECT_EQ(0xFFFF, OptionInfo::maxval(OptionInfo::UC));
+
+ EXPECT_STREQ("dc", OptionInfo::name(OptionInfo::DC));
+ EXPECT_STREQ("dc", OptionInfo::name(OptionInfo::getIndex('I')));
+ EXPECT_EQ(10, OptionInfo::word(OptionInfo::DC));
+ EXPECT_EQ(0, OptionInfo::defval(OptionInfo::DC));
+ EXPECT_EQ(0, OptionInfo::minval(OptionInfo::DC));
+ EXPECT_EQ(0xFFFF, OptionInfo::maxval(OptionInfo::DC));
+}
+
+TEST_F(OptionInfoTest, OtherValues) {
+ EXPECT_STREQ("ms", OptionInfo::name(OptionInfo::MS));
+ EXPECT_STREQ("ms", OptionInfo::name(OptionInfo::getIndex('M')));
+ EXPECT_EQ(1, OptionInfo::minval(OptionInfo::MS));
+ EXPECT_EQ(65536, OptionInfo::maxval(OptionInfo::MS));
+}
diff --git a/tests/tools/badpacket/tests/run_unittests.cc b/tests/tools/badpacket/tests/run_unittests.cc
new file mode 100644
index 0000000..6eeca75
--- /dev/null
+++ b/tests/tools/badpacket/tests/run_unittests.cc
@@ -0,0 +1,25 @@
+// Copyright (C) 2009 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 <util/unittests/run_all.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return (isc::util::unittests::run_all());
+}
diff --git a/tests/tools/badpacket/version.h b/tests/tools/badpacket/version.h
new file mode 100644
index 0000000..dc59b11
--- /dev/null
+++ b/tests/tools/badpacket/version.h
@@ -0,0 +1,26 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __VERSION_H
+#define __VERSION_H
+
+namespace isc {
+namespace badpacket {
+
+static const char* BADPACKET_VERSION = "1.0-1";
+
+} // namespace badpacket
+} // namespace isc
+
+#endif // __VERSION_H
diff --git a/tools/query_cmp/README b/tools/query_cmp/README
new file mode 100644
index 0000000..bd3efcc
--- /dev/null
+++ b/tools/query_cmp/README
@@ -0,0 +1,27 @@
+This is a tool to compare two DNS server's response to query.
+
+DIRECTORY STRUCTURE
+
+zonefile
+ The file under this directory is for the testee servers
+ to load before running the test, containing various types
+ of RRs in the test cases. It is in bind9's format. One
+ file is signed while the other is not, which you can choose.
+
+queries
+ The files under this directory are the input of the test,
+ involving various types of query cases.
+
+src
+ The scripts of this test.
+ It uses the dns python binding interface of bind10 from the
+ source tree, so src/lib/dns/python/.libs must be added to
+ PYTHONPATH environment variable ahead of running the tests.
+
+RUNNING
+
+e.g.
+cd src
+./query_two_server.py -u -f ../queries/dquery01 -s 10.10.1.1 -p 30000 -t 10.10.10.2 -q 30002 > bind10test_normal
+
+./query_two_server.py --help' for more details
diff --git a/tools/query_cmp/queries/dquery01 b/tools/query_cmp/queries/dquery01
new file mode 100644
index 0000000..923ba7d
--- /dev/null
+++ b/tools/query_cmp/queries/dquery01
@@ -0,0 +1,394 @@
+# Fields Description
+#
+#query:ID QR OPCODE AA TC RD RA Z AD CD RCODE QDCOUNT ANCOUNT NSCOUNT ARCOUNT QNAME QTYPE QCLASS
+#response:ID QR OPCODE AA TC RD RA Z AD CD RCODE QDCOUNT ANCOUNT NSCOUNT ARCOUNT QNAME QTYPE QCLASS <answer> <authority> <additional>
+# <answer> := <rr1> .. <rrN>
+# <rr> := NAME TYPE CLASS TTL RDLENGTH <rdata>
+# <rdata> := ADDRESS |
+# NSDNAME |
+# MNAME RNAME SERIAL REFRESH RETRY EXPIRE MINIMUM |
+# ...
+# <authority> := <rr1> .. <rrN>
+# <additional> := <rr1> .. <rrN>
+#
+#
+#
+# Description in BNF (http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form)
+# <query> ::= <header> <question>
+# <header> ::= <ID> <QR> <OPCODE> <AA> <TC> <RD> <RA> <Z> <AD> <CD> <RCODE>
+# <QDCOUNT> <ANCOUNT> <NSCOUNT> <ARCOUNT>
+# <question> ::= <QNAME> <QTYPE> <QCLASS>
+#
+# <response> ::= <header> <question> <answer> <authority> <additional>
+# <answer> ::= <rrset>
+# <authority> ::= <rrset>
+# <additional> ::= <rrset>
+# <rrset> ::= { <rr> }
+# <rr> ::= <name> <type> <class> <ttl> <rdlength> <rdata>
+# <name> ::= <subdomain> | ""
+# <subdomain> ::= <label> | <subdomain> "." <label>
+# <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
+# <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
+# <let-dig-hyp> ::= <let-dig> | "-"
+# <let-dig> ::= <letter> | <digit>
+# <letter> ::= "a" | .. | "z" | "A" | .. | "Z"
+# <digit> ::= "0" | .. | "9"
+# <type> ::= A | NS | CNAME | SOA | PTR | MX | ..
+# <class> ::= IN | CH | HS | CS
+# <ttl> ::= <digit> | { <digit> }
+# <rdlength> ::= <digit> | { <digit> }
+# <rdata> ::= <address> |
+# <nsdname> |
+# <cname> |
+# <preference> <exchange> |
+# <ptrdname> |
+# ...
+
+0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com A IN
+1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com A IN
+2 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com A IN
+3 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com A IN
+4 0 0 0 0 1 0 0 0 0 0 1 0 0 0 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com A IN
+5 0 0 0 0 1 0 0 0 0 0 1 0 0 0 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com A IN
+6 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com A IN
+7 0 0 0 0 1 0 0 0 0 0 1 0 0 0 B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com A IN
+8 0 0 0 0 1 0 0 0 0 0 1 0 0 0 M.example.com A IN
+9 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-a-record.example.com A IN
+
+10 0 0 0 0 1 0 0 0 0 0 1 0 0 0 NS.example.com A IN
+11 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nS.example.com A IN
+12 0 0 0 0 1 0 0 0 0 0 1 0 0 0 NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com A IN
+13 0 0 0 0 1 0 0 0 0 0 1 0 0 0 NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com A IN
+14 0 0 0 0 1 0 0 0 0 0 1 0 0 0 N.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com A IN
+15 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.multiple-type-ns-record.example.com a IN
+
+20 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C.example.com A IN
+21 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C.example.com CNAME IN
+22 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C.example.com MX IN
+23 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C.example.com ANY IN
+
+24 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C0.example.com A IN
+25 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C0.example.com CNAME IN
+26 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C0.example.com MX IN
+27 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C0.example.com ANY IN
+
+28 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C10.example.com A IN
+29 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C10.example.com CNAME IN
+30 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C10.example.com MX IN
+31 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C10.example.com ANY IN
+
+32 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C36.example.com A IN
+33 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C36.example.com CNAME IN
+34 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C36.example.com MX IN
+35 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C36.example.com ANY IN
+
+36 0 0 0 0 1 0 0 0 0 0 1 0 0 0 c.Example.coM A IN
+37 0 0 0 0 1 0 0 0 0 0 1 0 0 0 c.Example.coM CNAME IN
+38 0 0 0 0 1 0 0 0 0 0 1 0 0 0 c.Example.coM MX IN
+39 0 0 0 0 1 0 0 0 0 0 1 0 0 0 c.Example.coM ANY IN
+
+40 0 0 0 0 1 0 0 0 0 0 1 0 0 0 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com A IN
+41 0 0 0 0 1 0 0 0 0 0 1 0 0 0 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com CNAME IN
+42 0 0 0 0 1 0 0 0 0 0 1 0 0 0 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com MX IN
+43 0 0 0 0 1 0 0 0 0 0 1 0 0 0 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com ANY IN
+
+44 0 0 0 0 1 0 0 0 0 0 1 0 0 0 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA1.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA2.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA3.example.com A IN
+45 0 0 0 0 1 0 0 0 0 0 1 0 0 0 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA1.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA2.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA3.example.com CNAME IN
+46 0 0 0 0 1 0 0 0 0 0 1 0 0 0 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA1.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA2.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA3.example.com MX IN
+47 0 0 0 0 1 0 0 0 0 0 1 0 0 0 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA1.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA2.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA3.example.com ANY IN
+
+48 0 0 0 0 1 0 0 0 0 0 1 0 0 0 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA1.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA2.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA4.example.com A IN
+49 0 0 0 0 1 0 0 0 0 0 1 0 0 0 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA1.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA2.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA4.example.com CNAME IN
+50 0 0 0 0 1 0 0 0 0 0 1 0 0 0 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA1.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA2.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA4.example.com MX IN
+51 0 0 0 0 1 0 0 0 0 0 1 0 0 0 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA1.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA2.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA4.example.com ANY IN
+
+52 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.example.com A IN
+53 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.example.com CNAME IN
+54 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.example.com MX IN
+55 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.example.com ANY IN
+
+56 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.1.example.com A IN
+57 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.1.example.com CNAME IN
+58 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.1.example.com MX IN
+59 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.1.example.com ANY IN
+
+60 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C0.name1.cn A IN
+61 0 0 0 0 1 0 0 0 0 0 1 0 0 0 C12.name1.cn A IN
+
+70 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example.com SOA IN
+71 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example.com ANY IN
+
+72 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example1.com SOA IN
+73 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example1.com A IN
+74 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example1.com NS IN
+75 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example1.com ANY IN
+
+76 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example2.com SOA IN
+77 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example2.com A IN
+78 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example2.com NS IN
+79 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example2.com ANY IN
+
+80 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example3.com SOA IN
+81 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example3.com A IN
+82 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example3.com NS IN
+83 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example3.com ANY IN
+
+84 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example4.com SOA IN
+85 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example4.com A IN
+86 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example4.com NS IN
+87 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example4.com ANY IN
+
+88 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example13.com SOA IN
+89 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example13.com A IN
+90 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example13.com NS IN
+91 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example13.com ANY IN
+
+92 0 0 0 0 1 0 0 0 0 0 1 0 0 0 noexist.example.com A IN
+93 0 0 0 0 1 0 0 0 0 0 1 0 0 0 noexist.example.com ANY IN
+94 0 0 0 0 1 0 0 0 0 0 1 0 0 0 noexist.example.com NS IN
+
+95 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example9.com SOA IN
+96 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example9.com ns IN
+97 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example9.com ANY IN
+
+98 0 0 0 0 1 0 0 0 0 0 1 0 0 0 noexist.example.com SOA IN
+99 0 0 0 0 1 0 0 0 0 0 1 0 0 0 noexist.noexist SOA IN
+
+100 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1.1.10.10.in-addr.arpA PTR IN
+101 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com PTR IN
+102 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1.1.10.10.in-addr.arpa PTR IN
+103 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.examPle.com PTR IN
+104 0 0 0 0 1 0 0 0 0 0 1 0 0 0 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com PTR IN
+105 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com PTR IN
+106 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com PTR IN
+107 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1.2.168.192.in-addr.arpa PTR IN
+108 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-a-record.example.com PTR IN
+
+110 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com MX IN
+111 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com MX IN
+112 0 0 0 0 1 0 0 0 0 0 1 0 0 0 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com MX IN
+113 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com MX IN
+114 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com MX IN
+115 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-mx-record.example.com MX IN
+
+120 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com TXT IN
+121 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com TXT IN
+122 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com TXT IN
+123 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com TXT IN
+124 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com TXT IN
+125 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-txt-record.example.com TXT IN
+
+130 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com AAAA IN
+131 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com AAAA IN
+132 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com AAAA IN
+133 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com AAAA IN
+134 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com AAAA IN
+135 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-aaaa-record.example.com AAAA IN
+
+140 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com NAPTR IN
+141 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com NAPTR IN
+142 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com NAPTR IN
+143 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com NAPTR IN
+144 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com NAPTR IN
+145 0 0 0 0 1 0 0 0 0 0 1 0 0 0 2.1.2.1.5.5.5.0.7.7.1.e164.arpa NAPTR IN
+146 0 0 0 0 1 0 0 0 0 0 1 0 0 0 b.e164.arpa NAPTR IN
+147 0 0 0 0 1 0 0 0 0 0 1 0 0 0 6.8.e164.arpa NAPTR IN
+
+#150 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com A6 IN
+#151 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com A6 IN
+#152 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com A6 IN
+#153 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com A6 IN
+#154 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com A6 IN
+#155 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-a6-record.example.com A6 IN
+
+# case 160-163
+160 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com DNAME IN
+161 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com ANY IN
+162 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.A.example.com A IN
+163 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.A.example.com ANY IN
+
+# case 164-167
+164 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.exAmple.com DNAME IN
+165 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.exAmple.com ANY IN
+166 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.a.exAmple.com A IN
+167 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.a.exAmple.com ANY IN
+
+# case 168-171
+168 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com DNAME IN
+169 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com ANY IN
+170 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com A IN
+171 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com ANY IN
+
+# case 172-175
+172 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com DNAME IN
+173 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com ANY IN
+174 0 0 0 0 1 0 0 0 0 0 1 0 0 0 D.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com A IN
+175 0 0 0 0 1 0 0 0 0 0 1 0 0 0 D.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com ANY IN
+
+# case 176-179
+176 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.example.com DNAME IN
+177 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.example.com ANY IN
+178 0 0 0 0 1 0 0 0 0 0 1 0 0 0 D.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.example.com A IN
+179 0 0 0 0 1 0 0 0 0 0 1 0 0 0 D.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.example.com ANY IN
+
+# case 180-195
+180 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.d0.example.com a IN
+181 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.d1.example.com a IN
+182 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.d2.example.com a IN
+183 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.d4.example.com a IN
+184 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.d5.example.com a IN
+185 0 0 0 0 1 0 0 0 0 0 1 0 0 0 ns.d0.example.com a IN
+186 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.a.b.d1.example.com a IN
+187 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.a.b.d1.example.com mx IN
+
+188 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.d10.example.com a IN
+189 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.d20.example.com a IN
+190 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.d30.example.com a IN
+191 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.d40.example.com a IN
+192 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.d50.example.com a IN
+193 0 0 0 0 1 0 0 0 0 0 1 0 0 0 c45.example.com any IN
+194 0 0 0 0 1 0 0 0 0 0 1 0 0 0 d45.example.com any IN
+195 0 0 0 0 1 0 0 0 0 0 1 0 0 0 noexist.d45.example.com any IN
+196 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.d45.example.com any IN
+
+197 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.d72.example.com a IN
+198 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.d1.example.com a IN
+
+# case 200-205
+200 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com WKS IN
+201 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com WKS IN
+202 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com WKS IN
+203 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com WKS IN
+204 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com WKS IN
+205 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-wks-record.example.com WKS IN
+
+# case 210-215
+210 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com HINFO IN
+211 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com HINFO IN
+212 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com HINFO IN
+213 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com HINFO IN
+214 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com HINFO IN
+215 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-hinfo-record.example.com HINFO IN
+
+# case 220-225
+220 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com MINFO IN
+221 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com MINFO IN
+222 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com MINFO IN
+223 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com MINFO IN
+224 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com MINFO IN
+225 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-minfo-record.example.com MINFO IN
+
+# case 230-235
+230 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com NSAP IN
+231 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com NSAP IN
+232 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com NSAP IN
+233 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com NSAP IN
+234 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com NSAP IN
+235 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-nsap-record.example.com NSAP IN
+
+# case 240-245
+240 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com PX IN
+241 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com PX IN
+242 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com PX IN
+243 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com PX IN
+244 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com PX IN
+245 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-px-record.example.com PX IN
+
+# case 250-255
+250 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com LOC IN
+251 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com LOC IN
+252 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com LOC IN
+253 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com LOC IN
+254 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com LOC IN
+255 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-loc-record.example.com LOC IN
+
+# case 260-265
+260 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com SRV IN
+261 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com SRV IN
+262 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com SRV IN
+263 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com SRV IN
+264 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com SRV IN
+265 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-srv-record.example.com SRV IN
+
+# case 270-275
+270 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com KX IN
+271 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com KX IN
+272 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com KX IN
+273 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com KX IN
+274 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com KX IN
+275 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-kx-record.example.com KX IN
+
+# case 280-285
+280 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com CERT IN
+281 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com CERT IN
+282 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com CERT IN
+283 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com CERT IN
+284 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com CERT IN
+285 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-cert-record.example.com CERT IN
+
+# case 290-295
+290 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com APL IN
+291 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com APL IN
+292 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com APL IN
+293 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com APL IN
+294 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com APL IN
+295 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-apl-record.example.com APL IN
+
+# case 300-305
+300 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com SSHFP IN
+301 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com SSHFP IN
+302 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com SSHFP IN
+303 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com SSHFP IN
+304 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com SSHFP IN
+305 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-sshfp-record.example.com SSHFP IN
+
+# case 310-315
+310 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com IPSECKEY IN
+311 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com IPSECKEY IN
+312 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com IPSECKEY IN
+313 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com IPSECKEY IN
+314 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com IPSECKEY IN
+315 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-ipseckey-record.example.com IPSECKEY IN
+
+# case 320-325
+#320 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com DHCID IN
+#321 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com DHCID IN
+#322 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com DHCID IN
+#323 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com DHCID IN
+#324 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com DHCID IN
+#325 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-dhcid-record.example.com DHCID IN
+
+# case 330-335
+330 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.example.com SPF IN
+331 0 0 0 0 1 0 0 0 0 0 1 0 0 0 a.example.com SPF IN
+332 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com SPF IN
+333 0 0 0 0 1 0 0 0 0 0 1 0 0 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com SPF IN
+334 0 0 0 0 1 0 0 0 0 0 1 0 0 0 A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com SPF IN
+335 0 0 0 0 1 0 0 0 0 0 1 0 0 0 multiple-type-spf-record.example.com SPF IN
+
+360 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0.example.com A IN
+361 0 0 0 0 1 0 0 0 0 0 1 0 0 0 9.example.com A IN
+
+370 0 0 0 0 1 0 0 0 0 0 1 0 0 0 sub-cname.example.com A IN
+371 0 0 0 0 1 0 0 0 0 0 1 0 0 0 www.sub-dname.example.com A IN
+
+381 0 0 0 0 1 0 0 0 0 0 1 0 0 0 type-a-answer.toobigudp.com A IN
+382 0 0 0 0 1 0 0 0 0 0 1 0 0 0 type-a-authority.toobigudp.com A IN
+383 0 0 0 0 1 0 0 0 0 0 1 0 0 0 toobigudp.com NS IN
+384 0 0 0 0 1 0 0 0 0 0 1 0 0 0 type-txt-answer.toobigudp.com TXT IN
+385 0 0 0 0 1 0 0 0 0 0 1 0 0 0 type-txt-authority.toobigudp.com TXT IN
+386 0 0 0 0 1 0 0 0 0 0 1 0 0 0 type-cname-answer.toobigudp.com A IN
+387 0 0 0 0 1 0 0 0 0 0 1 0 0 0 type-cname-answer.toobigudp.com TXT IN
+
+390 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example.com DNSKEY IN
+391 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example.com RRSIG IN
+392 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example.com NSEC IN
+393 0 0 0 0 1 0 0 0 0 0 1 0 0 0 example.com DS IN
+#0x1000 0 0 0 0 1 0 0 0 0 0 1 0 0 0 version.bind txt CH
+#0x1001 0 0 0 0 1 0 0 0 0 0 1 0 0 0 hostname.bind txt CH
+#0x1002 0 0 0 0 1 0 0 0 0 0 1 0 0 0 id.server txt CH
+#0x1003 0 0 0 0 1 0 0 0 0 0 1 0 0 0 authors.bind txt CH
+#0x1000 0 0 0 0 1 0 0 0 0 0 1 0 0 0 version.bind txt CH
+#0x1001 0 0 0 0 1 0 0 0 0 0 1 0 0 0 hostname.bind txt CH
+#0x1002 0 0 0 0 1 0 0 0 0 0 1 0 0 0 id.server txt CH
+#0x1003 0 0 0 0 1 0 0 0 0 0 1 0 0 0 authors.bind txt CH
diff --git a/tools/query_cmp/queries/dquery01_no-type b/tools/query_cmp/queries/dquery01_no-type
new file mode 100644
index 0000000..69b09ba
--- /dev/null
+++ b/tools/query_cmp/queries/dquery01_no-type
@@ -0,0 +1,316 @@
+# Fields Description
+#
+#query:ID QR OPCODE AA TC RD RA Z AD CD RCODE QDCOUNT ANCOUNT NSCOUNT ARCOUNT testa.no-type.QNAME QTYPE QCLASS
+#response:ID QR OPCODE AA TC RD RA Z AD CD RCODE QDCOUNT ANCOUNT NSCOUNT ARCOUNT testa.no-type.QNAME QTYPE QCLASS
+# <answer> := <rr1> .. <rrN>
+# <rr> := NAME TYPE CLASS TTL RDLENGTH <rdata>
+# <rdata> := ADDRESS |
+# NSDNAME |
+# MNAME RNAME SERIAL REFRESH RETRY EXPIRE MINIMUM |
+# ...
+# <authority> := <rr1> .. <rrN>
+# <additional> := <rr1> .. <rrN>
+#
+#
+#
+# Description in BNF (http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form)
+# <query> ::= <header> <question>
+# <header> ::= <ID> <QR> <OPCODE> <AA> <TC> <RD> <RA> <Z> <AD> <CD> <RCODE>
+# <QDCOUNT> <ANCOUNT> <NSCOUNT> <ARCOUNT>
+# <question> ::= <QNAME> <QTYPE> <QCLASS>
+#
+# <response> ::= <header> <question> <answer> <authority> <additional>
+# <answer> ::= <rrset>
+# <authority> ::= <rrset>
+# <additional> ::= <rrset>
+# <rrset> ::= { <rr> }
+# <rr> ::= <name> <type> <class> <ttl> <rdlength> <rdata>
+# <name> ::= <subdomain> | ""
+# <subdomain> ::= <label> | <subdomain> "." <label>
+# <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
+# <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
+# <let-dig-hyp> ::= <let-dig> | "-"
+# <let-dig> ::= <letter> | <digit>
+# <letter> ::= "a" | .. | "z" | "A" | .. | "Z"
+# <digit> ::= "0" | .. | "9"
+# <type> ::= A | NS | CNAME | SOA | PTR | MX | testa.no-type...
+# <class> ::= IN | CH | HS | CS
+# <ttl> ::= <digit> | { <digit> }
+# <rdlength> ::= <digit> | { <digit> }
+# <rdata> ::= <address> |
+# <nsdname> |
+# <cname> |
+# <preference> <exchange> |
+# <ptrdname> |
+# ...
+
+0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com A IN
+1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com A IN
+2 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com A IN
+5 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com A IN
+8 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.M.example.com A IN
+9 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-a-record.example.com A IN
+
+10 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.NS.example.com A IN
+11 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.nS.example.com A IN
+12 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com A IN
+15 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.multiple-type-ns-record.example.com a IN
+
+20 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C.example.com A IN
+21 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C.example.com CNAME IN
+22 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C.example.com MX IN
+23 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C.example.com ANY IN
+
+24 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C0.example.com A IN
+25 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C0.example.com CNAME IN
+26 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C0.example.com MX IN
+27 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C0.example.com ANY IN
+
+28 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C10.example.com A IN
+29 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C10.example.com CNAME IN
+30 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C10.example.com MX IN
+31 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C10.example.com ANY IN
+
+32 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C36.example.com A IN
+33 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C36.example.com CNAME IN
+34 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C36.example.com MX IN
+35 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C36.example.com ANY IN
+
+36 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.c.Example.coM A IN
+37 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.c.Example.coM CNAME IN
+38 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.c.Example.coM MX IN
+39 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.c.Example.coM ANY IN
+
+40 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com A IN
+41 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com CNAME IN
+42 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com MX IN
+43 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com ANY IN
+
+60 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C0.name1.cn A IN
+61 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.C12.name1.cn A IN
+
+70 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example.com SOA IN
+71 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example.com ANY IN
+
+72 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example1.com SOA IN
+73 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example1.com A IN
+74 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example1.com NS IN
+75 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example1.com ANY IN
+
+76 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example2.com SOA IN
+77 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example2.com A IN
+78 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example2.com NS IN
+79 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example2.com ANY IN
+
+80 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example3.com SOA IN
+81 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example3.com A IN
+82 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example3.com NS IN
+83 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example3.com ANY IN
+
+84 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example4.com SOA IN
+85 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example4.com A IN
+86 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example4.com NS IN
+87 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example4.com ANY IN
+
+88 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example13.com SOA IN
+89 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example13.com A IN
+90 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example13.com NS IN
+91 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example13.com ANY IN
+
+92 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.noexist.example.com A IN
+93 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.noexist.example.com ANY IN
+94 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.noexist.example.com NS IN
+
+95 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example9.com SOA IN
+96 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example9.com ns IN
+97 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example9.com ANY IN
+
+98 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.noexist.example.com SOA IN
+99 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.noexist.noexist SOA IN
+
+100 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.1.1.10.10.in-addr.arpA PTR IN
+101 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com PTR IN
+102 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.1.1.10.10.in-addr.arpa PTR IN
+103 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.examPle.com PTR IN
+104 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com PTR IN
+107 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.1.2.168.192.in-addr.arpa PTR IN
+108 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-a-record.example.com PTR IN
+
+110 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com MX IN
+111 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com MX IN
+112 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com MX IN
+115 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-mx-record.example.com MX IN
+
+120 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com TXT IN
+121 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com TXT IN
+122 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com TXT IN
+125 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-txt-record.example.com TXT IN
+
+130 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com AAAA IN
+131 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com AAAA IN
+132 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com AAAA IN
+135 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-aaaa-record.example.com AAAA IN
+
+140 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com NAPTR IN
+141 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com NAPTR IN
+142 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com NAPTR IN
+145 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.2.1.2.1.5.5.5.0.7.7.1.e164.arpa NAPTR IN
+146 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.b.e164.arpa NAPTR IN
+147 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.6.8.e164.arpa NAPTR IN
+
+#150 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com A6 IN
+#151 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com A6 IN
+#152 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com A6 IN
+#155 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-a6-record.example.com A6 IN
+
+# case 160-163
+160 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com DNAME IN
+161 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com ANY IN
+162 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.A.example.com A IN
+163 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.A.example.com ANY IN
+
+# case 164-167
+164 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.exAmple.com DNAME IN
+165 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.exAmple.com ANY IN
+166 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.a.exAmple.com A IN
+167 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.a.exAmple.com ANY IN
+
+# case 168-171
+168 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com DNAME IN
+169 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com ANY IN
+170 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com A IN
+171 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com ANY IN
+
+# case 180-195
+180 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.d0.example.com a IN
+181 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.d1.example.com a IN
+182 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.d2.example.com a IN
+183 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.d4.example.com a IN
+184 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.d5.example.com a IN
+185 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.ns.d0.example.com a IN
+186 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.a.b.d1.example.com a IN
+187 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.a.b.d1.example.com mx IN
+
+188 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.d10.example.com a IN
+189 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.d20.example.com a IN
+190 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.d30.example.com a IN
+191 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.d40.example.com a IN
+192 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.d50.example.com a IN
+193 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.c45.example.com any IN
+194 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.d45.example.com any IN
+195 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.noexist.d45.example.com any IN
+196 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.d45.example.com any IN
+
+197 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.d72.example.com a IN
+198 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.d1.example.com a IN
+
+# case 200-205
+200 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com WKS IN
+201 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com WKS IN
+202 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com WKS IN
+205 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-wks-record.example.com WKS IN
+
+# case 210-215
+210 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com HINFO IN
+211 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com HINFO IN
+212 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com HINFO IN
+215 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-hinfo-record.example.com HINFO IN
+
+# case 220-225
+220 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com MINFO IN
+221 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com MINFO IN
+222 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com MINFO IN
+225 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-minfo-record.example.com MINFO IN
+
+# case 230-235
+230 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com NSAP IN
+231 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com NSAP IN
+232 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com NSAP IN
+235 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-nsap-record.example.com NSAP IN
+
+# case 240-245
+240 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com PX IN
+241 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com PX IN
+242 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com PX IN
+245 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-px-record.example.com PX IN
+
+# case 250-255
+250 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com LOC IN
+251 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com LOC IN
+252 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com LOC IN
+255 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-loc-record.example.com LOC IN
+
+# case 260-265
+260 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com SRV IN
+261 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com SRV IN
+262 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com SRV IN
+265 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-srv-record.example.com SRV IN
+
+# case 270-275
+270 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com KX IN
+271 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com KX IN
+272 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com KX IN
+275 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-kx-record.example.com KX IN
+
+# case 280-285
+280 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com CERT IN
+281 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com CERT IN
+282 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com CERT IN
+285 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-cert-record.example.com CERT IN
+
+# case 290-295
+290 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com APL IN
+291 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com APL IN
+292 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com APL IN
+295 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-apl-record.example.com APL IN
+
+# case 300-305
+300 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com SSHFP IN
+301 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com SSHFP IN
+302 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com SSHFP IN
+305 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-sshfp-record.example.com SSHFP IN
+
+# case 310-315
+310 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com IPSECKEY IN
+311 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com IPSECKEY IN
+312 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com IPSECKEY IN
+315 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-ipseckey-record.example.com IPSECKEY IN
+
+# case 320-325
+#320 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com DHCID IN
+#321 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com DHCID IN
+#322 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com DHCID IN
+#325 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-dhcid-record.example.com DHCID IN
+
+# case 330-335
+330 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.A.example.com SPF IN
+331 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.a.example.com SPF IN
+332 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com SPF IN
+335 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.multiple-type-spf-record.example.com SPF IN
+
+360 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.0.example.com A IN
+361 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.9.example.com A IN
+
+370 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.sub-cname.example.com A IN
+371 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.www.sub-dname.example.com A IN
+
+381 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.type-a-answer.toobigudp.com A IN
+382 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.type-a-authority.toobigudp.com A IN
+383 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.toobigudp.com NS IN
+384 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.type-txt-answer.toobigudp.com TXT IN
+385 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.type-txt-authority.toobigudp.com TXT IN
+386 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.type-cname-answer.toobigudp.com A IN
+387 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.type-cname-answer.toobigudp.com TXT IN
+
+390 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example.com DNSKEY IN
+391 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example.com RRSIG IN
+392 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example.com NSEC IN
+393 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.example.com DS IN
+#0x1000 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.version.bind txt CH
+#0x1001 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.hostname.bind txt CH
+#0x1002 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.id.server txt CH
+#0x1003 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.authors.bind txt CH
+#0x1000 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.version.bind txt CH
+#0x1001 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.hostname.bind txt CH
+#0x1002 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.id.server txt CH
+#0x1003 0 0 0 0 1 0 0 0 0 0 1 0 0 0 testa.no-type.authors.bind txt CH
diff --git a/tools/query_cmp/queries/dquery01_non-terminal b/tools/query_cmp/queries/dquery01_non-terminal
new file mode 100644
index 0000000..2bbd9de
--- /dev/null
+++ b/tools/query_cmp/queries/dquery01_non-terminal
@@ -0,0 +1,317 @@
+# Fields Description
+#
+#query:ID QR OPCODE AA TC RD RA Z AD CD RCODE QDCOUNT ANCOUNT NSCOUNT ARCOUNT non-terminal.QNAME QTYPE QCLASS
+#response:ID QR OPCODE AA TC RD RA Z AD CD RCODE QDCOUNT ANCOUNT NSCOUNT ARCOUNT non-terminal.QNAME QTYPE QCLASS
+# <answer> := <rr1> .. <rrN>
+# <rr> := NAME TYPE CLASS TTL RDLENGTH <rdata>
+# <rdata> := ADDRESS |
+# NSDNAME |
+# MNAME RNAME SERIAL REFRESH RETRY EXPIRE MINIMUM |
+# ...
+# <authority> := <rr1> .. <rrN>
+# <additional> := <rr1> .. <rrN>
+#
+#
+#
+# Description in BNF (http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form)
+# <query> ::= <header> <question>
+# <header> ::= <ID> <QR> <OPCODE> <AA> <TC> <RD> <RA> <Z> <AD> <CD> <RCODE>
+# <QDCOUNT> <ANCOUNT> <NSCOUNT> <ARCOUNT>
+# <question> ::= <QNAME> <QTYPE> <QCLASS>
+#
+# <response> ::= <header> <question> <answer> <authority> <additional>
+# <answer> ::= <rrset>
+# <authority> ::= <rrset>
+# <additional> ::= <rrset>
+# <rrset> ::= { <rr> }
+# <rr> ::= <name> <type> <class> <ttl> <rdlength> <rdata>
+# <name> ::= <subdomain> | ""
+# <subdomain> ::= <label> | <subdomain> "." <label>
+# <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
+# <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
+# <let-dig-hyp> ::= <let-dig> | "-"
+# <let-dig> ::= <letter> | <digit>
+# <letter> ::= "a" | .. | "z" | "A" | .. | "Z"
+# <digit> ::= "0" | .. | "9"
+# <type> ::= A | NS | CNAME | SOA | PTR | MX | non-terminal...
+# <class> ::= IN | CH | HS | CS
+# <ttl> ::= <digit> | { <digit> }
+# <rdlength> ::= <digit> | { <digit> }
+# <rdata> ::= <address> |
+# <nsdname> |
+# <cname> |
+# <preference> <exchange> |
+# <ptrdname> |
+# ...
+
+0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com A IN
+1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com A IN
+2 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com A IN
+5 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com A IN
+8 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.M.example.com A IN
+9 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-a-record.example.com A IN
+
+10 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.NS.example.com A IN
+11 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.nS.example.com A IN
+12 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com A IN
+15 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.multiple-type-ns-record.example.com a IN
+
+20 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C.example.com A IN
+21 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C.example.com CNAME IN
+22 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C.example.com MX IN
+23 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C.example.com ANY IN
+
+24 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C0.example.com A IN
+25 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C0.example.com CNAME IN
+26 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C0.example.com MX IN
+27 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C0.example.com ANY IN
+
+28 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C10.example.com A IN
+29 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C10.example.com CNAME IN
+30 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C10.example.com MX IN
+31 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C10.example.com ANY IN
+
+32 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C36.example.com A IN
+33 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C36.example.com CNAME IN
+34 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C36.example.com MX IN
+35 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C36.example.com ANY IN
+
+36 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.c.Example.coM A IN
+37 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.c.Example.coM CNAME IN
+38 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.c.Example.coM MX IN
+39 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.c.Example.coM ANY IN
+
+40 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com A IN
+41 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com CNAME IN
+42 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com MX IN
+43 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com ANY IN
+
+
+60 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C0.name1.cn A IN
+61 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.C12.name1.cn A IN
+
+70 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example.com SOA IN
+71 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example.com ANY IN
+
+72 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example1.com SOA IN
+73 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example1.com A IN
+74 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example1.com NS IN
+75 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example1.com ANY IN
+
+76 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example2.com SOA IN
+77 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example2.com A IN
+78 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example2.com NS IN
+79 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example2.com ANY IN
+
+80 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example3.com SOA IN
+81 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example3.com A IN
+82 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example3.com NS IN
+83 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example3.com ANY IN
+
+84 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example4.com SOA IN
+85 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example4.com A IN
+86 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example4.com NS IN
+87 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example4.com ANY IN
+
+88 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example13.com SOA IN
+89 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example13.com A IN
+90 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example13.com NS IN
+91 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example13.com ANY IN
+
+92 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.noexist.example.com A IN
+93 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.noexist.example.com ANY IN
+94 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.noexist.example.com NS IN
+
+95 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example9.com SOA IN
+96 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example9.com ns IN
+97 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example9.com ANY IN
+
+98 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.noexist.example.com SOA IN
+99 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.noexist.noexist SOA IN
+
+100 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.1.1.10.10.in-addr.arpA PTR IN
+101 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com PTR IN
+102 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.1.1.10.10.in-addr.arpa PTR IN
+103 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.examPle.com PTR IN
+104 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com PTR IN
+107 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.1.2.168.192.in-addr.arpa PTR IN
+108 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-a-record.example.com PTR IN
+
+110 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com MX IN
+111 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com MX IN
+112 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com MX IN
+115 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-mx-record.example.com MX IN
+
+120 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com TXT IN
+121 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com TXT IN
+122 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com TXT IN
+125 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-txt-record.example.com TXT IN
+
+130 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com AAAA IN
+131 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com AAAA IN
+132 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com AAAA IN
+135 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-aaaa-record.example.com AAAA IN
+
+140 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com NAPTR IN
+141 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com NAPTR IN
+142 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com NAPTR IN
+145 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.2.1.2.1.5.5.5.0.7.7.1.e164.arpa NAPTR IN
+146 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.b.e164.arpa NAPTR IN
+147 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.6.8.e164.arpa NAPTR IN
+
+#150 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com A6 IN
+#151 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com A6 IN
+#152 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com A6 IN
+#155 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-a6-record.example.com A6 IN
+
+# case 160-163
+160 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com DNAME IN
+161 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com ANY IN
+162 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.A.example.com A IN
+163 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.A.example.com ANY IN
+
+# case 164-167
+164 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.exAmple.com DNAME IN
+165 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.exAmple.com ANY IN
+166 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.a.exAmple.com A IN
+167 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.a.exAmple.com ANY IN
+
+# case 168-171
+168 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com DNAME IN
+169 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com ANY IN
+170 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com A IN
+171 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com ANY IN
+
+# case 180-195
+180 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.d0.example.com a IN
+181 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.d1.example.com a IN
+182 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.d2.example.com a IN
+183 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.d4.example.com a IN
+184 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.d5.example.com a IN
+185 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.ns.d0.example.com a IN
+186 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.a.b.d1.example.com a IN
+187 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.a.b.d1.example.com mx IN
+
+188 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.d10.example.com a IN
+189 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.d20.example.com a IN
+190 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.d30.example.com a IN
+191 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.d40.example.com a IN
+192 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.d50.example.com a IN
+193 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.c45.example.com any IN
+194 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.d45.example.com any IN
+195 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.noexist.d45.example.com any IN
+196 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.d45.example.com any IN
+
+197 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.d72.example.com a IN
+198 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.d1.example.com a IN
+
+# case 200-205
+200 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com WKS IN
+201 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com WKS IN
+202 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com WKS IN
+205 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-wks-record.example.com WKS IN
+
+# case 210-215
+210 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com HINFO IN
+211 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com HINFO IN
+212 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com HINFO IN
+215 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-hinfo-record.example.com HINFO IN
+
+# case 220-225
+220 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com MINFO IN
+221 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com MINFO IN
+222 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com MINFO IN
+225 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-minfo-record.example.com MINFO IN
+
+# case 230-235
+230 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com NSAP IN
+231 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com NSAP IN
+232 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com NSAP IN
+235 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-nsap-record.example.com NSAP IN
+
+# case 240-245
+240 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com PX IN
+241 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com PX IN
+242 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com PX IN
+245 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-px-record.example.com PX IN
+
+# case 250-255
+250 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com LOC IN
+251 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com LOC IN
+252 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com LOC IN
+255 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-loc-record.example.com LOC IN
+
+# case 260-265
+260 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com SRV IN
+261 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com SRV IN
+262 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com SRV IN
+265 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-srv-record.example.com SRV IN
+
+# case 270-275
+270 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com KX IN
+271 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com KX IN
+272 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com KX IN
+275 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-kx-record.example.com KX IN
+
+# case 280-285
+280 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com CERT IN
+281 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com CERT IN
+282 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com CERT IN
+285 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-cert-record.example.com CERT IN
+
+# case 290-295
+290 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com APL IN
+291 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com APL IN
+292 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com APL IN
+295 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-apl-record.example.com APL IN
+
+# case 300-305
+300 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com SSHFP IN
+301 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com SSHFP IN
+302 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com SSHFP IN
+305 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-sshfp-record.example.com SSHFP IN
+
+# case 310-315
+310 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com IPSECKEY IN
+311 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com IPSECKEY IN
+312 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com IPSECKEY IN
+315 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-ipseckey-record.example.com IPSECKEY IN
+
+# case 320-325
+#320 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com DHCID IN
+#321 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com DHCID IN
+#322 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com DHCID IN
+#325 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-dhcid-record.example.com DHCID IN
+
+# case 330-335
+330 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.A.example.com SPF IN
+331 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.a.example.com SPF IN
+332 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com SPF IN
+335 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.multiple-type-spf-record.example.com SPF IN
+
+360 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.0.example.com A IN
+361 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.9.example.com A IN
+
+370 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.sub-cname.example.com A IN
+371 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.www.sub-dname.example.com A IN
+
+381 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.type-a-answer.toobigudp.com A IN
+382 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.type-a-authority.toobigudp.com A IN
+383 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.toobigudp.com NS IN
+384 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.type-txt-answer.toobigudp.com TXT IN
+385 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.type-txt-authority.toobigudp.com TXT IN
+386 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.type-cname-answer.toobigudp.com A IN
+387 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.type-cname-answer.toobigudp.com TXT IN
+
+390 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example.com DNSKEY IN
+391 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example.com RRSIG IN
+392 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example.com NSEC IN
+393 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.example.com DS IN
+#0x1000 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.version.bind txt CH
+#0x1001 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.hostname.bind txt CH
+#0x1002 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.id.server txt CH
+#0x1003 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.authors.bind txt CH
+#0x1000 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.version.bind txt CH
+#0x1001 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.hostname.bind txt CH
+#0x1002 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.id.server txt CH
+#0x1003 0 0 0 0 1 0 0 0 0 0 1 0 0 0 non-terminal.authors.bind txt CH
diff --git a/tools/query_cmp/queries/dquery01_nxdomain b/tools/query_cmp/queries/dquery01_nxdomain
new file mode 100644
index 0000000..aed1ceb
--- /dev/null
+++ b/tools/query_cmp/queries/dquery01_nxdomain
@@ -0,0 +1,316 @@
+# Fields Description
+#
+#query:ID QR OPCODE AA TC RD RA Z AD CD RCODE QDCOUNT ANCOUNT NSCOUNT ARCOUNT nxdomain.QNAME QTYPE QCLASS
+#response:ID QR OPCODE AA TC RD RA Z AD CD RCODE QDCOUNT ANCOUNT NSCOUNT ARCOUNT nxdomain.QNAME QTYPE QCLASS
+# <answer> := <rr1> .. <rrN>
+# <rr> := NAME TYPE CLASS TTL RDLENGTH <rdata>
+# <rdata> := ADDRESS |
+# NSDNAME |
+# MNAME RNAME SERIAL REFRESH RETRY EXPIRE MINIMUM |
+# ...
+# <authority> := <rr1> .. <rrN>
+# <additional> := <rr1> .. <rrN>
+#
+#
+#
+# Description in BNF (http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form)
+# <query> ::= <header> <question>
+# <header> ::= <ID> <QR> <OPCODE> <AA> <TC> <RD> <RA> <Z> <AD> <CD> <RCODE>
+# <QDCOUNT> <ANCOUNT> <NSCOUNT> <ARCOUNT>
+# <question> ::= <QNAME> <QTYPE> <QCLASS>
+#
+# <response> ::= <header> <question> <answer> <authority> <additional>
+# <answer> ::= <rrset>
+# <authority> ::= <rrset>
+# <additional> ::= <rrset>
+# <rrset> ::= { <rr> }
+# <rr> ::= <name> <type> <class> <ttl> <rdlength> <rdata>
+# <name> ::= <subdomain> | ""
+# <subdomain> ::= <label> | <subdomain> "." <label>
+# <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
+# <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
+# <let-dig-hyp> ::= <let-dig> | "-"
+# <let-dig> ::= <letter> | <digit>
+# <letter> ::= "a" | .. | "z" | "A" | .. | "Z"
+# <digit> ::= "0" | .. | "9"
+# <type> ::= A | NS | CNAME | SOA | PTR | MX | nxdomain...
+# <class> ::= IN | CH | HS | CS
+# <ttl> ::= <digit> | { <digit> }
+# <rdlength> ::= <digit> | { <digit> }
+# <rdata> ::= <address> |
+# <nsdname> |
+# <cname> |
+# <preference> <exchange> |
+# <ptrdname> |
+# ...
+
+0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com A IN
+1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com A IN
+2 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com A IN
+5 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com A IN
+8 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.M.example.com A IN
+9 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-a-record.example.com A IN
+
+10 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.NS.example.com A IN
+11 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.nS.example.com A IN
+12 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com A IN
+15 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.multiple-type-ns-record.example.com a IN
+
+20 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C.example.com A IN
+21 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C.example.com CNAME IN
+22 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C.example.com MX IN
+23 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C.example.com ANY IN
+
+24 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C0.example.com A IN
+25 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C0.example.com CNAME IN
+26 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C0.example.com MX IN
+27 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C0.example.com ANY IN
+
+28 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C10.example.com A IN
+29 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C10.example.com CNAME IN
+30 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C10.example.com MX IN
+31 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C10.example.com ANY IN
+
+32 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C36.example.com A IN
+33 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C36.example.com CNAME IN
+34 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C36.example.com MX IN
+35 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C36.example.com ANY IN
+
+36 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.c.Example.coM A IN
+37 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.c.Example.coM CNAME IN
+38 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.c.Example.coM MX IN
+39 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.c.Example.coM ANY IN
+
+40 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com A IN
+41 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com CNAME IN
+42 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com MX IN
+43 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com ANY IN
+
+60 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C0.name1.cn A IN
+61 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.C12.name1.cn A IN
+
+70 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example.com SOA IN
+71 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example.com ANY IN
+
+72 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example1.com SOA IN
+73 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example1.com A IN
+74 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example1.com NS IN
+75 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example1.com ANY IN
+
+76 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example2.com SOA IN
+77 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example2.com A IN
+78 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example2.com NS IN
+79 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example2.com ANY IN
+
+80 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example3.com SOA IN
+81 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example3.com A IN
+82 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example3.com NS IN
+83 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example3.com ANY IN
+
+84 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example4.com SOA IN
+85 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example4.com A IN
+86 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example4.com NS IN
+87 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example4.com ANY IN
+
+88 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example13.com SOA IN
+89 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example13.com A IN
+90 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example13.com NS IN
+91 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example13.com ANY IN
+
+92 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.noexist.example.com A IN
+93 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.noexist.example.com ANY IN
+94 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.noexist.example.com NS IN
+
+95 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example9.com SOA IN
+96 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example9.com ns IN
+97 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example9.com ANY IN
+
+98 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.noexist.example.com SOA IN
+99 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.noexist.noexist SOA IN
+
+100 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.1.1.10.10.in-addr.arpA PTR IN
+101 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com PTR IN
+102 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.1.1.10.10.in-addr.arpa PTR IN
+103 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.examPle.com PTR IN
+104 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com PTR IN
+107 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.1.2.168.192.in-addr.arpa PTR IN
+108 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-a-record.example.com PTR IN
+
+110 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com MX IN
+111 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com MX IN
+112 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com MX IN
+115 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-mx-record.example.com MX IN
+
+120 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com TXT IN
+121 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com TXT IN
+122 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com TXT IN
+125 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-txt-record.example.com TXT IN
+
+130 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com AAAA IN
+131 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com AAAA IN
+132 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com AAAA IN
+135 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-aaaa-record.example.com AAAA IN
+
+140 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com NAPTR IN
+141 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com NAPTR IN
+142 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com NAPTR IN
+145 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.2.1.2.1.5.5.5.0.7.7.1.e164.arpa NAPTR IN
+146 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.b.e164.arpa NAPTR IN
+147 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.6.8.e164.arpa NAPTR IN
+
+#150 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com A6 IN
+#151 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com A6 IN
+#152 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com A6 IN
+#155 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-a6-record.example.com A6 IN
+
+# case 160-163
+160 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com DNAME IN
+161 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com ANY IN
+162 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.A.example.com A IN
+163 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.A.example.com ANY IN
+
+# case 164-167
+164 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.exAmple.com DNAME IN
+165 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.exAmple.com ANY IN
+166 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.a.exAmple.com A IN
+167 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.a.exAmple.com ANY IN
+
+# case 168-171
+168 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com DNAME IN
+169 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com ANY IN
+170 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com A IN
+171 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com ANY IN
+
+# case 180-195
+180 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.d0.example.com a IN
+181 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.d1.example.com a IN
+182 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.d2.example.com a IN
+183 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.d4.example.com a IN
+184 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.d5.example.com a IN
+185 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.ns.d0.example.com a IN
+186 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.a.b.d1.example.com a IN
+187 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.a.b.d1.example.com mx IN
+
+188 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.d10.example.com a IN
+189 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.d20.example.com a IN
+190 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.d30.example.com a IN
+191 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.d40.example.com a IN
+192 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.d50.example.com a IN
+193 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.c45.example.com any IN
+194 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.d45.example.com any IN
+195 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.noexist.d45.example.com any IN
+196 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.d45.example.com any IN
+
+197 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.d72.example.com a IN
+198 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.d1.example.com a IN
+
+# case 200-205
+200 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com WKS IN
+201 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com WKS IN
+202 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com WKS IN
+205 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-wks-record.example.com WKS IN
+
+# case 210-215
+210 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com HINFO IN
+211 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com HINFO IN
+212 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com HINFO IN
+215 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-hinfo-record.example.com HINFO IN
+
+# case 220-225
+220 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com MINFO IN
+221 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com MINFO IN
+222 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com MINFO IN
+225 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-minfo-record.example.com MINFO IN
+
+# case 230-235
+230 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com NSAP IN
+231 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com NSAP IN
+232 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com NSAP IN
+235 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-nsap-record.example.com NSAP IN
+
+# case 240-245
+240 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com PX IN
+241 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com PX IN
+242 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com PX IN
+245 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-px-record.example.com PX IN
+
+# case 250-255
+250 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com LOC IN
+251 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com LOC IN
+252 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com LOC IN
+255 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-loc-record.example.com LOC IN
+
+# case 260-265
+260 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com SRV IN
+261 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com SRV IN
+262 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com SRV IN
+265 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-srv-record.example.com SRV IN
+
+# case 270-275
+270 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com KX IN
+271 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com KX IN
+272 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com KX IN
+275 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-kx-record.example.com KX IN
+
+# case 280-285
+280 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com CERT IN
+281 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com CERT IN
+282 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com CERT IN
+285 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-cert-record.example.com CERT IN
+
+# case 290-295
+290 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com APL IN
+291 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com APL IN
+292 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com APL IN
+295 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-apl-record.example.com APL IN
+
+# case 300-305
+300 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com SSHFP IN
+301 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com SSHFP IN
+302 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com SSHFP IN
+305 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-sshfp-record.example.com SSHFP IN
+
+# case 310-315
+310 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com IPSECKEY IN
+311 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com IPSECKEY IN
+312 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com IPSECKEY IN
+315 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-ipseckey-record.example.com IPSECKEY IN
+
+# case 320-325
+#320 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com DHCID IN
+#321 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com DHCID IN
+#322 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com DHCID IN
+#325 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-dhcid-record.example.com DHCID IN
+
+# case 330-335
+330 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.A.example.com SPF IN
+331 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.a.example.com SPF IN
+332 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com SPF IN
+335 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.multiple-type-spf-record.example.com SPF IN
+
+360 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.0.example.com A IN
+361 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.9.example.com A IN
+
+370 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.sub-cname.example.com A IN
+371 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.www.sub-dname.example.com A IN
+
+381 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.type-a-answer.toobigudp.com A IN
+382 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.type-a-authority.toobigudp.com A IN
+383 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.toobigudp.com NS IN
+384 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.type-txt-answer.toobigudp.com TXT IN
+385 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.type-txt-authority.toobigudp.com TXT IN
+386 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.type-cname-answer.toobigudp.com A IN
+387 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.type-cname-answer.toobigudp.com TXT IN
+
+390 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example.com DNSKEY IN
+391 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example.com RRSIG IN
+392 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example.com NSEC IN
+393 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.example.com DS IN
+#0x1000 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.version.bind txt CH
+#0x1001 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.hostname.bind txt CH
+#0x1002 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.id.server txt CH
+#0x1003 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.authors.bind txt CH
+#0x1000 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.version.bind txt CH
+#0x1001 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.hostname.bind txt CH
+#0x1002 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.id.server txt CH
+#0x1003 0 0 0 0 1 0 0 0 0 0 1 0 0 0 nxdomain.authors.bind txt CH
diff --git a/tools/query_cmp/src/lib/compare_rrset.py b/tools/query_cmp/src/lib/compare_rrset.py
new file mode 100755
index 0000000..737d761
--- /dev/null
+++ b/tools/query_cmp/src/lib/compare_rrset.py
@@ -0,0 +1,285 @@
+#!/usr/bin/python3
+
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+import struct
+from pydnspp import *
+
+# Some position parameters used in the formatted output report.
+POS_TOTAL = 80
+POS_TITLE = 14
+POS_LEFT = int((POS_TOTAL - POS_TITLE) / 2)
+
+def get_header_field(msg):
+ header = {}
+ header['id'] = msg.get_qid()
+ header['qr'] = msg.get_header_flag(Message.HEADERFLAG_QR)
+ header['opcode'] = msg.get_opcode()
+ header['aa'] = msg.get_header_flag(Message.HEADERFLAG_AA)
+ header['tc'] = msg.get_header_flag(Message.HEADERFLAG_TC)
+ header['rd'] = msg.get_header_flag(Message.HEADERFLAG_RD)
+ header['ra'] = msg.get_header_flag(Message.HEADERFLAG_RA)
+ header['ad'] = msg.get_header_flag(Message.HEADERFLAG_AD)
+ header['cd'] = msg.get_header_flag(Message.HEADERFLAG_CD)
+ #header['rcode'] = dns.rcode.from_flags(msg.flags, msg.ednsflags)
+ header['rcode'] = msg.get_rcode()
+
+ header['qdcount'] = msg.get_rr_count(Message.SECTION_QUESTION)
+ header['ancount'] = msg.get_rr_count(Message.SECTION_ANSWER)
+ header['nscount'] = msg.get_rr_count(Message.SECTION_AUTHORITY)
+ header['arcount'] = msg.get_rr_count(Message.SECTION_ADDITIONAL)
+
+ return header
+
+def header_cmp(buf, msg1, msg2, diff):
+ """ Compare the header of msg1 and msg2.
+
+ @param buf: the formatted difference for output.
+ @type buf: dict
+ @param diff: the key is each flag in the header, the value is
+ True for different and False for same
+ @type diff: dict
+ """
+
+ header1 = get_header_field(msg1)
+ header2 = get_header_field(msg2)
+
+ list = ['id', 'qr', 'opcode', 'aa', 'tc', 'rd', \
+ 'ra', 'ad', 'cd', 'rcode', 'qdcount', 'ancount', \
+ 'nscount', 'arcount']
+
+ for header in list:
+ diff[header] = header1[header] != header2[header]
+
+ buf['header'] = ''
+ for key in list:
+ if diff[key]:
+ buf['header'] = buf['header'] + \
+ '%-*s%-*s%-*s\n' % (POS_TITLE, key, \
+ POS_LEFT, header1[key], POS_LEFT, \
+ header2[key])
+
+ for key in diff.keys():
+ if diff[key]: return(False)
+
+ return(True)
+
+
+def output(sect, buf, rrset, isleft):
+ """ Format and return the rrset according to which section
+ and which message it belongs to.
+
+ @param sect: section name
+ @type sect: string
+ @param buf: passed by parameter, to store the formatted string
+ @param buf: dict
+ @param rrset: the rrset to be formatted
+ @type rrset: RRset
+ @param isleft: to be compared, the report has the content corresponding to
+ the 1st message printed on the left, while the one corresponding
+ to the 2nd message on the right. This is a flag to which one it is.
+ @type isleft: BOOL
+ """
+
+ if not sect in buf:
+ buf[sect] = ''
+ if sect == 'question':
+ buf[sect] = buf[sect] + '%-*s' % (POS_TITLE, 'name')
+ if not isleft:
+ buf[sect] = buf[sect] + ' ' * POS_LEFT
+ buf[sect] = buf[sect] + rrset.get_name().to_text() + "\n"
+
+ buf[sect] = buf[sect] + '%-*s' % (POS_TITLE, 'class')
+ if not isleft:
+ buf[sect] = buf[sect] + ' ' * POS_LEFT
+ buf[sect] = buf[sect] + rrset.get_class().to_text() + "\n"
+
+ buf[sect] = buf[sect] + '%-*s' % (POS_TITLE, 'type')
+ if not isleft:
+ buf[sect] = buf[sect] + ' ' * POS_LEFT
+ buf[sect] = buf[sect] + rrset.get_type().to_text() + "\n"
+
+ else:
+ buf[sect] = buf[sect] + '%-*s' % (POS_TITLE, 'ttl')
+ if not isleft:
+ buf[sect] = buf[sect] + ' ' * int(POS_LEFT)
+ buf[sect] = buf[sect] + rrset.get_ttl().to_text() + "\n"
+
+ buf[sect] = buf[sect] + '%-*s' % (POS_TITLE, 'name')
+ if not isleft:
+ buf[sect] = buf[sect] + ' ' * int(POS_LEFT)
+ buf[sect] = buf[sect] + rrset.get_name().to_text() + "\n"
+
+ buf[sect] = buf[sect] + '%-*s' % (POS_TITLE, 'class')
+ if not isleft:
+ buf[sect] = buf[sect] + ' ' * int(POS_LEFT)
+ buf[sect] = buf[sect] + rrset.get_class().to_text() + "\n"
+
+ buf[sect] = buf[sect] + '%-*s' % (POS_TITLE, 'type')
+ if not isleft:
+ buf[sect] = buf[sect] + ' ' * int(POS_LEFT)
+ buf[sect] = buf[sect] + rrset.get_type().to_text() + "\n"
+
+ buf[sect] = buf[sect] + '%-*s' % (POS_TITLE, 'rdata')
+
+ i = 0
+ rdata = rrset.get_rdata()
+ for item in rdata:
+ if i > 0:
+ buf[sect] = buf[sect] + ' ' * POS_TITLE
+ if not isleft:
+ buf[sect] = buf[sect] + ' ' * POS_LEFT
+ buf[sect] = buf[sect] + item.to_text() + "\n"
+ i = i + 1
+
+ buf[sect] = buf[sect] + "\n"
+
+def array_cmp(sectname, buf, rlist1, rlist2):
+ """ Compare each entry of the question section of rlist1 and rlist2.
+ Compare each RRset of the sectname section (can be answer, authority,
+ additional) of rlist1 and rlist2.
+
+ @param buf: store the formatted output of difference
+ @type: dict
+ """
+
+ diff_flag = True
+ while len(rlist1) > 0:
+ rr1 = rlist1.pop()
+ find2 = False
+ for rr2 in rlist2:
+ if sectname == 'question':
+ res = question_cmp(rr1, rr2)
+ else:
+ res = rr_cmp(rr1, rr2)
+ if res:
+ find2 = True
+ rlist2.remove(rr2)
+ break
+ if not find2:
+ output(sectname, buf, rr1, True)
+ diff_flag = False
+
+ while len(rlist2) > 0:
+ rr2 = rlist2.pop()
+ output(sectname, buf, rr2, False)
+ diff_flag = False
+ return(diff_flag)
+
+def question_cmp(rra, rrb):
+ if rra.get_name() != rrb.get_name(): return(False)
+ if rra.get_class() != rrb.get_class(): return(False)
+ if rra.get_type() != rrb.get_type(): return(False)
+ return(True)
+
+def rr_cmp(rra, rrb):
+ """ Compare two rrsets: rra and rrb """
+
+ if rra.get_name() != rrb.get_name(): return(False)
+ if rra.get_class() != rrb.get_class(): return(False)
+ if rra.get_type() != rrb.get_type(): return(False)
+ if rra.get_ttl() != rrb.get_ttl(): return(False)
+ rdata_a = rra.get_rdata()
+ rdata_b = rrb.get_rdata()
+ rdata_al = len(rdata_a)
+ rdata_bl = len(rdata_b)
+
+ if rdata_al != rdata_bl:
+ return(False)
+
+ cmp_flag = False
+ # Iterate rdata in rrset a, find if there is same rdata in rrset b
+ for ra in rdata_a:
+ for rb in rdata_b:
+ if ra.to_text() == rb.to_text():
+ cmp_flag = True
+ rdata_b.remove(rb)
+ break
+ if not cmp_flag:
+ break
+ return(cmp_flag)
+
+def resp_casecmp(msg1, msg2, num):
+ """ Compare two response message, and print the different part
+ in formatted report.
+
+ @param msg1: 1st response message
+ @type msg1: Message
+ @param msg2: 2nd response message
+ @type msg2: Message
+ @param num: the id of the query case in the 1st column of the input file.
+ Used by output report to locate the specified query case.
+ @type num: int
+ """
+
+ diff = {}
+ buf = {}
+
+ query = msg1.get_question()[0]
+
+ res_hdr = header_cmp(buf, msg1, msg2, diff)
+ res_ques = array_cmp('question', buf, \
+ msg1.get_question(), msg2.get_question())
+ res_ans = array_cmp('answer', buf, \
+ msg1.get_section(Message.SECTION_ANSWER), \
+ msg2.get_section(Message.SECTION_ANSWER))
+ res_auth = array_cmp('authority', buf, \
+ msg1.get_section(Message.SECTION_AUTHORITY), \
+ msg2.get_section(Message.SECTION_AUTHORITY))
+ res_addi = array_cmp('additional', buf, \
+ msg1.get_section(Message.SECTION_ADDITIONAL), \
+ msg2.get_section(Message.SECTION_ADDITIONAL))
+
+ # If there are any differnt comparisons in the sections above, print the details
+ # contained in buf formattedly.
+ if not res_hdr or not res_ques or not res_ans or not res_auth or not res_addi:
+ print('=' * 30, ' BEGIN QUERY %s ' % num, '=' * 30, sep='')
+ print('%-*s%-*s%-*s' % (POS_TITLE, '', int(POS_LEFT), 'SERVER1', \
+ POS_TOTAL - POS_TITLE, 'SERVER2'))
+ print('-' * 80)
+ print('Query: ', query.to_text(), sep='')
+ print('-' * 80)
+
+ if not res_hdr:
+ print(buf['header'])
+ print('-' * 80)
+
+ if not res_ques:
+ print("QUESTION")
+ print('-' * 80)
+ print(buf['question'])
+
+ if not res_ans:
+ print("ANSWER")
+ print('-' * 80)
+ print(buf['answer'])
+
+ if not res_auth:
+ print("AUTHORITY")
+ print('-' * 80)
+ print(buf['authority'])
+
+ if not res_addi:
+ print("ADDITIONAL")
+ print('-' * 80)
+ print(buf['additional'])
+
+ print('=' * 30, ' END QUERY %s ' % num, '=' * 30, sep='')
+ print("\n\n")
+
+ return False
+
+ return True
+
diff --git a/tools/query_cmp/src/lib/handledns.py b/tools/query_cmp/src/lib/handledns.py
new file mode 100755
index 0000000..e33ce9e
--- /dev/null
+++ b/tools/query_cmp/src/lib/handledns.py
@@ -0,0 +1,284 @@
+#!/usr/bin/python3
+
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+import errno
+import sys
+import select
+import socket
+import struct
+import time
+
+from pydnspp import *
+
+RECV_BUFSIZE = 65536
+
+def _wait_for(ir, iw, ix, expiration):
+ done = False
+ while not done:
+ if expiration is None:
+ timeout = None
+ else:
+ timeout = expiration - time.time()
+ if timeout <= 0.0:
+ raise socket.timeout
+ try:
+ if timeout is None:
+ (r,w,x) = select.select(ir,iw,ix)
+ else:
+ (r,w,x) = select.select(ir,iw,ix,timeout)
+ except select.error as e:
+ if e.args[0] != errno.EINTR:
+ raise e
+ else:
+ done = True
+ if len(r) == 0 and len(w) == 0 and len(x) == 0:
+ raise socket.timeout
+
+def _wait_for_readable(s,expiration):
+ _wait_for([s],[],[s],expiration)
+
+def _compute_expiration(timeout):
+ if timeout is None:
+ return None
+ else:
+ return time.time() + timeout
+
+def _send_udp(q, where, timeout=None, port=53, source=None, source_port=0):
+ """ Return the response obtained after sending a query via UDP.
+ Refered to dnspython source code. """
+
+ qwire = MessageRenderer()
+ q.to_wire(qwire)
+ if source is not None:
+ source = (source, source_port)
+
+ udpCliSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
+
+ expiration = _compute_expiration(timeout)
+ if source is not None:
+ udpCliSock.bind(source)
+
+ dest = (where, port)
+ udpCliSock.sendto(qwire.get_data(), dest)
+
+ while True:
+ _wait_for_readable(udpCliSock, expiration)
+ rwire, r_addr = udpCliSock.recvfrom(RECV_BUFSIZE)
+ if dest[0] == r_addr[0] and dest[1:] == r_addr[1:]:
+ break
+ else:
+ sys.stderr.write('Got a respose from: %s instead of %s\n' % (r_addr, dest))
+
+ udpCliSock.close()
+
+ resp = Message(Message.PARSE)
+ resp.from_wire(rwire)
+
+ return resp
+
+def _connect(s, address):
+ try:
+ s.connect(address)
+ except socket.error as msg:
+ (exctype,value) = sys.exc_info()[:2]
+ if value.errno != errno.EINPROGRESS and \
+ value.errno != errno.EWOULDBLOCK and \
+ value.errno != errno.EALREADY:
+ raise value
+
+def _net_read(sock, count, expiration):
+ """ Read the specified number of bytes from sock. Keep trying until we
+ either get the desired amount, or we hit EOF.
+ A Timeout exception will be raised if the operation is not completed
+ by the expiration time.
+ """
+
+ msgdata = b''
+ while count > 0:
+ _wait_for_readable(sock, expiration)
+ data = sock.recv(count)
+ if not data:
+ return None
+
+ count -= len(data)
+ msgdata += data
+
+ return msgdata
+
+def _net_write(sock, data, expiration):
+ """ Write the specified data to the socket.
+ A Timeout exception will be raised if the operation is not completed
+ by the expiration time.
+ """
+ current = 0
+ l = len(data)
+ while current < 1:
+ _wait_for_writable(sock, expiration)
+ current += sock.send(data[current:])
+
+def _send_tcp(q, dest, timeout=None, dest_port=53, source=None, source_port=0):
+ """ Return the response obtained after sending a query via TCP.
+ Refered to dnspython source code """
+
+ qwire = MessageRenderer()
+ q.to_wire(qwire)
+
+ if source is not None:
+ source = (source, source_port)
+ dest = (dest, dest_port)
+ tcpCliSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
+
+ expiration = _compute_expiration(timeout)
+ tcpCliSock.setblocking(False)
+ if source is not None:
+ tcpCliSock.bind(source)
+ _connect(tcpCliSock, dest)
+
+ wire_s = qwire.get_data()
+ l = len(wire_s)
+
+ tcpmsg = struct.pack("!H", l) + wire_s
+ _net_write(tcpCliSock, tcpmsg, expiration)
+ ldata = _net_read(tcpCliSock, 2, expiration)
+ (l,) = struct.unpack("!H", ldata)
+ res_wire = _net_read(tcpCliSock, l, expiration)
+ tcpCliSock.close()
+
+ resp = Message(Message.PARSE)
+ resp.from_wire(res_wire)
+
+ return resp
+
+def send_req(query, server, port=53, timeout=5):
+ """ Return the response message obtained after
+ sending the query.
+
+ @param query: the query readed from input file
+ @type query: dict
+ @param server: the testee server ip address
+ @type server: string
+ @param port: the testee server listening port. The default is 53.
+ @type port: int
+ @param timeout: the number of seconds to wait before the query times out.
+ The default is 5.
+ @type timeout: float
+ """
+
+ qname = query["qname"]
+ qtype = query["qtype"]
+ qclass = query["qclass"]
+ edns = query["edns"]
+ dnssec = query["dnssec"]
+ qheader = query['header']
+ protocol = query['protocol']
+
+ msg = Message(Message.RENDER)
+ msg.set_qid(int(qheader['id']))
+ msg.set_opcode(Opcode.QUERY())
+ msg.set_rcode(Rcode(int(qheader['rcode'])))
+
+ if qheader['qr'] == 1:
+ msg.set_header_flag(Message.HEADERFLAG_QR)
+ if qheader['aa'] == 1:
+ msg.set_header_flag(Message.HEADERFLAG_AA)
+ if qheader['tc'] == 1:
+ msg.set_header_flag(Message.HEADERFLAG_TC)
+ if qheader['rd'] == 1:
+ msg.set_header_flag(Message.HEADERFLAG_RD)
+ if qheader['ra'] == 1:
+ msg.set_header_flag(Message.HEADERFLAG_RA)
+ if qheader['ad'] == 1:
+ msg.set_header_flag(Message.HEADERFLAG_AD)
+ if qheader['cd'] == 1:
+ msg.set_header_flag(Message.HEADERFLAG_CD)
+
+ try:
+ msg.add_question(Question(Name(qname), \
+ RRClass(qclass), RRType(qtype)))
+ except InvalidRRType as e:
+ sys.stderr.write('Unrecognized RR queryeter string: %s\n' % qtype)
+ return None
+
+ if edns == 1 or dnssec == 1:
+ edns_conf = EDNS()
+ payload = query['payload']
+ edns_conf.set_udp_size(payload)
+
+ if dnssec == 1:
+ edns_conf.set_dnssec_awareness(True)
+ else:
+ edns_conf.set_dnssec_awareness(False)
+
+ msg.set_edns(edns_conf)
+
+ port = int(port)
+ if protocol == 'udp':
+ resp = _send_udp(msg, server, timeout, port)
+ else:
+ resp = _send_tcp(msg, server, timeout, port)
+
+ return resp
+
+
+def main():
+ query = {}
+ query['qname'] = "A.example.com"
+ query['qtype'] = "ANY"
+ query['qclass'] = "IN"
+ query["edns"] = 1
+ query["dnssec"] = 1
+ query["protocol"] = 'tcp'
+ query["payload"] = 4096
+
+ query['header'] = {}
+ query['header']['id'] = 0
+ query['header']['qr'] = 0
+ query['header']['opcode'] = 0
+ query['header']['aa'] = 0
+ query['header']['tc'] = 0
+ query['header']['rd'] = 1
+ query['header']['ra'] = 0
+ query['header']['z'] = 0
+ query['header']['ad'] = 0
+ query['header']['cd'] = 0
+ query['header']['rcode'] = 0
+ query['header']['qdcount'] = 0
+ query['header']['ancount'] = 0
+ query['header']['nscount'] = 0
+ query['header']['arcount'] = 0
+
+ resp = send_req(query, "218.241.108.124", "4040")
+
+ if resp == None:
+ print('timeout')
+ exit(1)
+
+ print('qid -----')
+ print(resp.get_qid())
+
+ rrset = resp.get_section(Message.SECTION_ANSWER)[0]
+ print('name-----')
+ print(rrset.get_name())
+ print('type')
+ print(rrset.get_type())
+ print('class-----')
+ print(rrset.get_class())
+ print(rrset.get_ttl())
+ rdata = rrset.get_rdata()
+ print(rdata[0].to_text())
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/query_cmp/src/lib/read_query.py b/tools/query_cmp/src/lib/read_query.py
new file mode 100755
index 0000000..01d64fe
--- /dev/null
+++ b/tools/query_cmp/src/lib/read_query.py
@@ -0,0 +1,93 @@
+#!/usr/bin/python3
+
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+import re
+import sys
+
+def read_query(file, querylist):
+ fh = open(file)
+ while True:
+ query = {}
+ query['header'] = {}
+
+ line = fh.readline()
+ if not line: break
+ line = line.rstrip('\n')
+
+ if re.search('^#', line): continue
+ if re.search('^\s*$', line): continue
+
+ fields = line.split(' ')
+ query['header']['id'] = fields.pop(0)
+ query['header']['qr'] = fields.pop(0)
+ query['header']['opcode'] = int(fields.pop(0))
+ query['header']['aa'] = fields.pop(0)
+ query['header']['tc'] = fields.pop(0)
+ query['header']['rd'] = fields.pop(0)
+ query['header']['ra'] = fields.pop(0)
+ query['header']['z'] = fields.pop(0)
+ query['header']['ad'] = fields.pop(0)
+ query['header']['cd'] = fields.pop(0)
+ query['header']['rcode'] = fields.pop(0)
+ query['header']['qdcount'] = fields.pop(0)
+ query['header']['ancount'] = fields.pop(0)
+ query['header']['nscount'] = fields.pop(0)
+ query['header']['arcount'] = fields.pop(0)
+
+ if query['header']['opcode'] == 0:
+ get_qtuple(query, 'question', fields)
+
+ querylist.append(query)
+
+ fh.close()
+
+def get_qtuple(query, sectname, list):
+ if sectname == 'question':
+ count = int(query['header']['qdcount'])
+ item = {}
+ i = 0
+ while i < count:
+ query[sectname] = []
+ item['qname'] = list.pop(0)
+ item['qtype'] = list.pop(0)
+ item['qclass'] = list.pop(0)
+ query[sectname].append(item)
+ i += 1
+
+def print_query(querylist):
+ keylist = ['id', 'qr', 'opcode', 'aa', 'tc', 'rd', 'ra', 'z',
+ 'ad', 'cd', 'rcode', 'qdcount', 'ancount', 'nscount',
+ 'arcount']
+ for q in querylist:
+ for key in keylist:
+ print(q['header'][key], ' ')
+ print_question(q)
+
+def print_question(query):
+ i = 0
+
+ while i < len(query['question']):
+ print(query['question'][i]['qname'], \
+ query['question'][i]['qtype'], \
+ query['question'][i]['qclass'], \
+ sep=' ')
+ i += 1
+
+if __name__ == '__main__':
+ qlist = []
+ read_query(sys.argv[1], qlist)
+ print_query(qlist)
+
diff --git a/tools/query_cmp/src/query_two_server.py b/tools/query_cmp/src/query_two_server.py
new file mode 100755
index 0000000..13e234a
--- /dev/null
+++ b/tools/query_cmp/src/query_two_server.py
@@ -0,0 +1,102 @@
+#!/usr/bin/python3
+
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+import sys; sys.path.append('lib')
+from optparse import OptionParser
+
+from read_query import *
+import handledns
+import compare_rrset
+
+def getopt():
+ """
+ get options from user.
+ """
+ usage = "usage: %prog -f <file> -s <svr1> [-p <port1>] -t <svr2> [-q <port2>] [-e] [-u] [--bufsize] [--edns]"
+ parser = OptionParser(usage)
+ parser.add_option("-f", "--file", dest="filename",
+ help="specify the input data filename")
+ parser.add_option("-s", "--svr1", dest="server1",
+ help="specify the tested DNS server 1")
+ parser.add_option("-p",
+ help="specify the port of the tested DNS server 1, default is 53")
+ parser.add_option("-t", "--svr2", dest="server2",
+ help="specify the tested DNS server 2")
+ parser.add_option("-q",
+ help="specify the port of the tested DNS server 2, default is 53")
+ parser.add_option("-e", "--dnssec", action="store_true",
+ default=False, help="turn on dnssec")
+ parser.add_option("", "--edns", action="store_true",
+ default=False, help="turn on edns, if -e is set, --edns will lapse, it must be True")
+ parser.add_option("-u", "--udp", action="store_true", default=False,
+ help="if set, query by udp, otherwise by tcp, default is unset")
+ parser.add_option("", "--bufsize", default=4096,
+ help="if --edns is set, --bufsize specifies payload of edns0, default is 4096")
+
+
+ (options, args) = parser.parse_args()
+
+ if(options.filename == None or options.server1 == None or
+ options.server2 == None):
+ parser.print_help()
+ sys.exit(1)
+
+ return options
+
+def main():
+ opts = getopt()
+
+ qlist = []
+ read_query(opts.filename, qlist)
+
+ for q in qlist:
+ # initial query
+ query = {}
+ if opts.dnssec:
+ query['edns'] = 1
+ query['dnssec'] = 1
+ elif opts.edns:
+ query['edns'] = 1
+ query['dnssec'] = 0
+ else:
+ query['edns'] = 0
+ query['dnssec'] = 0
+
+ if opts.udp:
+ query['protocol'] = 'udp'
+ else:
+ query['protocol'] = 'tcp'
+
+ query['payload'] = opts.bufsize
+ query['qname'] = q['question'][0]['qname']
+ query['qtype'] = q['question'][0]['qtype']
+ query['qclass'] = q['question'][0]['qclass']
+ query['header'] = q['header']
+ id = q['header']['id']
+
+ # send the query to the 1st and 2nd server, and store the
+ # response in res1 and res2
+ res1 = handledns.send_req(query, opts.server1, opts.p)
+ res2 = handledns.send_req(query, opts.server2, opts.q)
+
+ if res1 != None and res2 != None:
+ # compare res1 and res2, print the different part.
+ res3 = compare_rrset.resp_casecmp(res1, res2, id)
+ else:
+ sys.stderr.write('Empty response.\n')
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/query_cmp/zonefile/example.com.txt b/tools/query_cmp/zonefile/example.com.txt
new file mode 100644
index 0000000..5154014
--- /dev/null
+++ b/tools/query_cmp/zonefile/example.com.txt
@@ -0,0 +1,1298 @@
+$TTL 86400
+@ IN SOA NS1.example.com. root.example.com. (
+ 2010091701 ; serial
+ 3600 ; refresh
+ 900 ; retry
+ 604800 ; expire
+ 3600 ; minimum
+ )
+ IN NS NS1.example.com.
+ IN NS NS2.demo.example.com.
+ IN NS NS3.noexist.cn.
+
+NS1 IN A 218.241.111.236
+NS2.demo IN A 218.241.108.15
+
+$ORIGIN example.com.
+minittl 100 IN A 10.10.1.1
+
+a IN A 192.168.1.10
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN A 192.168.1.10
+
+subdomain IN NS ns1.mysub.com.
+subdomain IN NS ns2.mysub.com.
+
+subdn IN NS ns.subdn.cn.
+
+NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA NS ns1.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ns1.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA A 192.168.2.1
+
+NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE NS ns1.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
+ns1.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE A 192.168.2.2
+N.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M IN NS ns1.N.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M
+ns1.N.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M IN A 192.168.2.3
+
+C IN CNAME A
+C0 IN CNAME C1
+C1 IN CNAME C2
+C2 IN CNAME C3
+C3 IN CNAME C4
+C4 IN CNAME C5
+C5 IN CNAME C6
+C6 IN CNAME C7
+C7 IN CNAME C8
+C8 IN CNAME C9
+C9 IN CNAME C10
+C10 IN CNAME C11
+C11 IN CNAME C12
+C12 IN CNAME C13
+C13 IN CNAME C14
+C14 IN CNAME C15
+C15 IN CNAME C16
+C16 IN CNAME C17
+C17 IN CNAME C18
+C18 IN CNAME C19
+C19 IN CNAME C20
+C20 IN CNAME C21
+C21 IN CNAME C22
+C22 IN CNAME C23
+C23 IN CNAME C24
+C24 IN CNAME C25
+
+C30 IN CNAME C31
+C31 IN CNAME C32
+C32 IN CNAME C33
+C33 IN CNAME C34
+C34 IN CNAME C35
+C35 IN CNAME C36
+C36 IN CNAME C37
+C37 IN CNAME C38
+C38 IN CNAME C39
+C39 IN CNAME C40
+C40 IN CNAME C41
+C41 IN CNAME C42
+C42 IN CNAME C43
+C43 IN CNAME C44
+C44 IN CNAME C45
+C45 IN CNAME C46
+C46 IN CNAME C47
+C47 IN CNAME C48
+C48 IN CNAME C49
+C49 IN CNAME C50
+C50 IN CNAME C51
+C51 IN CNAME A
+
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA CNAME AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+
+A2 IN A 192.168.1.12
+B1 IN CNAME A2
+
+a IN PTR 1.1.226.159.cnnic.cn.
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN PTR 2.1.226.159.cnnic.cn.
+multiple-type-ptr-record IN PTR 192.168.2.1
+multiple-type-ptr-record IN PTR 192.168.2.2
+multiple-type-ptr-record IN PTR 192.168.2.3
+multiple-type-ptr-record IN PTR 192.168.2.4
+multiple-type-ptr-record IN PTR 192.168.2.5
+multiple-type-ptr-record IN PTR 192.168.2.6
+multiple-type-ptr-record IN PTR 192.168.2.7
+multiple-type-ptr-record IN PTR 192.168.2.8
+multiple-type-ptr-record IN PTR 192.168.2.9
+multiple-type-ptr-record IN PTR 192.168.2.10
+multiple-type-ptr-record IN PTR 192.168.2.11
+multiple-type-ptr-record IN PTR 192.168.2.12
+multiple-type-ptr-record IN PTR 192.168.2.13
+multiple-type-ptr-record IN PTR 192.168.2.14
+multiple-type-ptr-record IN PTR 192.168.2.15
+multiple-type-ptr-record IN PTR 192.168.2.16
+multiple-type-ptr-record IN PTR 192.168.2.17
+multiple-type-ptr-record IN PTR 192.168.2.18
+
+A IN MX 10 mail
+mail IN A 10.4.0.1
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN MX 1 mail1
+
+multiple-type-mx-record IN MX 10 mail1
+multiple-type-mx-record IN MX 10 mail2
+multiple-type-mx-record IN MX 10 mail3
+multiple-type-mx-record IN MX 10 mail4
+multiple-type-mx-record IN MX 10 mail5
+multiple-type-mx-record IN MX 10 mail6
+multiple-type-mx-record IN MX 10 mail7
+multiple-type-mx-record IN MX 10 mail8
+multiple-type-mx-record IN MX 10 mail9
+multiple-type-mx-record IN MX 10 mail10
+multiple-type-mx-record IN MX 10 mail11
+multiple-type-mx-record IN MX 10 mail12
+multiple-type-mx-record IN MX 20 mail13
+multiple-type-mx-record IN MX 30 mail14
+multiple-type-mx-record IN MX 40 mail15
+multiple-type-mx-record IN MX 50 mail16
+multiple-type-mx-record IN MX 60 mail17
+multiple-type-mx-record IN MX 70 mail18
+multiple-type-mx-record IN MX 80 mail19
+multiple-type-mx-record IN MX 90 mail20
+mail1 IN A 10.4.1.1
+mail2 IN A 10.4.1.2
+mail3 IN A 10.4.1.3
+mail4 IN A 10.4.1.4
+mail5 IN A 10.4.1.5
+mail6 IN A 10.4.1.6
+mail7 IN A 10.4.1.7
+mail8 IN A 10.4.1.8
+mail9 IN A 10.4.1.9
+mail10 IN A 10.4.1.10
+mail11 IN A 10.4.1.11
+mail12 IN A 10.4.1.12
+mail13 IN A 10.4.1.13
+mail14 IN A 10.4.1.14
+mail15 IN A 10.4.1.15
+mail16 IN A 10.4.1.16
+mail17 IN A 10.4.1.17
+mail18 IN A 10.4.1.18
+mail19 IN A 10.4.1.19
+mail20 IN A 10.4.1.20
+
+
+A IN TXT "ms-dos 3.0/6.0 ?" "google-app_,;!@#$%^&*()[]{}=+|\`~'<>,."
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN TXT "kkkkkkkkkkkkkkkkkkkkkkkk"
+
+multiple-type-txt-record IN TXT txt::1
+multiple-type-txt-record IN TXT txt::2
+multiple-type-txt-record IN TXT txt::3
+multiple-type-txt-record IN TXT txt::4
+multiple-type-txt-record IN TXT txt::5
+multiple-type-txt-record IN TXT txt::6
+multiple-type-txt-record IN TXT txt::7
+multiple-type-txt-record IN TXT txt::8
+multiple-type-txt-record IN TXT txt::9
+multiple-type-txt-record IN TXT txt::10
+multiple-type-txt-record IN TXT txt::11
+multiple-type-txt-record IN TXT txt::12
+multiple-type-txt-record IN TXT txt::13
+multiple-type-txt-record IN TXT txt::14
+multiple-type-txt-record IN TXT txt::15
+multiple-type-txt-record IN TXT txt::16
+multiple-type-txt-record IN TXT txt::17
+multiple-type-txt-record IN TXT txt::18
+multiple-type-txt-record IN TXT txt::19
+multiple-type-txt-record IN TXT txt::20
+multiple-type-txt-record IN TXT txt::21
+multiple-type-txt-record IN TXT txt::22
+multiple-type-txt-record IN TXT txt::23
+multiple-type-txt-record IN TXT txt::24
+multiple-type-txt-record IN TXT txt::25
+multiple-type-txt-record IN TXT txt::26
+multiple-type-txt-record IN TXT txt::27
+multiple-type-txt-record IN TXT txt::28
+multiple-type-txt-record IN TXT txt::29
+multiple-type-txt-record IN TXT txt::30
+multiple-type-txt-record IN TXT txt::31
+
+
+A IN AAAA 2001:dc7::1
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN AAAA 2001:dc7::2
+multiple-type-aaaa-record IN AAAA aaaa::1
+multiple-type-aaaa-record IN AAAA aaaa::2
+multiple-type-aaaa-record IN AAAA aaaa::3
+multiple-type-aaaa-record IN AAAA aaaa::4
+multiple-type-aaaa-record IN AAAA aaaa::5
+multiple-type-aaaa-record IN AAAA aaaa::6
+multiple-type-aaaa-record IN AAAA aaaa::7
+multiple-type-aaaa-record IN AAAA aaaa::8
+multiple-type-aaaa-record IN AAAA aaaa::9
+multiple-type-aaaa-record IN AAAA aaaa::10
+multiple-type-aaaa-record IN AAAA aaaa::11
+multiple-type-aaaa-record IN AAAA aaaa::12
+multiple-type-aaaa-record IN AAAA aaaa::13
+multiple-type-aaaa-record IN AAAA aaaa::14
+multiple-type-aaaa-record IN AAAA aaaa::15
+multiple-type-aaaa-record IN AAAA aaaa::16
+multiple-type-aaaa-record IN AAAA aaaa::17
+multiple-type-aaaa-record IN AAAA aaaa::18
+multiple-type-aaaa-record IN AAAA aaaa::19
+multiple-type-aaaa-record IN AAAA aaaa::20
+multiple-type-aaaa-record IN AAAA aaaa::21
+multiple-type-aaaa-record IN AAAA aaaa::22
+multiple-type-aaaa-record IN AAAA aaaa::23
+multiple-type-aaaa-record IN AAAA aaaa::24
+multiple-type-aaaa-record IN AAAA aaaa::25
+multiple-type-aaaa-record IN AAAA aaaa::26
+multiple-type-aaaa-record IN AAAA aaaa::27
+multiple-type-aaaa-record IN AAAA aaaa::28
+multiple-type-aaaa-record IN AAAA aaaa::29
+multiple-type-aaaa-record IN AAAA aaaa::30
+multiple-type-aaaa-record IN AAAA aaaa::31
+
+A IN NAPTR 18 35 "Au" "siip+E2000U" "!^.*$!simmmp:information at cnnic.cn!" mb
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN NAPTR 18 35 "Au" "siip+E2001U" "!^.*$!simmmp:information at cnnic.cn!" .
+
+sub IN ns ns.sub
+ns.sub IN A 218.241.111.236
+sub-cname IN CNAME www.sub
+sub-dname IN DNAME sub
+
+www.a.b.sub2 IN A 192.168.1.1
+
+A IN DNAME sub1.cnnic.cn.
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN DNAME sub2.cnnic.cn.
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE IN DNAME sub3.cnnic.cn.
+
+d0 IN DNAME sub
+d1 IN DNAME sub2
+test.d0 IN A 192.168.1.2
+test.d1 IN A 192.168.1.3
+d0 IN A 192.168.1.4
+d1 IN A 192.168.1.5
+
+d2 IN DNAME d3
+d3 IN DNAME d2
+
+d4 IN DNAME d5
+d5 IN CNAME d4
+www.d5 IN A 192.168.1.6
+
+d10 IN DNAME d11
+d11 IN DNAME d12
+d12 IN DNAME d13
+d13 IN DNAME d14
+d14 IN DNAME d15
+d15 IN DNAME d16
+d16 IN DNAME d17
+d17 IN DNAME d18
+d18 IN DNAME d19
+d19 IN DNAME d20
+d20 IN DNAME d21
+d21 IN DNAME d22
+d22 IN DNAME d23
+d23 IN DNAME d24
+d24 IN DNAME d25
+d25 IN DNAME d26
+d26 IN DNAME d27
+d27 IN DNAME d28
+d28 IN DNAME d29
+d29 IN DNAME d30
+d30 IN DNAME d31
+d31 IN DNAME d32
+d32 IN DNAME d33
+d33 IN DNAME d34
+d34 IN DNAME d35
+d35 IN DNAME d36
+d36 IN DNAME d37
+d37 IN DNAME d38
+d38 IN DNAME d39
+d39 IN DNAME d40
+www.d40 IN CNAME www.d41
+d41 IN DNAME d42
+d42 IN DNAME d43
+d43 IN DNAME d44
+d44 IN DNAME d45
+d45 IN DNAME d46
+d46 IN DNAME d47
+d47 IN DNAME d48
+d48 IN DNAME d49
+d49 IN DNAME d50
+d50 IN DNAME d51
+d51 IN DNAME d52
+d52 IN DNAME d53
+d53 IN DNAME d54
+d54 IN DNAME d55
+d55 IN DNAME d56
+d56 IN DNAME d57
+d57 IN DNAME d58
+d58 IN DNAME d59
+d59 IN DNAME d60
+
+d70 IN DNAME dicarl.cnnic.cn.
+
+d72 IN DNAME sub01.d72
+;sub01 IN DNAME sub02.d72
+
+cname-point-to-multiple-type-a-record IN CNAME multiple-type-a-record
+multiple-type-a-record IN A 192.168.2.1
+multiple-type-a-record IN A 192.168.2.2
+multiple-type-a-record IN A 192.168.2.3
+multiple-type-a-record IN A 192.168.2.4
+multiple-type-a-record IN A 192.168.2.5
+multiple-type-a-record IN A 192.168.2.6
+multiple-type-a-record IN A 192.168.2.7
+multiple-type-a-record IN A 192.168.2.8
+multiple-type-a-record IN A 192.168.2.9
+multiple-type-a-record IN A 192.168.2.10
+multiple-type-a-record IN A 192.168.2.11
+multiple-type-a-record IN A 192.168.2.12
+multiple-type-a-record IN A 192.168.2.13
+multiple-type-a-record IN A 192.168.2.14
+multiple-type-a-record IN A 192.168.2.15
+multiple-type-a-record IN A 192.168.2.16
+multiple-type-a-record IN A 192.168.2.17
+multiple-type-a-record IN A 192.168.2.18
+multiple-type-a-record IN A 192.168.2.19
+multiple-type-a-record IN A 192.168.2.20
+multiple-type-a-record IN A 192.168.2.21
+multiple-type-a-record IN A 192.168.2.22
+multiple-type-a-record IN A 192.168.2.23
+multiple-type-a-record IN A 192.168.2.24
+multiple-type-a-record IN A 192.168.2.25
+multiple-type-a-record IN A 192.168.2.26
+multiple-type-a-record IN A 192.168.2.27
+multiple-type-a-record IN A 192.168.2.28
+multiple-type-a-record IN A 192.168.2.29
+multiple-type-a-record IN A 192.168.2.30
+multiple-type-a-record IN A 192.168.2.31
+
+multiple-type-ns-record IN NS dns1.multiple-type-ns-record
+multiple-type-ns-record IN NS dns2.multiple-type-ns-record
+multiple-type-ns-record IN NS dns3.multiple-type-ns-record
+multiple-type-ns-record IN NS dns4.multiple-type-ns-record
+multiple-type-ns-record IN NS dns5.multiple-type-ns-record
+multiple-type-ns-record IN NS dns6.multiple-type-ns-record
+multiple-type-ns-record IN NS dns7.multiple-type-ns-record
+multiple-type-ns-record IN NS dns8.multiple-type-ns-record
+multiple-type-ns-record IN NS dns9.multiple-type-ns-record
+multiple-type-ns-record IN NS dns10.multiple-type-ns-record
+multiple-type-ns-record IN NS dns11.multiple-type-ns-record
+multiple-type-ns-record IN NS dns12.multiple-type-ns-record
+multiple-type-ns-record IN NS dns13.multiple-type-ns-record
+multiple-type-ns-record IN NS dns14.multiple-type-ns-record
+multiple-type-ns-record IN NS dns15.multiple-type-ns-record
+multiple-type-ns-record IN NS dns16.multiple-type-ns-record
+multiple-type-ns-record IN NS dns17.multiple-type-ns-record
+multiple-type-ns-record IN NS dns18.multiple-type-ns-record
+multiple-type-ns-record IN NS dns19.multiple-type-ns-record
+multiple-type-ns-record IN NS dns20.multiple-type-ns-record
+multiple-type-ns-record IN NS dns21.multiple-type-ns-record
+multiple-type-ns-record IN NS dns22.multiple-type-ns-record
+multiple-type-ns-record IN NS dns23.multiple-type-ns-record
+multiple-type-ns-record IN NS dns24.multiple-type-ns-record
+dns1.multiple-type-ns-record IN A 192.168.4.1
+dns2.multiple-type-ns-record IN A 192.168.4.2
+dns3.multiple-type-ns-record IN A 192.168.4.3
+dns4.multiple-type-ns-record IN A 192.168.4.4
+dns5.multiple-type-ns-record IN A 192.168.4.5
+dns6.multiple-type-ns-record IN A 192.168.4.6
+dns7.multiple-type-ns-record IN A 192.168.4.7
+dns8.multiple-type-ns-record IN A 192.168.4.8
+dns9.multiple-type-ns-record IN A 192.168.4.9
+dns10.multiple-type-ns-record IN A 192.168.4.10
+dns11.multiple-type-ns-record IN A 192.168.4.11
+dns12.multiple-type-ns-record IN A 192.168.4.12
+dns13.multiple-type-ns-record IN A 192.168.4.13
+dns14.multiple-type-ns-record IN A 192.168.4.14
+dns15.multiple-type-ns-record IN A 192.168.4.15
+dns16.multiple-type-ns-record IN A 192.168.4.16
+dns17.multiple-type-ns-record IN A 192.168.4.17
+dns18.multiple-type-ns-record IN A 192.168.4.18
+dns19.multiple-type-ns-record IN A 192.168.4.19
+dns20.multiple-type-ns-record IN A 192.168.4.20
+dns21.multiple-type-ns-record IN A 192.168.4.21
+dns22.multiple-type-ns-record IN A 192.168.4.22
+dns23.multiple-type-ns-record IN A 192.168.4.23
+dns24.multiple-type-ns-record IN A 192.168.4.24
+dns25.multiple-type-ns-record IN A 192.168.4.25
+
+;demo IN NS ns1
+
+a IN WKS 218.241.108.4 TCP (telnet smtp domain)
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN WKS 218.241.108.5 TCP (telnet smtp domain)
+
+multiple-type-wks-record IN WKS 218.241.109.1 TCP (telnet smtp domain)
+multiple-type-wks-record IN WKS 218.241.109.2 TCP (telnet smtp domain)
+multiple-type-wks-record IN WKS 218.241.109.3 TCP (telnet smtp domain)
+multiple-type-wks-record IN WKS 218.241.109.4 TCP (telnet smtp domain)
+multiple-type-wks-record IN WKS 218.241.109.5 TCP (telnet smtp domain)
+multiple-type-wks-record IN WKS 218.241.109.6 TCP (telnet smtp domain x11)
+multiple-type-wks-record IN WKS 218.241.109.7 TCP (telnet smtp domain)
+multiple-type-wks-record IN WKS 218.241.109.8 TCP (telnet smtp domain)
+multiple-type-wks-record IN WKS 218.241.109.9 TCP (telnet smtp domain)
+multiple-type-wks-record IN WKS 218.241.109.10 TCP (telnet smtp domain)
+multiple-type-wks-record IN WKS 218.241.109.11 TCP (telnet ssh smtp domain)
+multiple-type-wks-record IN WKS 218.241.109.12 TCP (telnet ssh smtp domain)
+multiple-type-wks-record IN WKS 218.241.109.13 TCP (telnet ssh smtp domain)
+multiple-type-wks-record IN WKS 218.241.109.14 TCP (http ssh smtp domain)
+multiple-type-wks-record IN WKS 218.241.109.15 TCP (http ssh smtp domain)
+multiple-type-wks-record IN WKS 218.241.109.16 TCP (http ssh smtp x11)
+
+a IN HINFO VAX-11/780 UNIX
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN HINFO SHARP "PC-1500 1983"
+
+multiple-type-hinfo-record IN HINFO "DG MV-4000" 1990
+multiple-type-hinfo-record IN HINFO "DG MV-4001" 1990
+multiple-type-hinfo-record IN HINFO "DG MV-4002" 1990
+multiple-type-hinfo-record IN HINFO "DG MV-4003" 1990
+multiple-type-hinfo-record IN HINFO "DG MV-4004" 1990
+multiple-type-hinfo-record IN HINFO "DG MV-4005" 1990
+multiple-type-hinfo-record IN HINFO "DG MV-4006" 1990
+multiple-type-hinfo-record IN HINFO "DG MV-4007" 1990
+multiple-type-hinfo-record IN HINFO "DG MV-4008" 1990
+multiple-type-hinfo-record IN HINFO "DG MV-4009" 1990
+multiple-type-hinfo-record IN HINFO "DG MV-4010" 1990
+multiple-type-hinfo-record IN HINFO "DG MV-4011" 1990
+multiple-type-hinfo-record IN HINFO "DG MV-4012" 1990
+multiple-type-hinfo-record IN HINFO "DG MV-4013" 1990
+multiple-type-hinfo-record IN HINFO "DG MV-4014" 1990
+multiple-type-hinfo-record IN HINFO "DG MV-4015" 1990
+multiple-type-hinfo-record IN HINFO "DG MV-4016" 1990
+
+a IN MINFO root.cnnic.cn. error.cnnic.cn.
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN MINFO root63.cnnic.cn. error63.cnnic.cn.
+multiple-type-minfo-record IN MINFO sun00.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+multiple-type-minfo-record IN MINFO sun01.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+multiple-type-minfo-record IN MINFO sun02.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+multiple-type-minfo-record IN MINFO sun03.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+multiple-type-minfo-record IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+multiple-type-minfo-record IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.dn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+multiple-type-minfo-record IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.en. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+multiple-type-minfo-record IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.fn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+multiple-type-minfo-record IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.gn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+
+a IN NSAP 0x47000580ffff0000003210999911112222333343
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN NSAP 0x47000580ffff0000003210999911112222333344
+multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333345
+multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333346
+multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333347
+multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333348
+multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333349
+multiple-type-nsap-record IN NSAP 0x47000580ffff000000321099991111222233334A
+multiple-type-nsap-record IN NSAP 0x47000580ffff000000321099991111222233334b
+multiple-type-nsap-record IN NSAP 0x47000580ffff000000321099991111222233334C
+multiple-type-nsap-record IN NSAP 0x47000580ffff000000321099991111222233334d
+multiple-type-nsap-record IN NSAP 0x47000580ffff000000321099991111222233334E
+multiple-type-nsap-record IN NSAP 0x47000580ffff000000321099991111222233334f
+multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333350
+multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333351
+multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333352
+multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333353
+
+; zebra don't support NSAP-PTR now.
+;47000580ffff000000321099991111222233334444 IN NSAP-PTR a
+
+A IN PX 10 ab.net2.it. O-ab.PRMD-net2.ADMDb.C-it.
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN PX 10 ab.net2.it. O-ab.PRMD-net2.ADMDb.C-it.
+multiple-type-px-record IN PX 10 ab.net2.it. O-ab.PRMD-net2.ADMDb.C-it.
+multiple-type-px-record IN PX 11 ab.net3.it. 1-ab.PRMD-net2.ADMDb.C-it.
+multiple-type-px-record IN PX 12 ab.net4.it. 2-ab.PRMD-net2.ADMDb.C-it.
+multiple-type-px-record IN PX 13 ab.net5.it. 3-ab.PRMD-net2.ADMDb.C-it.
+multiple-type-px-record IN PX 14 ab.net6.it. 4-ab.PRMD-net2.ADMDb.C-it.
+multiple-type-px-record IN PX 15 ab.net7.it. 5-ab.PRMD-net2.ADMDb.C-it.
+multiple-type-px-record IN PX 16 ab.net8.it. 6-ab.PRMD-net2.ADMDb.C-it.
+multiple-type-px-record IN PX 10 ab.net9.it. 7-ab.PRMD-net2.ADMDb.C-it.
+multiple-type-px-record IN PX 10 ab.net2.it. 8-ab.PRMD-net2.ADMDb.C-it.
+multiple-type-px-record IN PX 10 ab.net2.it. 9-ab.PRMD-net2.ADMDb.C-it.
+
+A IN LOC 32 7 19 S 116 2 25 E 10m
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN LOC 32 7 19 S 116 2 25 E 10m
+multiple-type-loc-record IN LOC 32 7 19 S 116 2 25 E 10m
+multiple-type-loc-record IN LOC 33 7 19 S 117 2 25 E 11m
+multiple-type-loc-record IN LOC 34 7 19 S 118 2 25 E 12m
+multiple-type-loc-record IN LOC 35 7 19 S 119 2 25 E 13m
+multiple-type-loc-record IN LOC 36 7 19 S 120 2 25 E 14m
+multiple-type-loc-record IN LOC 37 7 19 S 121 2 25 E 15m
+multiple-type-loc-record IN LOC 38 7 19 S 122 2 25 E 16m
+multiple-type-loc-record IN LOC 39 7 19 S 123 2 25 E 17m
+multiple-type-loc-record IN LOC 40 7 19 S 124 2 25 E 18m
+multiple-type-loc-record IN LOC 41 7 19 S 125 2 25 E 19m
+multiple-type-loc-record IN LOC 42 7 19 S 126 2 25 E 20m
+multiple-type-loc-record IN LOC 43 7 19 S 127 2 25 E 21m
+multiple-type-loc-record IN LOC 44 7 19 S 128 2 25 E 22m
+multiple-type-loc-record IN LOC 45 7 19 S 129 2 25 E 23m
+multiple-type-loc-record IN LOC 46 7 19 S 130 2 25 E 24m
+multiple-type-loc-record IN LOC 47 7 19 S 131 2 25 E 25m
+multiple-type-loc-record IN LOC 89 59 59.999 S 179 59 59.999 E 1000000m 1000m 1000m
+multiple-type-loc-record IN LOC 90 S 180 E 0.01 0.01 0.01
+
+_http._tcp.a IN SRV 0 2 80 www.movie.edu.
+a IN SRV 0 2 80 www.movie.edu.
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN SRV 0 2 80 www.movie.edu.
+multiple-type-srv-record IN SRV 0 2 80 www.movie0.edu.
+multiple-type-srv-record IN SRV 1 3 80 www.movie1.edu.
+multiple-type-srv-record IN SRV 2 4 80 www.movie2.edu.
+multiple-type-srv-record IN SRV 3 5 80 www.movie3.edu.
+multiple-type-srv-record IN SRV 4 6 80 www.movie4.edu.
+multiple-type-srv-record IN SRV 5 7 80 www.movie5.edu.
+multiple-type-srv-record IN SRV 6 8 80 www.movie6.edu.
+multiple-type-srv-record IN SRV 7 9 80 www.movie7.edu.
+multiple-type-srv-record IN SRV 8 10 80 www.movie8.edu.
+multiple-type-srv-record IN SRV 9 11 80 www.movie9.edu.
+multiple-type-srv-record IN SRV 10 12 80 www.movie10.edu.
+multiple-type-srv-record IN SRV 11 13 80 www.movie11.edu.
+multiple-type-srv-record IN SRV 12 14 80 www.movie12.edu.
+
+a IN KX 20 kaku.google.com.
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN KX 20 kaku.google.com.
+multiple-type-kx-record IN KX 0 kaku0.google.com.
+multiple-type-kx-record IN KX 10 kaku1.google.com.
+multiple-type-kx-record IN KX 20 kaku2.google.com.
+multiple-type-kx-record IN KX 30 kaku3.google.com.
+multiple-type-kx-record IN KX 40 kaku4.google.com.
+multiple-type-kx-record IN KX 50 kaku5.google.com.
+multiple-type-kx-record IN KX 60 kaku6.google.com.
+multiple-type-kx-record IN KX 70 kaku7.google.com.
+multiple-type-kx-record IN KX 80 kaku8.google.com.
+multiple-type-kx-record IN KX 90 kaku9.google.com.
+multiple-type-kx-record IN KX 100 kaku10.google.com.
+multiple-type-kx-record IN KX 110 kaku11.google.com.
+multiple-type-kx-record IN KX 120 kaku12.google.com.
+multiple-type-kx-record IN KX 130 kaku13.google.com.
+
+A IN CERT 65534 65535 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi
+ WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl
+ d80jEeC8aTrO+KKmCaY= )
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN CERT 65534 65535 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi
+ WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl
+ d80jEeC8aTrO+KKmCaY= )
+multiple-type-cert-record IN CERT 65534 65535 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi
+ WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl
+ d80jEeC8aTrO+KKmCaY= )
+multiple-type-cert-record IN CERT 65533 65534 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi
+ WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl
+ d80jEeC8aTrO+KKmCaY= )
+multiple-type-cert-record IN CERT 65532 65533 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi
+ WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl
+ d80jEeC8aTrO+KKmCaY= )
+multiple-type-cert-record IN CERT 65531 65532 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi
+ WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl
+ d80jEeC8aTrO+KKmCaY= )
+multiple-type-cert-record IN CERT 65530 65531 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi
+ WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl
+ d80jEeC8aTrO+KKmCaY= )
+
+A IN APL 1:192.168.32.0/21 !1:192.168.38.0/28
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN APL 1:192.168.32.0/21 !1:192.168.38.0/28
+multiple-type-apl-record IN APL 1:192.168.32.15/14 !1:192.168.38.0/28
+multiple-type-apl-record IN APL 1:192.168.33.15/15 !1:192.168.39.0/28
+multiple-type-apl-record IN APL 1:192.168.34.15/16 !1:192.168.40.0/28
+multiple-type-apl-record IN APL 1:192.168.35.15/17 !1:192.168.41.0/28
+multiple-type-apl-record IN APL 1:192.168.36.15/18 !1:192.168.42.0/28
+multiple-type-apl-record IN APL 1:192.168.37.15/19 !1:192.168.43.0/28
+multiple-type-apl-record IN APL 1:192.168.38.15/20 !1:192.168.44.0/28
+multiple-type-apl-record IN APL 1:192.168.39.15/21 !1:192.168.45.0/28
+multiple-type-apl-record IN APL 1:192.168.40.15/22 !1:192.168.46.0/28
+multiple-type-apl-record IN APL 1:192.168.41.15/23 !1:192.168.47.0/28
+multiple-type-apl-record IN APL 1:192.168.42.15/24 !1:192.168.48.0/28
+multiple-type-apl-record IN APL 1:192.168.43.15/25 !1:192.168.49.0/28
+multiple-type-apl-record IN APL 1:192.168.44.15/26 !1:192.168.50.0/28
+multiple-type-apl-record IN APL 1:192.168.45.15/27 !1:192.168.51.0/28
+multiple-type-apl-record IN APL 1:192.168.46.15/28 !1:192.168.52.0/28
+multiple-type-apl-record IN APL 1:192.168.47.15/29 !1:192.168.53.0/28
+multiple-type-apl-record IN APL 1:192.168.48.15/20 !1:192.168.54.0/28
+multiple-type-apl-record IN APL 1:224.0.0.0/4 2:FF00:0:0:AAAA:CCCC:BBBB:9999:5555/48
+multiple-type-apl-record IN APL 1:224.0.0.5/8 2:FF00::0/8
+multiple-type-apl-record IN APL 1:224.0.0.9/12 2:FF00::888f:0/24
+multiple-type-apl-record IN APL 1:224.0.0.13/16 2:FF00::7777/72
+
+A IN SSHFP 2 1 123456789abcdef67890123456789abcdef67890
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN SSHFP 2 1 123456789abcdef67890123456789abcdef67890
+multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67890
+multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67891
+multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67892
+multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67893
+multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67894
+multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67895
+multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67896
+multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67897
+multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67898
+multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67899
+multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef6789a
+multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef6789b
+multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef6789c
+
+a IN IPSECKEY ( 10 1 2
+ 192.0.2.3
+ AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN IPSECKEY ( 10 1 2
+ 192.0.2.3
+ AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )
+multiple-type-ipseckey-record IN IPSECKEY ( 10 1 2
+ 192.0.2.3
+ AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )
+multiple-type-ipseckey-record IN IPSECKEY ( 10 1 2
+ 192.0.2.4
+ BQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )
+multiple-type-ipseckey-record IN IPSECKEY ( 10 1 2
+ 192.0.2.5
+ CQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )
+multiple-type-ipseckey-record IN IPSECKEY ( 10 1 2
+ 192.0.2.6
+ DQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )
+multiple-type-ipseckey-record IN IPSECKEY ( 10 1 2
+ 192.0.2.7
+ EQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )
+multiple-type-ipseckey-record IN IPSECKEY ( 10 1 2
+ 192.0.2.8
+ FQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )
+multiple-type-ipseckey-record IN IPSECKEY ( 10 1 2
+ 192.0.2.9
+ GQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )
+multiple-type-ipseckey-record IN IPSECKEY ( 10 1 2
+ 192.0.2.10
+ HQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )
+multiple-type-ipseckey-record IN IPSECKEY ( 10 1 2
+ 192.0.2.11
+ IQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )
+
+A IN DHCID ( AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN DHCID ( AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+multiple-type-dhcid-record IN DHCID ( AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+multiple-type-dhcid-record IN DHCID ( BBIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+multiple-type-dhcid-record IN DHCID ( CCIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+multiple-type-dhcid-record IN DHCID ( DDIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+multiple-type-dhcid-record IN DHCID ( EEIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+multiple-type-dhcid-record IN DHCID ( FFIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+multiple-type-dhcid-record IN DHCID ( GGIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+multiple-type-dhcid-record IN DHCID ( HHIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+multiple-type-dhcid-record IN DHCID ( IIIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+multiple-type-dhcid-record IN DHCID ( JJIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+multiple-type-dhcid-record IN DHCID ( KKIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+
+A IN SPF 'sfasf'
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN SPF 'asdfghjkl;'
+multiple-type-spf-record IN SPF 'asdfghjkl;'
+multiple-type-spf-record IN SPF 'sina;'
+multiple-type-spf-record IN SPF 'cnnic;'
+multiple-type-spf-record IN SPF 'jkl;'
+multiple-type-spf-record IN SPF 'c=sina.com.'
+multiple-type-spf-record IN SPF 'fw=fff.com.cn.'
+multiple-type-spf-record IN SPF 'ibm;'
+multiple-type-spf-record IN SPF 'dell;'
+multiple-type-spf-record IN SPF 'google - microsoft;'
+multiple-type-spf-record IN SPF 'leibusi;'
+multiple-type-spf-record IN SPF "v=spf1"
+multiple-type-spf-record IN SPF "v=spf1 -all"
+multiple-type-spf-record IN SPF "v=spf1 ip4:192.168.0.1/16 -all "
+multiple-type-spf-record IN SPF "v=spf1 ip4:192.168.1.1/16 -all "
+multiple-type-spf-record IN SPF "v=spf1 ip4:192.168.2.1/16 -all "
+multiple-type-spf-record IN SPF "v=spf1 ip4:192.168.3.1/16 -all "
+multiple-type-spf-record IN SPF "v=spf1 ip4:192.168.4.1/16 -all "
+multiple-type-spf-record IN SPF "v=spf1 ip4:192.168.5.1/16 -all "
+multiple-type-spf-record IN SPF "v=spf1 ip4:192.168.6.1/16 -all "
+multiple-type-spf-record IN SPF "v=spf1 ip4:192.168.7.1/16 -all "
+
+testa.no-type IN A 10.1.1.1
+testaaaa.no-type IN AAAA ::1
+
+test.non-terminal.minittl 100 IN A 10.10.1.1
+
+test.non-terminal.a IN A 192.168.1.10
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN A 192.168.1.10
+
+test.non-terminal.subdomain IN NS ns1.mysub.com.
+test.non-terminal.subdomain IN NS ns2.mysub.com.
+
+test.non-terminal.subdn IN NS ns.subdn.cn.
+
+test.non-terminal.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA NS ns1.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+test.non-terminal.ns1.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA A 192.168.2.1
+
+
+test.non-terminal.C IN CNAME A
+test.non-terminal.C0 IN CNAME C1
+test.non-terminal.C1 IN CNAME C2
+test.non-terminal.C2 IN CNAME C3
+test.non-terminal.C3 IN CNAME C4
+test.non-terminal.C4 IN CNAME C5
+test.non-terminal.C5 IN CNAME C6
+test.non-terminal.C6 IN CNAME C7
+test.non-terminal.C7 IN CNAME C8
+test.non-terminal.C8 IN CNAME C9
+test.non-terminal.C9 IN CNAME C10
+test.non-terminal.C10 IN CNAME C11
+test.non-terminal.C11 IN CNAME C12
+test.non-terminal.C12 IN CNAME C13
+test.non-terminal.C13 IN CNAME C14
+test.non-terminal.C14 IN CNAME C15
+test.non-terminal.C15 IN CNAME C16
+test.non-terminal.C16 IN CNAME C17
+test.non-terminal.C17 IN CNAME C18
+test.non-terminal.C18 IN CNAME C19
+test.non-terminal.C19 IN CNAME C20
+test.non-terminal.C20 IN CNAME C21
+test.non-terminal.C21 IN CNAME C22
+test.non-terminal.C22 IN CNAME C23
+test.non-terminal.C23 IN CNAME C24
+test.non-terminal.C24 IN CNAME C25
+
+test.non-terminal.C30 IN CNAME C31
+test.non-terminal.C31 IN CNAME C32
+test.non-terminal.C32 IN CNAME C33
+test.non-terminal.C33 IN CNAME C34
+test.non-terminal.C34 IN CNAME C35
+test.non-terminal.C35 IN CNAME C36
+test.non-terminal.C36 IN CNAME C37
+test.non-terminal.C37 IN CNAME C38
+test.non-terminal.C38 IN CNAME C39
+test.non-terminal.C39 IN CNAME C40
+test.non-terminal.C40 IN CNAME C41
+test.non-terminal.C41 IN CNAME C42
+test.non-terminal.C42 IN CNAME C43
+test.non-terminal.C43 IN CNAME C44
+test.non-terminal.C44 IN CNAME C45
+test.non-terminal.C45 IN CNAME C46
+test.non-terminal.C46 IN CNAME C47
+test.non-terminal.C47 IN CNAME C48
+test.non-terminal.C48 IN CNAME C49
+test.non-terminal.C49 IN CNAME C50
+test.non-terminal.C50 IN CNAME C51
+test.non-terminal.C51 IN CNAME A
+
+test.non-terminal.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA CNAME AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+
+test.non-terminal.A2 IN A 192.168.1.12
+test.non-terminal.B1 IN CNAME A2
+
+test.non-terminal.a IN PTR 1.1.226.159.cnnic.cn.
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN PTR 2.1.226.159.cnnic.cn.
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.1
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.2
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.3
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.4
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.5
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.6
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.7
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.8
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.9
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.10
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.11
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.12
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.13
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.14
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.15
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.16
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.17
+test.non-terminal.multiple-type-ptr-record IN PTR 192.168.2.18
+
+test.non-terminal.A IN MX 10 mail
+test.non-terminal.mail IN A 10.4.0.1
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN MX 1 mail1
+
+test.non-terminal.multiple-type-mx-record IN MX 10 mail1
+test.non-terminal.multiple-type-mx-record IN MX 10 mail2
+test.non-terminal.multiple-type-mx-record IN MX 10 mail3
+test.non-terminal.multiple-type-mx-record IN MX 10 mail4
+test.non-terminal.multiple-type-mx-record IN MX 10 mail5
+test.non-terminal.multiple-type-mx-record IN MX 10 mail6
+test.non-terminal.multiple-type-mx-record IN MX 10 mail7
+test.non-terminal.multiple-type-mx-record IN MX 10 mail8
+test.non-terminal.multiple-type-mx-record IN MX 10 mail9
+test.non-terminal.multiple-type-mx-record IN MX 10 mail10
+test.non-terminal.multiple-type-mx-record IN MX 10 mail11
+test.non-terminal.multiple-type-mx-record IN MX 10 mail12
+test.non-terminal.multiple-type-mx-record IN MX 20 mail13
+test.non-terminal.multiple-type-mx-record IN MX 30 mail14
+test.non-terminal.multiple-type-mx-record IN MX 40 mail15
+test.non-terminal.multiple-type-mx-record IN MX 50 mail16
+test.non-terminal.multiple-type-mx-record IN MX 60 mail17
+test.non-terminal.multiple-type-mx-record IN MX 70 mail18
+test.non-terminal.multiple-type-mx-record IN MX 80 mail19
+test.non-terminal.multiple-type-mx-record IN MX 90 mail20
+test.non-terminal.mail1 IN A 10.4.1.1
+test.non-terminal.mail2 IN A 10.4.1.2
+test.non-terminal.mail3 IN A 10.4.1.3
+test.non-terminal.mail4 IN A 10.4.1.4
+test.non-terminal.mail5 IN A 10.4.1.5
+test.non-terminal.mail6 IN A 10.4.1.6
+test.non-terminal.mail7 IN A 10.4.1.7
+test.non-terminal.mail8 IN A 10.4.1.8
+test.non-terminal.mail9 IN A 10.4.1.9
+test.non-terminal.mail10 IN A 10.4.1.10
+test.non-terminal.mail11 IN A 10.4.1.11
+test.non-terminal.mail12 IN A 10.4.1.12
+test.non-terminal.mail13 IN A 10.4.1.13
+test.non-terminal.mail14 IN A 10.4.1.14
+test.non-terminal.mail15 IN A 10.4.1.15
+test.non-terminal.mail16 IN A 10.4.1.16
+test.non-terminal.mail17 IN A 10.4.1.17
+test.non-terminal.mail18 IN A 10.4.1.18
+test.non-terminal.mail19 IN A 10.4.1.19
+test.non-terminal.mail20 IN A 10.4.1.20
+
+
+test.non-terminal.A IN TXT "ms-dos 3.0/6.0 ?" "google-app_,;!@#$%^&*()[]{}=+|\`~'<>,."
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN TXT "kkkkkkkkkkkkkkkkkkkkkkkk"
+
+test.non-terminal.multiple-type-txt-record IN TXT txt::1
+test.non-terminal.multiple-type-txt-record IN TXT txt::2
+test.non-terminal.multiple-type-txt-record IN TXT txt::3
+test.non-terminal.multiple-type-txt-record IN TXT txt::4
+test.non-terminal.multiple-type-txt-record IN TXT txt::5
+test.non-terminal.multiple-type-txt-record IN TXT txt::6
+test.non-terminal.multiple-type-txt-record IN TXT txt::7
+test.non-terminal.multiple-type-txt-record IN TXT txt::8
+test.non-terminal.multiple-type-txt-record IN TXT txt::9
+test.non-terminal.multiple-type-txt-record IN TXT txt::10
+test.non-terminal.multiple-type-txt-record IN TXT txt::11
+test.non-terminal.multiple-type-txt-record IN TXT txt::12
+test.non-terminal.multiple-type-txt-record IN TXT txt::13
+test.non-terminal.multiple-type-txt-record IN TXT txt::14
+test.non-terminal.multiple-type-txt-record IN TXT txt::15
+test.non-terminal.multiple-type-txt-record IN TXT txt::16
+test.non-terminal.multiple-type-txt-record IN TXT txt::17
+test.non-terminal.multiple-type-txt-record IN TXT txt::18
+test.non-terminal.multiple-type-txt-record IN TXT txt::19
+test.non-terminal.multiple-type-txt-record IN TXT txt::20
+test.non-terminal.multiple-type-txt-record IN TXT txt::21
+test.non-terminal.multiple-type-txt-record IN TXT txt::22
+test.non-terminal.multiple-type-txt-record IN TXT txt::23
+test.non-terminal.multiple-type-txt-record IN TXT txt::24
+test.non-terminal.multiple-type-txt-record IN TXT txt::25
+test.non-terminal.multiple-type-txt-record IN TXT txt::26
+test.non-terminal.multiple-type-txt-record IN TXT txt::27
+test.non-terminal.multiple-type-txt-record IN TXT txt::28
+test.non-terminal.multiple-type-txt-record IN TXT txt::29
+test.non-terminal.multiple-type-txt-record IN TXT txt::30
+test.non-terminal.multiple-type-txt-record IN TXT txt::31
+
+
+test.non-terminal.A IN AAAA 2001:dc7::1
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN AAAA 2001:dc7::2
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::1
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::2
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::3
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::4
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::5
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::6
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::7
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::8
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::9
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::10
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::11
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::12
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::13
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::14
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::15
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::16
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::17
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::18
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::19
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::20
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::21
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::22
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::23
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::24
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::25
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::26
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::27
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::28
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::29
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::30
+test.non-terminal.multiple-type-aaaa-record IN AAAA aaaa::31
+
+test.non-terminal.A IN NAPTR 18 35 "Au" "siip+E2000U" "!^.*$!simmmp:information at cnnic.cn!" mb
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN NAPTR 18 35 "Au" "siip+E2001U" "!^.*$!simmmp:information at cnnic.cn!" .
+
+test.non-terminal.sub IN ns ns.sub
+test.non-terminal.ns.sub IN A 218.241.111.236
+test.non-terminal.sub-cname IN CNAME www.sub
+test.non-terminal.sub-dname IN DNAME sub
+
+test.non-terminal.www.a.b.sub2 IN A 192.168.1.1
+
+test.non-terminal.A IN DNAME sub1.cnnic.cn.
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN DNAME sub2.cnnic.cn.
+
+test.non-terminal.d0 IN DNAME sub
+test.non-terminal.d1 IN DNAME sub2
+test.non-terminal.test.d0 IN A 192.168.1.2
+test.non-terminal.test.d1 IN A 192.168.1.3
+test.non-terminal.d0 IN A 192.168.1.4
+test.non-terminal.d1 IN A 192.168.1.5
+
+test.non-terminal.d2 IN DNAME d3
+test.non-terminal.d3 IN DNAME d2
+
+test.non-terminal.d4 IN DNAME d5
+test.non-terminal.d5 IN CNAME d4
+test.non-terminal.www.d5 IN A 192.168.1.6
+
+test.non-terminal.d10 IN DNAME d11
+test.non-terminal.d11 IN DNAME d12
+test.non-terminal.d12 IN DNAME d13
+test.non-terminal.d13 IN DNAME d14
+test.non-terminal.d14 IN DNAME d15
+test.non-terminal.d15 IN DNAME d16
+test.non-terminal.d16 IN DNAME d17
+test.non-terminal.d17 IN DNAME d18
+test.non-terminal.d18 IN DNAME d19
+test.non-terminal.d19 IN DNAME d20
+test.non-terminal.d20 IN DNAME d21
+test.non-terminal.d21 IN DNAME d22
+test.non-terminal.d22 IN DNAME d23
+test.non-terminal.d23 IN DNAME d24
+test.non-terminal.d24 IN DNAME d25
+test.non-terminal.d25 IN DNAME d26
+test.non-terminal.d26 IN DNAME d27
+test.non-terminal.d27 IN DNAME d28
+test.non-terminal.d28 IN DNAME d29
+test.non-terminal.d29 IN DNAME d30
+test.non-terminal.d30 IN DNAME d31
+test.non-terminal.d31 IN DNAME d32
+test.non-terminal.d32 IN DNAME d33
+test.non-terminal.d33 IN DNAME d34
+test.non-terminal.d34 IN DNAME d35
+test.non-terminal.d35 IN DNAME d36
+test.non-terminal.d36 IN DNAME d37
+test.non-terminal.d37 IN DNAME d38
+test.non-terminal.d38 IN DNAME d39
+test.non-terminal.d39 IN DNAME d40
+test.non-terminal.www.d40 IN CNAME www.d41
+test.non-terminal.d41 IN DNAME d42
+test.non-terminal.d42 IN DNAME d43
+test.non-terminal.d43 IN DNAME d44
+test.non-terminal.d44 IN DNAME d45
+test.non-terminal.d45 IN DNAME d46
+test.non-terminal.d46 IN DNAME d47
+test.non-terminal.d47 IN DNAME d48
+test.non-terminal.d48 IN DNAME d49
+test.non-terminal.d49 IN DNAME d50
+test.non-terminal.d50 IN DNAME d51
+test.non-terminal.d51 IN DNAME d52
+test.non-terminal.d52 IN DNAME d53
+test.non-terminal.d53 IN DNAME d54
+test.non-terminal.d54 IN DNAME d55
+test.non-terminal.d55 IN DNAME d56
+test.non-terminal.d56 IN DNAME d57
+test.non-terminal.d57 IN DNAME d58
+test.non-terminal.d58 IN DNAME d59
+test.non-terminal.d59 IN DNAME d60
+
+test.non-terminal.d70 IN DNAME dicarl.cnnic.cn.
+
+test.non-terminal.d72 IN DNAME sub01.d72
+
+test.non-terminal.cname-point-to-multiple-type-a-record IN CNAME multiple-type-a-record
+test.non-terminal.multiple-type-a-record IN A 192.168.2.1
+test.non-terminal.multiple-type-a-record IN A 192.168.2.2
+test.non-terminal.multiple-type-a-record IN A 192.168.2.3
+test.non-terminal.multiple-type-a-record IN A 192.168.2.4
+test.non-terminal.multiple-type-a-record IN A 192.168.2.5
+test.non-terminal.multiple-type-a-record IN A 192.168.2.6
+test.non-terminal.multiple-type-a-record IN A 192.168.2.7
+test.non-terminal.multiple-type-a-record IN A 192.168.2.8
+test.non-terminal.multiple-type-a-record IN A 192.168.2.9
+test.non-terminal.multiple-type-a-record IN A 192.168.2.10
+test.non-terminal.multiple-type-a-record IN A 192.168.2.11
+test.non-terminal.multiple-type-a-record IN A 192.168.2.12
+test.non-terminal.multiple-type-a-record IN A 192.168.2.13
+test.non-terminal.multiple-type-a-record IN A 192.168.2.14
+test.non-terminal.multiple-type-a-record IN A 192.168.2.15
+test.non-terminal.multiple-type-a-record IN A 192.168.2.16
+test.non-terminal.multiple-type-a-record IN A 192.168.2.17
+test.non-terminal.multiple-type-a-record IN A 192.168.2.18
+test.non-terminal.multiple-type-a-record IN A 192.168.2.19
+test.non-terminal.multiple-type-a-record IN A 192.168.2.20
+test.non-terminal.multiple-type-a-record IN A 192.168.2.21
+test.non-terminal.multiple-type-a-record IN A 192.168.2.22
+test.non-terminal.multiple-type-a-record IN A 192.168.2.23
+test.non-terminal.multiple-type-a-record IN A 192.168.2.24
+test.non-terminal.multiple-type-a-record IN A 192.168.2.25
+test.non-terminal.multiple-type-a-record IN A 192.168.2.26
+test.non-terminal.multiple-type-a-record IN A 192.168.2.27
+test.non-terminal.multiple-type-a-record IN A 192.168.2.28
+test.non-terminal.multiple-type-a-record IN A 192.168.2.29
+test.non-terminal.multiple-type-a-record IN A 192.168.2.30
+test.non-terminal.multiple-type-a-record IN A 192.168.2.31
+
+test.non-terminal.multiple-type-ns-record IN NS dns1.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns2.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns3.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns4.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns5.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns6.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns7.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns8.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns9.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns10.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns11.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns12.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns13.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns14.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns15.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns16.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns17.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns18.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns19.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns20.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns21.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns22.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns23.multiple-type-ns-record
+test.non-terminal.multiple-type-ns-record IN NS dns24.multiple-type-ns-record
+test.non-terminal.dns1.multiple-type-ns-record IN A 192.168.4.1
+test.non-terminal.dns2.multiple-type-ns-record IN A 192.168.4.2
+test.non-terminal.dns3.multiple-type-ns-record IN A 192.168.4.3
+test.non-terminal.dns4.multiple-type-ns-record IN A 192.168.4.4
+test.non-terminal.dns5.multiple-type-ns-record IN A 192.168.4.5
+test.non-terminal.dns6.multiple-type-ns-record IN A 192.168.4.6
+test.non-terminal.dns7.multiple-type-ns-record IN A 192.168.4.7
+test.non-terminal.dns8.multiple-type-ns-record IN A 192.168.4.8
+test.non-terminal.dns9.multiple-type-ns-record IN A 192.168.4.9
+test.non-terminal.dns10.multiple-type-ns-record IN A 192.168.4.10
+test.non-terminal.dns11.multiple-type-ns-record IN A 192.168.4.11
+test.non-terminal.dns12.multiple-type-ns-record IN A 192.168.4.12
+test.non-terminal.dns13.multiple-type-ns-record IN A 192.168.4.13
+test.non-terminal.dns14.multiple-type-ns-record IN A 192.168.4.14
+test.non-terminal.dns15.multiple-type-ns-record IN A 192.168.4.15
+test.non-terminal.dns16.multiple-type-ns-record IN A 192.168.4.16
+test.non-terminal.dns17.multiple-type-ns-record IN A 192.168.4.17
+test.non-terminal.dns18.multiple-type-ns-record IN A 192.168.4.18
+test.non-terminal.dns19.multiple-type-ns-record IN A 192.168.4.19
+test.non-terminal.dns20.multiple-type-ns-record IN A 192.168.4.20
+test.non-terminal.dns21.multiple-type-ns-record IN A 192.168.4.21
+test.non-terminal.dns22.multiple-type-ns-record IN A 192.168.4.22
+test.non-terminal.dns23.multiple-type-ns-record IN A 192.168.4.23
+test.non-terminal.dns24.multiple-type-ns-record IN A 192.168.4.24
+test.non-terminal.dns25.multiple-type-ns-record IN A 192.168.4.25
+
+
+test.non-terminal.a IN WKS 218.241.108.4 TCP (telnet smtp domain)
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN WKS 218.241.108.5 TCP (telnet smtp domain)
+
+test.non-terminal.multiple-type-wks-record IN WKS 218.241.109.1 TCP (telnet smtp domain)
+test.non-terminal.multiple-type-wks-record IN WKS 218.241.109.2 TCP (telnet smtp domain)
+test.non-terminal.multiple-type-wks-record IN WKS 218.241.109.3 TCP (telnet smtp domain)
+test.non-terminal.multiple-type-wks-record IN WKS 218.241.109.4 TCP (telnet smtp domain)
+test.non-terminal.multiple-type-wks-record IN WKS 218.241.109.5 TCP (telnet smtp domain)
+test.non-terminal.multiple-type-wks-record IN WKS 218.241.109.6 TCP (telnet smtp domain x11)
+test.non-terminal.multiple-type-wks-record IN WKS 218.241.109.7 TCP (telnet smtp domain)
+test.non-terminal.multiple-type-wks-record IN WKS 218.241.109.8 TCP (telnet smtp domain)
+test.non-terminal.multiple-type-wks-record IN WKS 218.241.109.9 TCP (telnet smtp domain)
+test.non-terminal.multiple-type-wks-record IN WKS 218.241.109.10 TCP (telnet smtp domain)
+test.non-terminal.multiple-type-wks-record IN WKS 218.241.109.11 TCP (telnet ssh smtp domain)
+test.non-terminal.multiple-type-wks-record IN WKS 218.241.109.12 TCP (telnet ssh smtp domain)
+test.non-terminal.multiple-type-wks-record IN WKS 218.241.109.13 TCP (telnet ssh smtp domain)
+test.non-terminal.multiple-type-wks-record IN WKS 218.241.109.14 TCP (http ssh smtp domain)
+test.non-terminal.multiple-type-wks-record IN WKS 218.241.109.15 TCP (http ssh smtp domain)
+test.non-terminal.multiple-type-wks-record IN WKS 218.241.109.16 TCP (http ssh smtp x11)
+
+test.non-terminal.a IN HINFO VAX-11/780 UNIX
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN HINFO SHARP "PC-1500 1983"
+
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4000" 1990
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4001" 1990
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4002" 1990
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4003" 1990
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4004" 1990
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4005" 1990
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4006" 1990
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4007" 1990
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4008" 1990
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4009" 1990
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4010" 1990
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4011" 1990
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4012" 1990
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4013" 1990
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4014" 1990
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4015" 1990
+test.non-terminal.multiple-type-hinfo-record IN HINFO "DG MV-4016" 1990
+
+test.non-terminal.a IN MINFO root.cnnic.cn. error.cnnic.cn.
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN MINFO root63.cnnic.cn. error63.cnnic.cn.
+test.non-terminal.multiple-type-minfo-record IN MINFO sun00.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+test.non-terminal.multiple-type-minfo-record IN MINFO sun01.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+test.non-terminal.multiple-type-minfo-record IN MINFO sun02.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+test.non-terminal.multiple-type-minfo-record IN MINFO sun03.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+test.non-terminal.multiple-type-minfo-record IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+test.non-terminal.multiple-type-minfo-record IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.dn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+test.non-terminal.multiple-type-minfo-record IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.en. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+test.non-terminal.multiple-type-minfo-record IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.fn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+test.non-terminal.multiple-type-minfo-record IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.gn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+
+test.non-terminal.a IN NSAP 0x47000580ffff0000003210999911112222333343
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN NSAP 0x47000580ffff0000003210999911112222333344
+test.non-terminal.multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333345
+test.non-terminal.multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333346
+test.non-terminal.multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333347
+test.non-terminal.multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333348
+test.non-terminal.multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333349
+test.non-terminal.multiple-type-nsap-record IN NSAP 0x47000580ffff000000321099991111222233334A
+test.non-terminal.multiple-type-nsap-record IN NSAP 0x47000580ffff000000321099991111222233334b
+test.non-terminal.multiple-type-nsap-record IN NSAP 0x47000580ffff000000321099991111222233334C
+test.non-terminal.multiple-type-nsap-record IN NSAP 0x47000580ffff000000321099991111222233334d
+test.non-terminal.multiple-type-nsap-record IN NSAP 0x47000580ffff000000321099991111222233334E
+test.non-terminal.multiple-type-nsap-record IN NSAP 0x47000580ffff000000321099991111222233334f
+test.non-terminal.multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333350
+test.non-terminal.multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333351
+test.non-terminal.multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333352
+test.non-terminal.multiple-type-nsap-record IN NSAP 0x47000580ffff0000003210999911112222333353
+
+
+test.non-terminal.A IN PX 10 ab.net2.it. O-ab.PRMD-net2.ADMDb.C-it.
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN PX 10 ab.net2.it. O-ab.PRMD-net2.ADMDb.C-it.
+test.non-terminal.multiple-type-px-record IN PX 10 ab.net2.it. O-ab.PRMD-net2.ADMDb.C-it.
+test.non-terminal.multiple-type-px-record IN PX 11 ab.net3.it. 1-ab.PRMD-net2.ADMDb.C-it.
+test.non-terminal.multiple-type-px-record IN PX 12 ab.net4.it. 2-ab.PRMD-net2.ADMDb.C-it.
+test.non-terminal.multiple-type-px-record IN PX 13 ab.net5.it. 3-ab.PRMD-net2.ADMDb.C-it.
+test.non-terminal.multiple-type-px-record IN PX 14 ab.net6.it. 4-ab.PRMD-net2.ADMDb.C-it.
+test.non-terminal.multiple-type-px-record IN PX 15 ab.net7.it. 5-ab.PRMD-net2.ADMDb.C-it.
+test.non-terminal.multiple-type-px-record IN PX 16 ab.net8.it. 6-ab.PRMD-net2.ADMDb.C-it.
+test.non-terminal.multiple-type-px-record IN PX 10 ab.net9.it. 7-ab.PRMD-net2.ADMDb.C-it.
+test.non-terminal.multiple-type-px-record IN PX 10 ab.net2.it. 8-ab.PRMD-net2.ADMDb.C-it.
+test.non-terminal.multiple-type-px-record IN PX 10 ab.net2.it. 9-ab.PRMD-net2.ADMDb.C-it.
+
+test.non-terminal.A IN LOC 32 7 19 S 116 2 25 E 10m
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN LOC 32 7 19 S 116 2 25 E 10m
+test.non-terminal.multiple-type-loc-record IN LOC 32 7 19 S 116 2 25 E 10m
+test.non-terminal.multiple-type-loc-record IN LOC 33 7 19 S 117 2 25 E 11m
+test.non-terminal.multiple-type-loc-record IN LOC 34 7 19 S 118 2 25 E 12m
+test.non-terminal.multiple-type-loc-record IN LOC 35 7 19 S 119 2 25 E 13m
+test.non-terminal.multiple-type-loc-record IN LOC 36 7 19 S 120 2 25 E 14m
+test.non-terminal.multiple-type-loc-record IN LOC 37 7 19 S 121 2 25 E 15m
+test.non-terminal.multiple-type-loc-record IN LOC 38 7 19 S 122 2 25 E 16m
+test.non-terminal.multiple-type-loc-record IN LOC 39 7 19 S 123 2 25 E 17m
+test.non-terminal.multiple-type-loc-record IN LOC 40 7 19 S 124 2 25 E 18m
+test.non-terminal.multiple-type-loc-record IN LOC 41 7 19 S 125 2 25 E 19m
+test.non-terminal.multiple-type-loc-record IN LOC 42 7 19 S 126 2 25 E 20m
+test.non-terminal.multiple-type-loc-record IN LOC 43 7 19 S 127 2 25 E 21m
+test.non-terminal.multiple-type-loc-record IN LOC 44 7 19 S 128 2 25 E 22m
+test.non-terminal.multiple-type-loc-record IN LOC 45 7 19 S 129 2 25 E 23m
+test.non-terminal.multiple-type-loc-record IN LOC 46 7 19 S 130 2 25 E 24m
+test.non-terminal.multiple-type-loc-record IN LOC 47 7 19 S 131 2 25 E 25m
+test.non-terminal.multiple-type-loc-record IN LOC 89 59 59.999 S 179 59 59.999 E 1000000m 1000m 1000m
+test.non-terminal.multiple-type-loc-record IN LOC 90 S 180 E 0.01 0.01 0.01
+
+test.non-terminal._http._tcp.a IN SRV 0 2 80 www.movie.edu.
+test.non-terminal.a IN SRV 0 2 80 www.movie.edu.
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN SRV 0 2 80 www.movie.edu.
+test.non-terminal.multiple-type-srv-record IN SRV 0 2 80 www.movie0.edu.
+test.non-terminal.multiple-type-srv-record IN SRV 1 3 80 www.movie1.edu.
+test.non-terminal.multiple-type-srv-record IN SRV 2 4 80 www.movie2.edu.
+test.non-terminal.multiple-type-srv-record IN SRV 3 5 80 www.movie3.edu.
+test.non-terminal.multiple-type-srv-record IN SRV 4 6 80 www.movie4.edu.
+test.non-terminal.multiple-type-srv-record IN SRV 5 7 80 www.movie5.edu.
+test.non-terminal.multiple-type-srv-record IN SRV 6 8 80 www.movie6.edu.
+test.non-terminal.multiple-type-srv-record IN SRV 7 9 80 www.movie7.edu.
+test.non-terminal.multiple-type-srv-record IN SRV 8 10 80 www.movie8.edu.
+test.non-terminal.multiple-type-srv-record IN SRV 9 11 80 www.movie9.edu.
+test.non-terminal.multiple-type-srv-record IN SRV 10 12 80 www.movie10.edu.
+test.non-terminal.multiple-type-srv-record IN SRV 11 13 80 www.movie11.edu.
+test.non-terminal.multiple-type-srv-record IN SRV 12 14 80 www.movie12.edu.
+
+test.non-terminal.a IN KX 20 kaku.google.com.
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN KX 20 kaku.google.com.
+test.non-terminal.multiple-type-kx-record IN KX 0 kaku0.google.com.
+test.non-terminal.multiple-type-kx-record IN KX 10 kaku1.google.com.
+test.non-terminal.multiple-type-kx-record IN KX 20 kaku2.google.com.
+test.non-terminal.multiple-type-kx-record IN KX 30 kaku3.google.com.
+test.non-terminal.multiple-type-kx-record IN KX 40 kaku4.google.com.
+test.non-terminal.multiple-type-kx-record IN KX 50 kaku5.google.com.
+test.non-terminal.multiple-type-kx-record IN KX 60 kaku6.google.com.
+test.non-terminal.multiple-type-kx-record IN KX 70 kaku7.google.com.
+test.non-terminal.multiple-type-kx-record IN KX 80 kaku8.google.com.
+test.non-terminal.multiple-type-kx-record IN KX 90 kaku9.google.com.
+test.non-terminal.multiple-type-kx-record IN KX 100 kaku10.google.com.
+test.non-terminal.multiple-type-kx-record IN KX 110 kaku11.google.com.
+test.non-terminal.multiple-type-kx-record IN KX 120 kaku12.google.com.
+test.non-terminal.multiple-type-kx-record IN KX 130 kaku13.google.com.
+
+test.non-terminal.A IN CERT 65534 65535 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi
+ WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl
+ d80jEeC8aTrO+KKmCaY= )
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN CERT 65534 65535 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi
+ WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl
+ d80jEeC8aTrO+KKmCaY= )
+test.non-terminal.multiple-type-cert-record IN CERT 65534 65535 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi
+ WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl
+ d80jEeC8aTrO+KKmCaY= )
+test.non-terminal.multiple-type-cert-record IN CERT 65533 65534 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi
+ WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl
+ d80jEeC8aTrO+KKmCaY= )
+test.non-terminal.multiple-type-cert-record IN CERT 65532 65533 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi
+ WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl
+ d80jEeC8aTrO+KKmCaY= )
+test.non-terminal.multiple-type-cert-record IN CERT 65531 65532 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi
+ WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl
+ d80jEeC8aTrO+KKmCaY= )
+test.non-terminal.multiple-type-cert-record IN CERT 65530 65531 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi
+ WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl
+ d80jEeC8aTrO+KKmCaY= )
+
+test.non-terminal.A IN APL 1:192.168.32.0/21 !1:192.168.38.0/28
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN APL 1:192.168.32.0/21 !1:192.168.38.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.32.15/14 !1:192.168.38.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.33.15/15 !1:192.168.39.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.34.15/16 !1:192.168.40.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.35.15/17 !1:192.168.41.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.36.15/18 !1:192.168.42.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.37.15/19 !1:192.168.43.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.38.15/20 !1:192.168.44.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.39.15/21 !1:192.168.45.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.40.15/22 !1:192.168.46.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.41.15/23 !1:192.168.47.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.42.15/24 !1:192.168.48.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.43.15/25 !1:192.168.49.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.44.15/26 !1:192.168.50.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.45.15/27 !1:192.168.51.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.46.15/28 !1:192.168.52.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.47.15/29 !1:192.168.53.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:192.168.48.15/20 !1:192.168.54.0/28
+test.non-terminal.multiple-type-apl-record IN APL 1:224.0.0.0/4 2:FF00:0:0:AAAA:CCCC:BBBB:9999:5555/48
+test.non-terminal.multiple-type-apl-record IN APL 1:224.0.0.5/8 2:FF00::0/8
+test.non-terminal.multiple-type-apl-record IN APL 1:224.0.0.9/12 2:FF00::888f:0/24
+test.non-terminal.multiple-type-apl-record IN APL 1:224.0.0.13/16 2:FF00::7777/72
+
+test.non-terminal.A IN SSHFP 2 1 123456789abcdef67890123456789abcdef67890
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN SSHFP 2 1 123456789abcdef67890123456789abcdef67890
+test.non-terminal.multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67890
+test.non-terminal.multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67891
+test.non-terminal.multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67892
+test.non-terminal.multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67893
+test.non-terminal.multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67894
+test.non-terminal.multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67895
+test.non-terminal.multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67896
+test.non-terminal.multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67897
+test.non-terminal.multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67898
+test.non-terminal.multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef67899
+test.non-terminal.multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef6789a
+test.non-terminal.multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef6789b
+test.non-terminal.multiple-type-sshfp-record IN SSHFP 2 1 123456789abcdef67890123456789abcdef6789c
+
+test.non-terminal.A IN DHCID ( AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN DHCID ( AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+test.non-terminal.multiple-type-dhcid-record IN DHCID ( AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+test.non-terminal.multiple-type-dhcid-record IN DHCID ( BBIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+test.non-terminal.multiple-type-dhcid-record IN DHCID ( CCIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+test.non-terminal.multiple-type-dhcid-record IN DHCID ( DDIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+test.non-terminal.multiple-type-dhcid-record IN DHCID ( EEIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+test.non-terminal.multiple-type-dhcid-record IN DHCID ( FFIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+test.non-terminal.multiple-type-dhcid-record IN DHCID ( GGIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+test.non-terminal.multiple-type-dhcid-record IN DHCID ( HHIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+test.non-terminal.multiple-type-dhcid-record IN DHCID ( IIIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+test.non-terminal.multiple-type-dhcid-record IN DHCID ( JJIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+test.non-terminal.multiple-type-dhcid-record IN DHCID ( KKIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= )
+
+test.non-terminal.A IN SPF 'sfasf'
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IN SPF 'asdfghjkl;'
+test.non-terminal.multiple-type-spf-record IN SPF 'asdfghjkl;'
+test.non-terminal.multiple-type-spf-record IN SPF 'sina;'
+test.non-terminal.multiple-type-spf-record IN SPF 'cnnic;'
+test.non-terminal.multiple-type-spf-record IN SPF 'jkl;'
+test.non-terminal.multiple-type-spf-record IN SPF 'c=sina.com.'
+test.non-terminal.multiple-type-spf-record IN SPF 'fw=fff.com.cn.'
+test.non-terminal.multiple-type-spf-record IN SPF 'ibm;'
+test.non-terminal.multiple-type-spf-record IN SPF 'dell;'
+test.non-terminal.multiple-type-spf-record IN SPF 'google - microsoft;'
+test.non-terminal.multiple-type-spf-record IN SPF 'leibusi;'
+test.non-terminal.multiple-type-spf-record IN SPF "v=spf1"
+test.non-terminal.multiple-type-spf-record IN SPF "v=spf1 -all"
+test.non-terminal.multiple-type-spf-record IN SPF "v=spf1 ip4:192.168.0.1/16 -all "
+test.non-terminal.multiple-type-spf-record IN SPF "v=spf1 ip4:192.168.1.1/16 -all "
+test.non-terminal.multiple-type-spf-record IN SPF "v=spf1 ip4:192.168.2.1/16 -all "
+test.non-terminal.multiple-type-spf-record IN SPF "v=spf1 ip4:192.168.3.1/16 -all "
+test.non-terminal.multiple-type-spf-record IN SPF "v=spf1 ip4:192.168.4.1/16 -all "
+test.non-terminal.multiple-type-spf-record IN SPF "v=spf1 ip4:192.168.5.1/16 -all "
+test.non-terminal.multiple-type-spf-record IN SPF "v=spf1 ip4:192.168.6.1/16 -all "
+test.non-terminal.multiple-type-spf-record IN SPF "v=spf1 ip4:192.168.7.1/16 -all "
+
diff --git a/tools/query_cmp/zonefile/example.com.txt.signed b/tools/query_cmp/zonefile/example.com.txt.signed
new file mode 100644
index 0000000..a269535
--- /dev/null
+++ b/tools/query_cmp/zonefile/example.com.txt.signed
@@ -0,0 +1,6858 @@
+; File written on Wed Mar 9 15:16:51 2011
+; dnssec_signzone version 9.7.2-P2
+example.com. 86400 IN SOA NS1.example.com. root.example.com. (
+ 2010091701 ; serial
+ 3600 ; refresh (1 hour)
+ 900 ; retry (15 minutes)
+ 604800 ; expire (1 week)
+ 3600 ; minimum (1 hour)
+ )
+ 86400 RRSIG SOA 5 2 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ e192JzLhvELznlN3LpQfVh6L1ZYsL0maA39A
+ CN3hH6sorsleyLMX8imUmK88yCo/QaxLC+yx
+ sSe3wJT7W+Jv2dBQFb6HoEBYp0ESXvwoGz9k
+ IJOB2bEs9fwXmrnb/Km+T4HvS0yqUwCt+/06
+ yoCX/S95h4a6vexPpNZhhzVvpLw= )
+ 86400 NS NS1.example.com.
+ 86400 NS NS2.demo.example.com.
+ 86400 NS NS3.noexist.cn.
+ 86400 RRSIG NS 5 2 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ lxHd2KDisaKQgOO8Kc1eWr9PO+sWp4fUEWCb
+ +TUhy3iEvW+zLmrn+QUpN/dT4PM2t9kMtJRx
+ i1uB+tm6pD2sbCGaVTkW/k+mJeCBfLsQmlqK
+ qXIRX91AhRrFhJZtmK3yB/dT5zhdRxZGIviC
+ kL7BhU8e+RzIxli6bdJmkKFYcqA= )
+ 3600 NSEC a.example.com. NS SOA RRSIG NSEC DNSKEY
+ 3600 RRSIG NSEC 5 2 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ pU4PI8k+ay11yVQXuA8W6vjqGKod1wBIr20B
+ ZSK+nIAzpULXJ5CjcGxKSOkTglPDFQxY+UVY
+ ry4w6wJUF7tYNrGQYfRpOxgVLdv7NGNS+0fn
+ DXf9PsHj7V+5uacDo2peby01pmU5S6Ji0Wwt
+ iNxmbk1/GP7icCsFLGuO3jrPrlY= )
+ 86400 DNSKEY 256 3 5 (
+ AwEAAdCIGyYHX9ul1AYjflqR2dKMsCPoxt3+
+ nuUDCf2M9jxlY5H/ExPw7GcFAtNqbTN9fA50
+ d3zQ6z4wjR9T9kuEdfUgcRR5+RcgIkONZe9e
+ 0ULSOUgsVp5Jyx6avWBnI6RYGp3Vozs2HL7E
+ MobFrj5FWg4ipW9Cpbzgi+QR/ZM8bwEr
+ ) ; key id = 27366
+ 86400 DNSKEY 257 3 5 (
+ AwEAAZpJP70EU7QWkGo6r7oB1t/jYwt75b/G
+ qVS+a4KalZQK2WJul4dwQXoxg3byRKuYy6l8
+ 03UoFP8NtyTWPr8omGopeyZ0/0jN0QdbngoI
+ Bt2kC3i61fDGGoslIjnFK4ucWVXk+qROoqOu
+ 4ZFSOuULNItJl0j1Zb6bwCzhXx5Un0/E3Qnj
+ Xj2QTgmMDbZCRpa1Uv8XUc0VGluluB2YLXBT
+ 6BrN5YsVBK9TUNky0b9pUX8S7bfxXcyPd/9d
+ gJYkSZ6tShyctfmgRpeSa/BgYTOD2ymsQv6N
+ RdGk82LTHY37N8h1VDGIN/X+gfRZzm5VPCZS
+ CT+e/8sknTH5pctKwsQxnxc=
+ ) ; key id = 12133
+ 86400 RRSIG DNSKEY 5 2 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ NrkXeHhw2VCgU4KA8Pk8XBMisWb+RyQGSaAO
+ hk1Zkc0skthKEcCquu9z+zQ5G7lVc8zws8F2
+ 3dOdKEa0o/TFlmSH1XM5W3xMdHS+E0/gqEhK
+ y3qX8w9sj/eA0f6tNguQ/iCZtwKsLJsLxCYy
+ WPcg7sjsjKUO5tJ7EFsAFG8jdiw= )
+ 86400 RRSIG DNSKEY 5 2 86400 20110408061651 (
+ 20110309061651 12133 example.com.
+ WaHcL7AXsql6tPtnSudQT/wZnYNRKnMLSF5t
+ IcUgJaRGd8gZ/wfJR4lumwc+4NBDvyL8kNs+
+ FscFXQ1izXlPePvsCi4Tw6KYIj/wNoBNF52n
+ DprUpDIjGjXyZdQ2yWu57z8MQD4jx1xt5DkP
+ hRrdNPv3yeDIvY9/s1SsXscIY5SuEt5OpP93
+ 67Igc75uHxZFjBjQVaetf4zvwupafuRmlg/6
+ LmiGp0lGE9VTsG3PC0bCx2umzfv6LLX+Ibtk
+ XrEOc4S2YtJ5HbY43n9hH0Pg5eEOahDhlJSQ
+ tt7sw3NxgGMRbMYa+CGJ3GOBvHBhjNayCvxd
+ sMD+GHt4XBto4GCmQw== )
+_http._tcp.a.example.com. 86400 IN SRV 0 2 80 www.movie.edu.
+ 86400 RRSIG SRV 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ QcFXkDEyx+Hv6b3jN6+T9MHNGX5hPsGmqxCy
+ 7GHjbJ8wvz2AJHEb4YdLnaRrXO6fIotY4I07
+ 1ArIDlUPcF73VP6Hw5+U8vAeKc1Tye5L3920
+ lXNFkWmO1qif6qlvAeLwJEbb03fhNvUcSS4K
+ 6InXSaWu4Jqap7psqchJqgqxLrM= )
+ 3600 NSEC test.non-terminal._http._tcp.a.example.com. SRV RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ r5EXPMKrr5vRfjFvsgO79Bvd1UPVmP8TipWq
+ xly+WeQT7GkO3T9i3md3c7/JpJGy+cMErfzm
+ qkt70lmHkY3+e869hQE5F2YqOi7fZm/KRy/C
+ 28rw155VWfdWIZDcmXlPJA1EsK08B1/9Xio/
+ F28cMr7hN4ehtSy/kMDWIMFQ2hY= )
+test.non-terminal._http._tcp.a.example.com. 86400 IN SRV 0 2 80 www.movie.edu.
+ 86400 RRSIG SRV 5 7 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ CXIgvla4xVw4B+QRaARIeBupTwGwD4eZGrM/
+ v4urp7jGUrkPgqAJmk7Q4oAzN2szcWYFfawh
+ JRaMetY2HLocSDLvZDZcVWGvUM8RU1pu+SDD
+ u+h0Lwwb1AJghkCnpvudXR1K9cV0k5xStxXA
+ nJ5QYEvqKmvJ4Yo7hWfiq4RImfI= )
+ 3600 NSEC test.non-terminal.a.example.com. SRV RRSIG NSEC
+ 3600 RRSIG NSEC 5 7 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ OpgVpnelCCcv8mhJenxv/tFhTz85nT0X0NVa
+ HWJWpIuKzQQ3MwthdywY7Ugnm9FX+W4uWhyA
+ /VGzYHkoZz9Rf7Fm43lk6JO8lu9B3vC6lqZo
+ PBzYgvAWtjxKOrAuaDGPtHVhN31JHl0P0NUb
+ T1Bgqaa4rQUnYqQeWJdQs72Rnv4= )
+A2.example.com. 86400 IN A 192.168.1.12
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ CymhsyPRk+xv+OuaedapHOXJUf940neNT32N
+ DHyXirUBitJdbVzzilQBc7xVdbNSuwNcfNwC
+ PpwyAqFzmQ0JkzEpiyNxDHR/5weAkolKK5RT
+ 1Inne9XtZmf9z7Q1zDOBoXqLA8e4ZcnlQDhl
+ FsuxLTVp3XkuMJOszJVO1Gpi49A= )
+ 3600 NSEC test.non-terminal.A2.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ gfKvkjuJaKOuHyvrLX7YPbiEvz4W1hc962co
+ 7VrGrNa9qMAbW32zuLCirMefFbE/k19tJ6QJ
+ 4bmmi7m3NXGFbrTyk3vRTX+G75h4SneG/fzT
+ qGTCNzjCxYGSFIn2RKUNZmoIRa8yu4PAjnDf
+ KUgTsWBk4+JpAnvtjhLlP9otQpU= )
+test.non-terminal.A2.example.com. 86400 IN A 192.168.1.12
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ EFVi2Os1Vd/mEYv6HKVUeR+GeMHS9NuqHfSQ
+ M9LXZ0jpSRO9sdTqxg+tscacqmO/JOcGy1nM
+ 1mtSVnEWPj5KGdWOcoab2/JZSKaK+EnYQ9VP
+ OQbu8jMn8c0WL8L4QmWzbE1PQO/8nlHVCCwf
+ hzBtBmsn+btfAtD51h98phvU9B8= )
+ 3600 NSEC AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ I+nGXCfTaUS3sSFm6cVEvVJV5MxZ3BKkUV+z
+ 0JGZjxsNfrFJLh2lN0B4SyPVRIFri5cS7wPA
+ bhNJKILBelGpKo/nbsUuJTU56Dh6iRBqkI5F
+ HThZeRfcn3aSiBNU8FN2gl6Q++3ITwPbG/Cr
+ 0vZNVR3J767S67znc5m6IbhAHWo= )
+test.non-terminal.a.example.com. 86400 IN A 192.168.1.10
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ fZ2hxEo47ZfBk8yyZpkGlqkzopHpU2NUNsX2
+ OPL9fHbhjOs0KCKs1dLH3csTmXyc3bTZAk8O
+ CF5ZX4UHHP9CsOYZULDTdgi3H43tzb8+XrRn
+ flJnwl3sPUEooPfaeC+YYKYwvBCuoIeM3HOr
+ IFWJplCI+R321AoeCdiEKZE0HCY= )
+ 86400 WKS 218.241.108.4 6 23 25 53
+ 86400 RRSIG WKS 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ PTSaoyVOsTqxymccZOPbFEXJ2xhYiOYBoYAE
+ JiPJxb/vUNxPoJpQJlRwbvqylKhC0WmSSvkp
+ r1+8dCjvt3bOq/1f9vAurG1lQu1yoC7Dy5Mm
+ ybxMBqDjxH+XReiE14BiRAhPBrIOH1s+5Boh
+ WqkOFjwhr99l47eAOlxLSN/iIsM= )
+ 86400 PTR 1.1.226.159.cnnic.cn.
+ 86400 RRSIG PTR 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ OVlX74fHDc1A6uq7j/oTzC9BUNuPgGjxh6oS
+ T8XoH8NEQRAcUpd+7b4Usm5lI3mAVK2+0OtS
+ dt3jdfKXgMVAOeGA5iM7HZSgsUMxle2oB5ET
+ 5Nn1RpNOvGdMswuvEdlSc4GmSoGxTlB2MFkj
+ g3nBa0H3TTZCZSBRg+FGLIh9LRQ= )
+ 86400 HINFO "VAX-11/780" "UNIX"
+ 86400 RRSIG HINFO 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ y5U+AM4RKKDRgvetODc+KSA0sfIdBg9QPW+M
+ kPKkRhM1yE+bCn+/68+npVMPYOis16E1FYCd
+ qrrWaiEUSjO62RBeKLLEkgiMhstMUYXhxHue
+ daKWN1dKQ01D9jMQbtsmrHs6cl//xunWhej1
+ qQjY0lRtxv1GVky4xfQdom6LR1I= )
+ 86400 MINFO root.cnnic.cn. error.cnnic.cn.
+ 86400 RRSIG MINFO 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ H8krZdNDu6MbrnKUVk2+KQLQhvVgQBQ44Zqn
+ vF66Eek0huDgPqgeBOV8VNYrrNbKmWMqjstu
+ zZgC3fJRQ7Y/yYAulUkwQOEQ2ihBWmFQYIK0
+ JyWJEaFe5IKYe0j/nm9jbSQezv1AxfumUpfv
+ GDqpd9POZqo1lB5fglkXCdtvEHo= )
+ 86400 MX 10 mail.example.com.
+ 86400 RRSIG MX 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ lyq50HUzixKIl4Yl1LgPb1g8LWI7dG2mFhdU
+ 2a5Hw7t0Sm+NkZdmiV+xc0hyzAaMXAJj2FOy
+ sMOj/X/jolaFP3yMo5sC5IdIufioudbmmhSI
+ 62u+D5/EOb1ccDvnZvIzsEGaSJkF0ImeRSSp
+ g28NZiDsInPfT8BYGYoVE9aV8kc= )
+ 86400 TXT "ms-dos 3.0/6.0 ?" "google-app_,\;!@#$%^&*()[]{}=+|`~'<>,."
+ 86400 RRSIG TXT 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ytPnH7CKSUlhysvADRzna3JjFjv7qmsm+hcx
+ J2hUqu0DDpfDqE1OLqbXh+Egka4GO38T45IS
+ QfCkEB04wKJIsTVI5gHhJCclr0hPNWUtHudu
+ lVe107AVodRtRBu4mI6CQZko6/eOPDY5bA6s
+ 0ngLEv5paCGqhSMdScUzMTl5tDo= )
+ 86400 NSAP 0x47000580ffff0000003210999911112222333343
+ 86400 RRSIG NSAP 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ WCWGjMGQ0mLM9rJ8MrzW8Oh3ifigxg5L/bYA
+ 0OVoy6qHUHgwJKQLFPywBeK0MFvN69U6iYkC
+ rEspFiN6Dvpu0GvIonFHSNrlzK5R8AiNHo1m
+ 9WrdcljAti7pdIwlqJvzPJ0uxMZ+FbdVgXl1
+ FgigqOvzFk1hs+6MdFtgrcB4sEY= )
+ 86400 PX 10 ab.net2.it. O-ab.PRMD-net2.ADMDb.C-it.
+ 86400 RRSIG PX 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ZXO5G1J9CcoF6hGd+gZzD8T/nF1MIz5JqAEE
+ qIpDX72dfHEVginHhzm41aSS/R2krjwJKt6p
+ ny/2af/e9nq3PkKHDtZug5frLkf1pkJko4RF
+ u5Kj27gPKfKuxUIRRp095nk45MVk8B0+eiMb
+ rsNAdOO/g2/BLnxqpXj78BdCpjY= )
+ 86400 AAAA 2001:dc7::1
+ 86400 RRSIG AAAA 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ MnTVEK0eSa1lPIpYJ6O1laVlrs4OLvJXZzKj
+ awNcWQOZVpaPkPH33ktGGfpY+CdsvHaTRgv/
+ me9Zw0+dlq9Z8prGi/a6ip1gR6wkONY4Jo06
+ l5YHvDt7P/43wisGsc/dl+TRS3njH2ZOdipo
+ hsYPBQIQem/PS6IziuiY+jgk59M= )
+ 86400 LOC 32 7 19.000 S 116 2 25.000 E 10.00m 1m 10000m 10m
+ 86400 RRSIG LOC 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ iG0R8q8YpHJGRSjpopJzPEwflaKja+04lnX8
+ kEm99VZzqOniwmiNhMGZTovRq2XwnAT4TkTv
+ g2Uak5vhAZG7CfTSwlM6Iw7IJWtEtH5hqRFZ
+ pjvm/DVlSb/Id4gyqxggrgbI+39VinJ0XSXV
+ lx7hy3hwJ/QvU9Qa0TCbwpPkOrM= )
+ 86400 SRV 0 2 80 www.movie.edu.
+ 86400 RRSIG SRV 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ hCqwgDea7OtI2Hcxm7Apno/MT2h57VzxwvfU
+ Je1dOXkD2lQUzwY4EneOG5bbyybt5QaZ7gy1
+ WjwiS/mJGREj4LPpgefH+UDr445OjYEimO8t
+ 3y0mMkun6O/pUhH1siaE0NibNVUJhTkd4h2o
+ Qoen+g9qHy9sltBugqPlmnJorNQ= )
+ 86400 NAPTR 18 35 "Au" "siip+E2000U" "!^.*$!simmmp:information at cnnic.cn!" mb.example.com.
+ 86400 RRSIG NAPTR 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ aoJZOwZeZxhNLM9Mo1imybr9y0C7wTwiUNbe
+ pgjjYUC+gy83gq+bCH/K3dvr6spU/FleNOEg
+ GuM5VYW6UqAo/S7baLj/xrT+5F3W649E2LAK
+ 0l4q9PQhZOaMnj/+LNBDeqG2bqOYTcNjDFvb
+ fvzvCJZ8jmLzlwlVq37k+HsZuMs= )
+ 86400 KX 20 kaku.google.com.
+ 86400 RRSIG KX 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ OKmV0m/7w+qPNWdVE9PfjoTSffaECcKfraMT
+ ALmtgpJCXDa9UsRyUDmPdeMX90J65p6yyO57
+ Lgta+5x5qrslmJR8EyBdUHv8Stp+lnOOgI4r
+ niPkUN//MhzcC9NXX05S3bTEyYR/COpoqwKU
+ pGymTYS7rC/LmizMEcWa0/JstH0= )
+ 86400 CERT 65534 65535 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45Ik
+ skceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVf
+ SCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY= )
+ 86400 RRSIG CERT 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ QXp2HLZU535WhcxbJjkvbnTFv30VmEa5STm3
+ k1daniwZm0enyaFfZ0TnuxFCgGYBGnOo/E94
+ AUxQ/pvRBxbtaqLaDLKJ6bnTrhjT6onaffXZ
+ rgV4tXSBWSYrQR33fowzXwjbOWzhYT7RyI6z
+ vj/Tm33hr2hxjbChXZivR3SRx5c= )
+ 86400 DNAME sub1.cnnic.cn.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ jTN7VyQTJ040YKb75yXhTsOIa4Y4arJFKEp8
+ RFV2U3OT6e/BP8unmO9mYMTJqwdRkxA0ZBJx
+ viH400eZ/4OBntXHz+iTE+JsBwFo+KOQuZQb
+ WqTNJGUFyoSKttWw3c6T8SiExmGV48wXj+xO
+ dVtHIpNTrgFCOkSpg8Y8LuC3MRs= )
+ 86400 APL 1:192.168.32.0/21 !1:192.168.38.0/28
+ 86400 RRSIG APL 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ FSLqGFSxKyRNgAS8q0vyMJZpLvDPiB00DLC9
+ prD8SsIjRytdltqQQnpZWyE3d12OapOkC+6N
+ ONCvTBqDSb4GHXfDmqMeIStAc6ef9LeqNEMW
+ HkinGJgnvZTakSKWkdvauJw9mEhCvjVtT4ah
+ oRpTRR3cmakjM/mZaXwahejfj5g= )
+ 86400 SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7890 )
+ 86400 RRSIG SSHFP 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Js5Zk0o7ZllVXt0wLGGDtTzK0MFOa9vk8i71
+ ihCVh9En+z9+4ZeV4fWliO+4owS4G+H3z5qu
+ 8iQTPCx3VAAIJzSuAxxO8KR0Fg7PXsfcxQHR
+ YgZatXLuXmUf3rSUI79YCMknwVdL1jnK3P0H
+ 3VS7iAQZ6rnln6yoMOHfN8dHMIs= )
+ 3600 NSEC A2.example.com. A WKS PTR HINFO MINFO MX TXT NSAP PX AAAA LOC SRV NAPTR KX CERT DNAME APL SSHFP RRSIG NSEC DHCID SPF
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ pTbeGiHI1nPV2vKXOXMI+v/vXGZoiK12hveH
+ p6fnnffBdbkHn9ZMaNvdeUlsDy3zPVgra9On
+ LjDonIqej3Gw7heEatRG3KEez2e/WIY3+sIs
+ PD2IctkxPOoaJgb5+3WZDserCIm0qbHeVu3u
+ 4sSZxh1KPKpRKzES3Y5EBAkTShA= )
+ 86400 DHCID ( AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 48830 190 32
+ 86400 RRSIG DHCID 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ UP4csgLTkvDnicg2qF0NO8daixH1IWQJ9Xqp
+ OMos806bqSvKhyn/Egh6QVNe+UAmnRpSobEC
+ h6jKWcEVFjhM2EtbOUUlfjNaWwRrqlPIHhl1
+ urWsWm1x+kOrQcgd8jREbXtsq65kmlTTwZU7
+ 2PR18hHvCljIHbrCkZy3WC+mDk0= )
+ 86400 SPF "'sfasf'"
+ 86400 RRSIG SPF 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ g0zsyxQnbYfe9mcugFcY6u8aBmesbWr8+BfG
+ 7NN2gCDSON8zUhW3fC9XRSL7AfDm5gHYyQv8
+ a6yNwOVNSjK6jZgaiHLarVo+RZCp9Fxe/rcj
+ TajZEfALjJXuA10nJ6d7gfpQMfGzy24bpsKZ
+ lVUZa2xp5TGdZoQS/8YkS8j7RNg= )
+a.example.com. 86400 IN A 192.168.1.10
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ElzDQoY56Lqw1pcMpRU3mQIfvDVbal2ZGcHs
+ 9wJAWOHHKdHsFLUjW2S9ouw4Sh8/9orWdlsf
+ TjsOdLnwtbRS+dPAJW5LvFsnURtUAPzr0/fH
+ scBNgZQPeqQs79B+qudK62Ut8WP6ZtvvY6Go
+ 1NLlZN7svt2D6KONYflAPi5jbPg= )
+ 86400 WKS 218.241.108.4 6 23 25 53
+ 86400 RRSIG WKS 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ xX9GEZOOAhRdohwFTwzcFSYCV/GiTmAHx5uA
+ c7XSZ/+Tp7POJw4WAXNdYPBFb6rELOCdfjrj
+ MK79Nm6mQYp94BBd3CULyrvZCrPFOrEyXda+
+ LVCXrV1WAnedZPqyQqsCivoUBMDCHXgFA4EG
+ t4rZFFr9YRIkiD1XMD3eNYgrPXE= )
+ 86400 PTR 1.1.226.159.cnnic.cn.
+ 86400 RRSIG PTR 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ vYg08ocmOC1FF1Ws75r6TLX/3QpD5WA2/KEb
+ SnTtlEf7scvN4piy8XR4F40ujvGTVZqom/Zu
+ gt1CZ1RL38A+PdGsbUU03VBPxUHmY0DsMlxW
+ H9fVbFU3GxF3hfabOSYSRL7qU1xOmnz+a7R7
+ r2nL7YWDRJ1ogjA/G8+YKvQeWeo= )
+ 86400 HINFO "VAX-11/780" "UNIX"
+ 86400 RRSIG HINFO 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ vC57e5vliWEWZuqBL8U5NChvNxqJM+3xgem1
+ scN8zllo7wrScrlKDVwjVwbxDOs26iAcMkrw
+ C/iPV5cNh4JQwL4zjALWtwZWm9ndvprQjzex
+ 9HteKJj7YPWZC37BaUAhoC0z4E+RkW+1Ao6e
+ MP1HjSr6jL5VafrgqKApT8XWQxM= )
+ 86400 MINFO root.cnnic.cn. error.cnnic.cn.
+ 86400 RRSIG MINFO 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ePWNl3irfoaR9Vh5SfkL1nPi8kWqDLXhZUzo
+ Qqjs71fV3Pudc2iMLXLjO54VlikjnfKVwXLk
+ QVbjdGfVQne5EbgsSif44ulb6cIB/NDj3pwa
+ A2L7VARae55qHCdJ4VPHYOlHdvLkLd14U480
+ grKhu8jACjKhFbIzRo1ZNidRizE= )
+ 86400 MX 10 mail.example.com.
+ 86400 RRSIG MX 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ JlFRRzmvOeBaOETnpTrWAgTSYr8CkX8mhY9e
+ Cm7/RdfAn43o2TeZLvMYQcKsu5oBw3NfUWnw
+ UKr0Glsu10ca6029Z6y81m4+tRtmH7XYOPsC
+ vCPxMMyyTi3kszMwLky5Ac2IrPucZYszYf9f
+ a8fekbydJTBhnHqFM+9RjwmYbMs= )
+ 86400 TXT "ms-dos 3.0/6.0 ?" "google-app_,\;!@#$%^&*()[]{}=+|`~'<>,."
+ 86400 RRSIG TXT 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ uU8x5VUArAYXSK4D6ivpwJmvmu9kHTAPSFkk
+ 4Yph04dWDV5Ae+6KIy1QidHPiQPZ7dX6ZTIW
+ lPaGMD/yH4gg9xKTM0vaB/SmFQnZhb3GMEYu
+ Ucx9G45jm0BOQs0eEhK+juo+dCOYWdfUb7sl
+ GqOtB/DBkal9+voLqsJOdcgjEco= )
+ 86400 NSAP 0x47000580ffff0000003210999911112222333343
+ 86400 RRSIG NSAP 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ aA0ZJlKJW7iv0NFwmZEJmvaNJ71hI04MIhaf
+ KELGz5IgnIdqntxCHebCcgjW0fvGMF8FgtqU
+ NqBgjW2lXBiJGGxRIxgJXUmohREN7LlnGpNx
+ 816yK3SGTYrxecpub0nZeXb1mbZKHcb+W74a
+ opelKvJioG26vIC7MkxwkawsAW8= )
+ 86400 PX 10 ab.net2.it. O-ab.PRMD-net2.ADMDb.C-it.
+ 86400 RRSIG PX 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ uvJp85WdHc3ajrwC9vGedFEpQgQgXrEs77CD
+ sNo9wUyMaCjVHHxoslLsy+eOYY4BqIxapck1
+ pgwZ02osNrBNKjfvkXwP2ttA2GsG4Gutwgk8
+ B1fyZWe4prvQd3WEPkqkRTgQhc/nEQ6DKQ2N
+ vTyk4pUVHEX/CxIZIGE+kzBx6gk= )
+ 86400 AAAA 2001:dc7::1
+ 86400 RRSIG AAAA 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ QEq+y/E6z8UjAfmzaTSwjrdTpnv4kUmWbv5+
+ oDUybEVVKulj/kDpVb+tzBTRUfqUHuVQRe12
+ b6fChfcTZlBi01nEXkAbFUoo0RGCY9GFCf4M
+ wDSomX0LzZHBtlPVG3QQ8Z1vNSN0efrJ3SWT
+ Yu7bOpughjQ4dP3Sc0LGQN4lLbw= )
+ 86400 LOC 32 7 19.000 S 116 2 25.000 E 10.00m 1m 10000m 10m
+ 86400 RRSIG LOC 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Sp20GwyCuoEnQL31m29xkm5mjiEWKGNm7DGc
+ 04As336Y6BoSKsXUnliuyIMoZ1a5XrW/3Yxu
+ FrKk+XRSCafO6Fi9MxnrSdGBwgthwrEb2GgL
+ v6tAAHthmaDJCA5DAA+GZnMGj3bKIq1Lneez
+ xexMWbPbtFK1w6gqeygY8pgLMOQ= )
+ 86400 SRV 0 2 80 www.movie.edu.
+ 86400 RRSIG SRV 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ pqpYwqtFQBWB5F2plUrBE3D1VjdK5gr3qn0Q
+ VnYsGwCUo8LCpmxCpn009KgXf91RcSVZwRJ0
+ sexp50fh8Oo+so4M7C0sCSxe1XqhS/oEBBxK
+ z3tpM+ylYADwJMmMfMmflTM4je6FQIK5k8yO
+ dVYPJ2Y6Xkd3uuUDYIk4NOu1iyM= )
+ 86400 NAPTR 18 35 "Au" "siip+E2000U" "!^.*$!simmmp:information at cnnic.cn!" mb.example.com.
+ 86400 RRSIG NAPTR 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ OmJUFic+v8NWX0OxDm9U8+ZBtspVquQFXvee
+ iEhrPN8ms6/zKOqe71rhnS9da/9Gs+bDBSde
+ yARIDxYWRYSU5x2dR9nrWqWY+XIoqYI+vdqW
+ cg2HrHa5dCWzvI2MIcyp+A1m64g4pBF5xhUn
+ 9mOusmK7YLi22e472GXgtAWh2Nw= )
+ 86400 KX 20 kaku.google.com.
+ 86400 RRSIG KX 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ SBkT8DPqeIW8+qEVjgHRWJSdMqucv3bA/TBh
+ eNFVyp6u6VW0Dq2YRKIibVOqGYIAchFq5Ifp
+ YwqV8vZxskQQpmSgKKZhNYEtSoou3ORG3N27
+ ntyDCQY0f3j8+6N2xcI16TyhgDjP8WYKx2Mr
+ zWPyLHCfptWHL4OSKn4LmmxMeLQ= )
+ 86400 CERT 65534 65535 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45Ik
+ skceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVf
+ SCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY= )
+ 86400 RRSIG CERT 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ YqH31YUxUbrlLoyNCuHFLow01TqCqiflwGqK
+ nnjzEpHF5dbR8gkCHRp31UMed1WcoHxpoBeg
+ JfpD2yAqzyfATYaknu5s/l1zoYiOl+kU/pN7
+ PqquasRTNKzo9PSfD5NmXgLQ2I/hOjpxHNvy
+ NwT5VxCU5TKGIMZw1EA2Gfgw6pI= )
+ 86400 DNAME sub1.cnnic.cn.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ DXSCV9RKokxzfbsgvDXIRTpBvMpWK5FozrMY
+ giy7qDSoU3akrReo7O35psp2+8Rihvq5HyLJ
+ zAO0MMZXIyW2Bd2FxLfSxpLtAtDkdeQ1/waj
+ dDkK/dMkY5uz+rVFnt16/g2G7ShDyInc8Nlf
+ 4HH3nIU2YClg05mjeI3VtdRM5zM= )
+ 86400 APL 1:192.168.32.0/21 !1:192.168.38.0/28
+ 86400 RRSIG APL 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ If5+oBn2NU31TDxoKb6MusC5RFVX1hWah7OJ
+ oXHkP33tEU70Ovdh0n1JLPMjzdnedrspjYee
+ prQjpiW+/KaCfS7+xugXZ+45FWfzQm5Ocs+/
+ +iydvftx4RrwFLih0jUjVfBTodCpA1dLb0Z5
+ 9x5O8S1b6iAQZXyQLn8+0mCAN9w= )
+ 86400 SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7890 )
+ 86400 RRSIG SSHFP 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ PidCLCkvs9HUMw1w6bsUvdsT16a2mG2bQqhY
+ YqmUOnZ+cCntfnbcHFeQGHm2fGm7THqpPIL8
+ pwr0do+lV8Sf5YnWJer9p2KCFFsLpnZJUY5Z
+ UyRQkRm81dWPR2F7+huR8S78m9dEvoDisDDX
+ Fw5t3oSFLkco8N/vaDDThnp6dc0= )
+ 86400 IPSECKEY ( 10 1 2 192.0.2.3
+ AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6
+ BSGvgqt4AQ== )
+ 86400 RRSIG IPSECKEY 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ L8ztcLWUE8f53+4DMwE8nqQoJhVK6ywwamHZ
+ 2xltOoAzkZoYWxZsqvysxXlA8IxfG7nNYmPO
+ 0u0Ia3flUOQeI+xmAahxFCa5ztldTAfc2PkG
+ 7vMxa+qN/tubWZ77SWnxVml9xPBjPwtJ3kl8
+ tuRgiWwATeVXIinJGIr/Fl0r7Ik= )
+ 3600 NSEC _http._tcp.a.example.com. A WKS PTR HINFO MINFO MX TXT NSAP PX AAAA LOC SRV NAPTR KX CERT DNAME APL SSHFP IPSECKEY RRSIG NSEC DHCID SPF
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ w6YvWWARxQ761+8wUd+lcuW3KNibOGqCX2Kn
+ efA6I1OLq9KrAQY9KF5Qvn3u2xUBLt7p7ePl
+ cS/47WkRSysSJ18j1LnrAOyMKEUUBmZZq60J
+ P8j4Zvsspj5jjj6IwaU3EtNVravFmLzhku5s
+ /IbU1PvVvcaeGh7lnKs/0raKj+Q= )
+ 86400 DHCID ( AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 48830 190 32
+ 86400 RRSIG DHCID 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ BV2VsatEe1XAb3nUR68y0eg0HdgWzSMhfVJ9
+ w3rVj6Gccz/aJsLeBl+XFRqp8RzNvYVOkSTy
+ swWK4ddHJMng1SkdmR5e57/jSjjkqRtJ52us
+ AFgs+2/Up+0iAiaeyv9J/cW9EtXMxNJh0cKw
+ +OPdwi1Yy+kSCHS4qinfWHbRLF0= )
+ 86400 SPF "'sfasf'"
+ 86400 RRSIG SPF 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ GSHy+0Bwoe7ypHO4KBAMn2J3IoN60QR0mald
+ T8xdz0cBDR/T1EVTAeP0DAMEx+xW6E8NcOHJ
+ U3tnyR7yFmXEqMUl/2HKEZLxAQ9+sdJtVgy3
+ 9UzpygeXY43MYjPebSViPw/PUfQklXm8yyi/
+ VFGFnO8L+9zCaCwJpce5uGP+GRA= )
+test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com. 86400 IN A 192.168.1.10
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Q0zTGD3auYV/zpg18kfQlAugNsqdRqq0mVQs
+ ubZwjeVtUBjm2b96y6+h/BtxkPCKZbJrVGke
+ 44jTu0HCfLTuC8c0+OTpv6BXyi9XiFVfSl7Z
+ SnTM+mqfEDv3DlTmrGSmBuaCNnf+biMBUVdO
+ mCDpORNeLHJIwSApZsM9apJM0aM= )
+ 86400 WKS 218.241.108.5 6 23 25 53
+ 86400 RRSIG WKS 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ u4A5zuAlFFMfuVRSINxZKXIBM4Nm7okA9Wn1
+ 8kWv3K+Ec3xU82wxehQpk5qWagC4VCkNiEVw
+ WgDuvBsPcinueD+GdlNeVJ59wTKU6cqgj2nD
+ VqjB4EnSwadpqFuW+mMe8MJJYriJP+Tzolt2
+ kowEc9w15TYX3ITJNQqsRx9++Oc= )
+ 86400 PTR 2.1.226.159.cnnic.cn.
+ 86400 RRSIG PTR 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ k4DEnKmI20oKcU2oefuEhFI6axkTG+tnnl6O
+ rLKegw84qAqGT6GDKf7MCZrV1eGYR2PfjhPX
+ 9L+prQyWBKbqfPgWkzJ87mGwx8n9o9Q00URn
+ 4WDYgs7bSUyIPvzKvYHXkx+01kIhD+50BfHS
+ t69Z4Q6/PfTYwW7TfSOzuhLzPGs= )
+ 86400 HINFO "SHARP" "PC-1500 1983"
+ 86400 RRSIG HINFO 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ZCZilAKkXyQr9+hs/BlvS9zMdtpYNzbfx2dK
+ wznvSe/G63jthJufEA6N2K1rp7nWd9Z6Orob
+ dRZc1NfUIRlyD10/RQPSCFCxBg9OlHciTLQV
+ NW4KsS4kQanWTfC5DhZl6+dIy0GtNzedJdSX
+ V9PHPwJALlDBXcbsSLI3TkCrrDE= )
+ 86400 MINFO root63.cnnic.cn. error63.cnnic.cn.
+ 86400 RRSIG MINFO 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ f9NUDLDsNiu9icsi8YfmxhV02Fld7T4zptql
+ vipbGv3ovRWIOxOkFTXtz4dXVvnaZPy5IVFR
+ LbZWfFMmwsdDe/Z62BhGCfpva+e+jGpDso5X
+ t9r6JEnJKoY9d8RScTxYnh6EC6PYLxRVbdBN
+ cLaKVvZzJlI6eYdJtlBMGGr0jeU= )
+ 86400 MX 1 mail1.example.com.
+ 86400 RRSIG MX 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ QKs1t6oEUaG4p4icapLonCSxyNhwZl3DPsKm
+ KnIc5E39G1G3CNakvAPB+Ifatd/Rf/Z00Bo7
+ ChGSHeAx3fAKs4wpnM0DZNOf1D+2P94ZmIKi
+ nmadgIqAdejFWpZZP/GJq1nEyyfMlCKOIYdc
+ LMn2uM26K5SV/DrLgYU7mCiYKCk= )
+ 86400 TXT "kkkkkkkkkkkkkkkkkkkkkkkk"
+ 86400 RRSIG TXT 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Uenb1BTI/L347qz4Gxv0aZ1cHKGYIeAR/vtX
+ 7IVMEMys2aTdgVcShVmebZfpPPD/YIsEWvLF
+ u5LMbC4mSkzknkikmDCpI8sGPFHkWePxM5Re
+ gUMPPwd+3Vo42EPgrx5RuynPu93MJgN0EGuQ
+ pm6TS0xQ0XeCtjYpb30P1E/5K/4= )
+ 86400 NSAP 0x47000580ffff0000003210999911112222333344
+ 86400 RRSIG NSAP 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ a9jbOAcpAEnVMaLtCNrabbuBr65CzlhWfdBP
+ 5iG3nDi4R5UDg91u59emAHsad0CiKcryGo4R
+ GGjoTXkvirsw9huWxqNi1L6QKSU7b3kMRvBL
+ sG2RA/ivTo0rrwkWwuKDY7GFGVjfyIF4cBqK
+ tW70Iyql/rj9B8wLWRpVbLkgi9U= )
+ 86400 PX 10 ab.net2.it. O-ab.PRMD-net2.ADMDb.C-it.
+ 86400 RRSIG PX 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ krWoX+41uSgfMZaJP64PI1/z5nUHWgL0xGS9
+ uFUNJYhzv5MO6NFlwfs+10kXk/oHY8TwdvpC
+ ziFREwZUDYX3BWt80WZnch/zUA9tkldU505k
+ EjWHiDjyQ2NC15Dox+nAtkBz+XoINcepdBlr
+ uxCzzPwYgE4s23Sr0lnp4wQRTbY= )
+ 86400 AAAA 2001:dc7::2
+ 86400 RRSIG AAAA 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ PehLeS+MGsMJHjgJKPmptn/v2KA1yKDtTQ+P
+ sbK4QvaPz2S6H4tOBCYzvC0kwFoO0ZlReDQ6
+ IJBw86j7pQyHjspWsw3ut40210sIJjooAk/8
+ NGRUX1w2FKs1bxFLNj7CZia64dGsmTZdgCfR
+ UjHfmoLmuXMELzS6hWWvMWMnWvg= )
+ 86400 LOC 32 7 19.000 S 116 2 25.000 E 10.00m 1m 10000m 10m
+ 86400 RRSIG LOC 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ oV2omi7AMXSjzcjLSeSS5MeCdCed2vqrQGwy
+ HugFFpJyWn1pgrvFvKkLRlScLrL/naDe355i
+ /E7CxwtdYiBv5/n9sY4XA8j1aMCW9qoOb4Ei
+ 4PP24c2PwPrSP1FydcvoNQVuLd5ppDCC4aOB
+ fcb8mAJJ941nhSxihZTnrQeKwus= )
+ 86400 SRV 0 2 80 www.movie.edu.
+ 86400 RRSIG SRV 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ egtegXH6TootY47XJ4REvaZo9QQWOlcd7C/V
+ IDgfDu74QIPiDXtRNc/uXsNunBK2+R5bReoA
+ RPiO8Q+LxV9y63kJpY2Os6y3MQgjopO6WT8e
+ 814Jixx+5ObvLHee1Usac6dEH+4uUcPDugVL
+ PhQqKphpGPlFbdFEW6kCkDMBPxk= )
+ 86400 NAPTR 18 35 "Au" "siip+E2001U" "!^.*$!simmmp:information at cnnic.cn!" .
+ 86400 RRSIG NAPTR 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ kq7iyWWH6807fM9vOmP9nSGLjHU1VoEFHzGq
+ KYEDnqysgPbfdyQtr+omnG0gKxjDbQanJWUl
+ IG3b994/ruJeALSotx6hFdskQDQ4J+hU8Wuh
+ GrD0Zb6oZxME0hGn70ViJdAkc+qHMdSrH6e6
+ 7JazF0jmUhK1S7/EPvv68wPLcas= )
+ 86400 KX 20 kaku.google.com.
+ 86400 RRSIG KX 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ xVLr9xd5+UjL2g0Kir2DzHYzJSMMeg9X09DO
+ TutD4dDuCKVxUw81PRxPpps6Pdo++Bhh1Yd3
+ Nd9X5uM7kUBpmQeL2AY5zdOX8q6l/NZCW8/+
+ EXEnw6BOiQcT+eqyFAFEKBE6QfadbQ8eDCK9
+ ArRf4Ht/hctk+YVgW6bHh+WF4Zk= )
+ 86400 CERT 65534 65535 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45Ik
+ skceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVf
+ SCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY= )
+ 86400 RRSIG CERT 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ g1xXDzfVxOqpLVpaSoyD8ayuqHaJWJeC3q1Y
+ wuve/6oblmigWBJ8GpFUutNBF66xcBoGq47A
+ 5Wp3wTtFkh6uGPQCjGj165R8SRtwfNFMqEov
+ PkWQ3PjwzXuvvchAU90FpGAs6zbIfTpJ5CBY
+ f5A6NAjkWnMJcatsCgDZ03sAzRM= )
+ 86400 DNAME sub2.cnnic.cn.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ZGNJLjdUhhfQ0B4SJGACKtmFtXsJwbxeixB4
+ z1/6NW2cQmxRj04+oc3IVvIdysnCYIHgTJ6b
+ 249w4gxEg4kWXrwynH2Q2EALXDKXHtr5Sxv1
+ xypNN90HgeZIort0z0IHHf1KFGprVRpvhOoD
+ H7MKV3/IjxIG6hOfIkqRCORdAlg= )
+ 86400 APL 1:192.168.32.0/21 !1:192.168.38.0/28
+ 86400 RRSIG APL 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ vPoOy1Zm3riVKMtYRW3QhHhedDEleIUQwec8
+ 0vH2duS76faWAhPJ2OR5xyUo6rrNG8CyAirB
+ egV2nRb1m6yCvaOllaFkreftfh3gqtquy9Di
+ OTeGa2JUzx5ep0DZwLdTTEOMT8ZEy5HJI9a/
+ v/Gj55XfX1qpdTYcwNKk5fph044= )
+ 86400 SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7890 )
+ 86400 RRSIG SSHFP 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Bq0jac6ocgBfp1jVfoz0zuXUhZCInKuqRo6z
+ FME0kCDLlkdDdvmuzZ9wff+33IkUO0Zf6vgd
+ wp+Q7MOcFEQpQnAdJ2UWLFR3o1GA6VIycMLj
+ m5vVBHVafsGA5UVrUaPWQCBv0dFcywOxPhWq
+ N4ml4uyj2NxzKZDfVk71q/tl8FE= )
+ 3600 NSEC B1.example.com. A WKS PTR HINFO MINFO MX TXT NSAP PX AAAA LOC SRV NAPTR KX CERT DNAME APL SSHFP RRSIG NSEC DHCID SPF
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ MeXmHsliVtd/jfTmHHFMHMMx5IOU5upT6tfh
+ QvtLrHMgump6L7h4tPlWf7qu/RPORoK2ScHJ
+ 2WQ/vABTjpqRaPAZ3naf/C3NdvQNlxiF4QCt
+ qqozoDt/80SKBjCYSYYQUnPLVzuHlhEhVqa9
+ ZQiXMPDt81veOFBIfCzLipXres0= )
+ 86400 DHCID ( AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 48830 190 32
+ 86400 RRSIG DHCID 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Wb+zYKQqaL53cmm95v5TlojhNY10lBDrfm9B
+ uNu9IxNxwFFyoLKa1SX4f+vtwNlBJegUlGPa
+ DmSbyWEc99mJsrmF1Op5inC4zTxT+8YWc90c
+ 6S7poPNt3tm2E0RR4XJCq6X2yD20rUUE329G
+ oH1oTAn2kSaGTAn7M8tdLXnz92U= )
+ 86400 SPF "'asdfghjkl"
+ 86400 RRSIG SPF 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ o3Z3KK/4QQDSwRvOsMRyln15uzMKKi7z4lE4
+ AnMd2GIAJRUeSBNL7fEFf5QWX4XEMbZsZe4a
+ j1M+vM2Q+BkR72Qyd2Rgpo87JQ/hxjRFRetE
+ Y6sYfXJT0Ijm4TGk10PAsmS2M14CMhuOI15p
+ TbcAUkb7/KkpxQJ1NRkFbS/ecEE= )
+B1.example.com. 86400 IN CNAME A2.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ xMQqlscw/ifphV6hGrGaiEjfqEJCJ3Zh0eSd
+ VcSpVw7gH+MLraVt46XLoLdhEfAE0ZQy+AIn
+ xlZ+JkI5Hvn7esajI3K8RgJ3ojVEm6/wFmke
+ 8VazBgNTI6Zy36sy7Jf4LKrReDCOt9HpJWGw
+ sHqhrNiPg3KthOXDuBpoxKA1kMs= )
+ 3600 NSEC test.non-terminal.B1.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ vm+bqEPs23rL8rAuXudjU9nz/iadsrpRoJ59
+ ROTRA+k9Ekz6MlZ3pKT3bnQxbOq6v292qJeZ
+ 5QZDCf+yrxrJDvxtGzFTI8m0bq/jARGXNVbH
+ ukX9aYNNz3MoXfkQKroFF0xlngQ8MZ8qh+TG
+ t1xjI6GKU0MtdQ/iZ421DTCqwrU= )
+test.non-terminal.B1.example.com. 86400 IN CNAME A2.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ LFlxD9dqqHc/HMUDa+gSI0x2M3XzH0cgxe8q
+ WjGh9GvAnB9UP1fpvWM1iHtGJ1DAmQlpt1yj
+ F7HOwh+JOvqi78Gsl+kteE9wK/ByxJ/DCXhj
+ Sq772P22kYI7XTq03TdK5L5ndfxv5kSBBefj
+ BhSnulx4t1Ril1kGvq6TfFoww20= )
+ 3600 NSEC C.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ PN1EanRRN2EjhbwuS4j+icsaAlG8q2TquN1e
+ SDikRfzScbE+xv9UYObEKG64Ee0vmf7FbRhU
+ MT72kHNrBYgsoFS/eCn7xoB03UBZt48BPiKE
+ yR6BzCKSziPTpEGZNj+NNWdQVRPdbGU3MHAO
+ Jm0qec4CZOVOXkZG58TSGwdayDM= )
+test.non-terminal.C.example.com. 86400 IN CNAME A.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ YEuFEc9D8bozRPKXShr0B6O3HNAGT2KXMIQ4
+ 3QyyPQ8Qv7zV0ReUdwYukiVhSJ1Xstx7vk9k
+ uwtTtvEk+eqp5743sFnJO3gkoWtlTnfouG4j
+ Yk6JQoB7w0z+hgwsE+xKr1Z2bsnJ34otufNR
+ oejNWmnEwoSmRWg7xSI8lYrFjzE= )
+ 3600 NSEC C0.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ w07IQQxLsnQ8W+B47Fz2QoxFKWrclBl4+Lzz
+ NZ5Lp8u7XOiLiHPHXp/5938JPpUEb1ldcANE
+ vKBz5fhNFNriSYa/WfV/JTGRv9UnilPepv9Y
+ cas6TNMdz/y3QUazB1bzkwnbKLoMklubRVPN
+ QgBLndRN/hSqaZ/2ePNE95kfZck= )
+C0.example.com. 86400 IN CNAME C1.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ eSc7WW63CbLiH+2kG9YcP/f1E3zNPSFy2CDe
+ dTeZ/FsyoxsOVVEKBUcaIgndTtJNUCk26GPR
+ 9dwLYxsEvdZZ1tKbfHlkqlmGAu4nzdetGLtK
+ uOpMdFboNEgh8AXB2phRn0EJNsuhHQFBmX9W
+ pjN01eJksOOSv/znnrBReFObbaY= )
+ 3600 NSEC test.non-terminal.C0.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ pZtzNxFYvEv34K+8Gi6fwr5SOAEDsshZ13tM
+ tW9Sr0oM3Ea9AAjfKd66eYbLF6nq/FOYQRfp
+ t56NX2zYtT019mX1KbEouoAJCrkcmnns4aTc
+ unQE9yGMk8moKXwRH+rQSflemTHO9aeD1SVv
+ It8kxXfalR3otJIrr1HdYvXPVv4= )
+test.non-terminal.C0.example.com. 86400 IN CNAME C1.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ szsFYsg8E77ew6WNUuLIqnDrGz1l6OuxP5TM
+ woVGsDwGc7E6gbzbcXMTNPKSSDLa7dchjKv7
+ Kd+ORNcCVBu8qtos7/TfpSsH0hbea0k2E7EO
+ U3QeZ8EsNCd3MSXBLpn1016/+EUGML5U3dRs
+ kmcWyT/iM86rpz6Gby4KPIcULlU= )
+ 3600 NSEC C1.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ UJxr9cwrOEfFo3dd32fV5YJ3FM4TqyqEBeBk
+ WKU+Z7Uc2x6+U5EO2GDY0Qj8jVEwVugZ53V2
+ hwh+85CuwfbFX5LkCLbmSOH3jte5SdHAdayL
+ s1iDNJ7hCji86n8bIZzmBsxlRgC9swfwO0vk
+ YjHGuigrb/Cv4XnYnr4PuTnBgP0= )
+C1.example.com. 86400 IN CNAME C2.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ KZy5KO3IZZ/9PbyHj8DEas8yJW9bJTcyFbb/
+ fC3kk6iBhkmcKfdK3fF1M6ARf7DC0uoQfEcz
+ RSoaVn2Eb5EMEEriPSFvUk5GxDObLM0fhLS3
+ E/H13bdXR4v4swxkV+0PBQQiu1GEK0WRoXVm
+ C6dF3pM4JYEtRgqc/RxY82s+Mks= )
+ 3600 NSEC test.non-terminal.C1.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ KJNedKMxaPWxtwSKthu6+vanXp4Sc5vMvgxs
+ UObrVIhoxTUvcy+pOD7x0/bQtQYUILfwO4X9
+ HXElIDqA/XENZKjb+PQoD/Yz0G/63mBAHYEi
+ VSdUGCmbdNJ8LTkr/mJSLBmdP3xo23Fv+uIZ
+ lkErdo/LHUT1n2Y5tmfyvmUvwAw= )
+test.non-terminal.C1.example.com. 86400 IN CNAME C2.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ h3NccjYD8iWBjbbKf58ksZtm9a4FJxWqcLsl
+ RUytSY27B5mC+R6Pyf4s/pzYPIGioubmnYAO
+ kU0of8wQ5r0HA7Fuwm1OU3xVJVVOtRBb5OhS
+ bShf6S4uanb6oYgModWWdkuo3fFP5uQIT06n
+ 6/kC13Q6XCprmhjzfuQB4j0T1Rc= )
+ 3600 NSEC C10.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ rjjq6NZ1RSwBrNgpNAFbsgi6RCLQ4Os8DhQ0
+ NblY+u/XuzVC0QvRERNY+mUFsKP6BpKcjFSn
+ mZnwUB33qEh4F4kVwaFPToSYSOQLlODWClEy
+ gM6+2xZ5eJzLj3tnZ3PipI/hY1DdOAhuvCZi
+ 8fJIDi52+nH+CoddYKn/V9LUgrE= )
+C10.example.com. 86400 IN CNAME C11.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ QfKnR859+8/BBOKUnTxi7LKj6Pf7hzYFUNV9
+ xuNJaIHrWGAWixW8zcgd2EfEk9ec47UHP3TS
+ +opYiujwFRLbOcU56Qn8dffBU43fWSaQ74ra
+ tKqgq7U8qpMc3567CqdZKINqMQQaJSpw6Gu0
+ kHbc2X01ZEJFecUQuGzv73sXrzw= )
+ 3600 NSEC test.non-terminal.C10.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ g5GR8P3cLs7xX4R5imA1qoklOXvj7bcyj5FX
+ xz6qcnKudO+iivT5+Tp8wbxv9RXzgurndozI
+ q/g/PiSt+NWV4R4A+kqhCuZuBQ1QZfRPGqAz
+ PSCpQ4pko8jJC0qAMGZ9SdM1R0s1aMEaIcg5
+ YMQd7U9zDVgWXOW7q1L54eLuwX8= )
+test.non-terminal.C10.example.com. 86400 IN CNAME C11.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ afPyrqIU9RdgUUrraxxzvqY3b1PWPI1AhZpv
+ W/u8BU9UzBGlEyGSIO42D8qmwgJ3KPiNy/2M
+ 55orqBYqKtjWHjuxZHpwRTvz+r4q/v0AEyV/
+ pPNHZg0Nw5J47CulFYiYhw0j+xKf0oLzXXCD
+ ndgvR8m79JZy1crI2LJqRTwQKiY= )
+ 3600 NSEC C11.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ r0Af/Nk6Kvnx1egV4d1uIKR2Wz3AjGbpuXSF
+ DcYZvIK0OqpT3eLbpHOWi1+hWsqf1lBsXQaC
+ si+rBLnEMlJfTQbvLHJ2z28/1CTFu4cb+wMm
+ 37P2Ss6sJhv9iTz392llnoQ97kkpmUxMKdeC
+ fOsehff6le3ZMpNhVBROdLoeXaQ= )
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com. 86400 IN A 192.168.1.10
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ CNvJP68kKf1Th5pvbU08zEWfDsY3mKbom/fy
+ gOJhbQ7SvZhWom9e5eNj4B+tsnoUGM5DheoC
+ RWdTMtaj7dyGGdM8HvyReVgb2841VcpaDPmV
+ RnniANnePU7rZeN89BUqat5lrOu2At5pVnz1
+ BBqMxzobM9isuJzFYpZUsn3QO6Y= )
+ 86400 WKS 218.241.108.5 6 23 25 53
+ 86400 RRSIG WKS 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ kxFeJoJ6fkNBZ/wzqK1CEK0eOQK+R9SBi6eW
+ cFii6EJRcAlwoD+zneCAoXpcBoqOFGxbVCQB
+ F+P7qXStE40WvDn6KyrLpFoqJYRBzSC430ov
+ KT/2OVhkJrzypYAm91zoxo4EC2iPT4BYyyv7
+ axx7ABNnDcnuPA3ox6heXG3Fenk= )
+ 86400 PTR 2.1.226.159.cnnic.cn.
+ 86400 RRSIG PTR 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Xh4hhbpAtHKUg4JiUx5Lnp2i8GBbjrDnLY1z
+ Ga7Y0rJfzqK3BETsyjjJyvkjQSXiGju0+I38
+ mwc34rZTVxmx6wFd9V9oK74T9nPodQdmqOYu
+ dEMdEk1rfMCA7pvj9pbiMpGOZRHdZ4KT2mMc
+ MrQtVRJh3kFhuLhRDAT9Xa3/NE8= )
+ 86400 HINFO "SHARP" "PC-1500 1983"
+ 86400 RRSIG HINFO 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ fuakGfnFU5L6F+novql1ep/d03IiLQGNwvew
+ GvVuNpVKjTWmUi5DIpBJT9MkPjk2mdo1FKWO
+ gRBvkLU82/6dyA9wjzq3vbinVvD3f5+iUPqu
+ uvDXgzlQaT8k5bVkNHtwCfJXbjw4Vc4/8omi
+ gkTnl8Jdf6VmCShXrZ2Y6TK5uzY= )
+ 86400 MINFO root63.cnnic.cn. error63.cnnic.cn.
+ 86400 RRSIG MINFO 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ WJylyrzRUPDYqF36lOX6BM57HWvzzE/2dXJY
+ xSua252Pk1UZrN9yiDJRttQTmqkU3DxtE1KB
+ vOkCsdOc8R3dr8BsgN8m2PQAvCQY8cuqDG31
+ le4yKF9DE0BcdXbu3Ic5bp3huSY4iuaiutvA
+ fuovrtzelaUUvhkZYA9+0EHy4JQ= )
+ 86400 MX 1 mail1.example.com.
+ 86400 RRSIG MX 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ dRQ0vpIrS+qhzSOnmbiiJqdyOLCTjlp2LdV+
+ dSTcplTwtRRYcW9/MUoDzh2kfEk5m3loSrH5
+ 7HrsMlt8d2chhCJo/tF4radCDBTZuK7i+OEv
+ P17QFhd6DMw5S9dEM/o5j1ijUZv7/QgsbjbF
+ 6Unv34bQSidfUqi0JoC5O+VuGLk= )
+ 86400 TXT "kkkkkkkkkkkkkkkkkkkkkkkk"
+ 86400 RRSIG TXT 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ziSZV9Y3JQ7zh3h50cymTtW0tzJE3i9CLO0c
+ ofJJaHBP1Mx82MMRDMS4tNejRpY/vYUFwrrb
+ y7H6vUTWJFHH4DhqNQrqndZbqxLatuUtkmEH
+ raIs2zUYMIgS2aT96E23+kTmPIXDGvIQ0SVG
+ d8vDsm0wnpz9OL0R/1pv17hRIg8= )
+ 86400 NSAP 0x47000580ffff0000003210999911112222333344
+ 86400 RRSIG NSAP 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Eyj3aQnd4lBNfUtxZpPlEFFTZUYGiBrnX25r
+ PHgK55h5M226SWWtyv/JYtJjcMntHqq7fbON
+ r/8NKCO6dAZra5LYtdRLVO6t7lJBxsWHI6ce
+ cDTVE3PWxk1niK9LBCwdckOwONp635Iukmbt
+ sdfEadu9Aw9BMmOT83QUJyjW3f4= )
+ 86400 PX 10 ab.net2.it. O-ab.PRMD-net2.ADMDb.C-it.
+ 86400 RRSIG PX 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ TEIjyOUWJ6OaOHnRSus3ytt+p5R/t0PnPJwd
+ TyfYrLfXaA2z9G4L7OWcT3z7wf8g55WVR28p
+ PmXiTBNL6LFQH+1ASX+YaDZNx6Dc2b7bOKql
+ 5dIccxMLWi/jiWEaPp/y58Pz9t/qOF9ZNGVg
+ +w+Rul+6HaX6Dc53uokFKc2xtKA= )
+ 86400 AAAA 2001:dc7::2
+ 86400 RRSIG AAAA 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ sqigB9U+/ncUGao2Eai2Ykkyx8cZfbPcWy5l
+ 9771JB4coWudFuK6gj9dXowjgHtN+1VulzQ0
+ vSUM0GIZjGcJe8hXrQsOOClQls75z5bVYVTR
+ 0Mu1t5nXhPtCD1mgzyzStHtC4jiXmyAw3K2U
+ G1UJFlHYzZyBYQMLx0qLC+gRS+M= )
+ 86400 LOC 32 7 19.000 S 116 2 25.000 E 10.00m 1m 10000m 10m
+ 86400 RRSIG LOC 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ wM51Zvyo9rTgJ3GokcU+AKxZGiDQDuMGuo/V
+ P/sa9nlHSoNIL885Qzbdn91Ur0NFReQAo+9m
+ s60Jk8S0eCafQrlKit9/fPBMZuO/7HDnkavG
+ QiIe1GvIZPQwqaj0z31MpMG7TqF3wi/c0NlY
+ qz8VfjrkNoEEZGIzJFJ/EWghLK8= )
+ 86400 SRV 0 2 80 www.movie.edu.
+ 86400 RRSIG SRV 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ XAb5QB77kd8LUWKP6k8DshtBvKSR1n6HtS4t
+ qSic2+Cek9sldt3h9KAy5gL3LwzWUVRHprJC
+ KthF2ZTlA79uoRLbiBpA5lnmanDArO9+Csz9
+ FMj2zRG7bQIpichZIJbGBgzsidnE6cuCsrXQ
+ j8RqPdPYuBL8hBIdKpW+24YhXEw= )
+ 86400 NAPTR 18 35 "Au" "siip+E2001U" "!^.*$!simmmp:information at cnnic.cn!" .
+ 86400 RRSIG NAPTR 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ iv9JeJd/Z/4ASBeZj7yWIPxL4SnvEGJQuONm
+ hqvmxr7QtvltBhlGZnYZj8rBixrc8A9lubjK
+ W3Li4/QCF5CJ/ivoeTLQzn1Ca/hMpPLMJ4/N
+ WfQoeOzn4n+4fVIrsVfbdVM7rJV6+d2nHQaE
+ 79uwSTwm1pja2jkMtVo2/9AgYw0= )
+ 86400 KX 20 kaku.google.com.
+ 86400 RRSIG KX 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ qPQPZDruP4UD7ORY7dw3hdWHVD8MllW/DEj4
+ WYi3F4eYolclA7u/9cebt8UJii8cigY+24TI
+ sbfRIIqvkhOg+UjBtiUQsj7x0XxQXEbim571
+ aj2HLvBHO6I991c6ndatShQ3d0jYNyE1qhCD
+ v9i1SkmjfuSRVWiIdpQLUO9qz40= )
+ 86400 CERT 65534 65535 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45Ik
+ skceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVf
+ SCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY= )
+ 86400 RRSIG CERT 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ YM+IPkTYsPTqNajmdirxCd7NRCB82AtDIYgc
+ qn6vCRGM/2v6vb+tciYnRl85uoAGysS+bmiF
+ lqB4OGCydIVhS4KTMXwDPPf1d91nqy8BWz7Y
+ ei+B2qhTnSGQu96sSBdJ2ceU6E4/6/u6LY5a
+ lAVt8Vk0B2WjTAjUi0VIDqhL3HU= )
+ 86400 DNAME sub2.cnnic.cn.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ SBDS/nWt3HswZJxW2jRGzCsYgxCBofEeuhbq
+ QmNSGR2bHojp59AYZR00B3aAOdA0RGhdZKuU
+ yLCqNdFyDmasGtdc7CP1Bd6Ey0hVCM9wbLV8
+ QaOWo/vgsWcNFMAAxR7YqUfiRaTJLV+IouDH
+ uHodkILrgsOqpXvdGaZLe3wTQPI= )
+ 86400 APL 1:192.168.32.0/21 !1:192.168.38.0/28
+ 86400 RRSIG APL 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ VEqPOVp4Nc02haMJeh4YTHDoVGmuTt6cz1fo
+ Lp89B62/L1KGFHXE6FrB01fLcwT43FbOHVLA
+ zi4szJ9YBC+rqSbVBwm1lo/TskBTA0VLrbEP
+ 9iEYpPtqn03I1upG44cdfcXyNKx3fZGEOeaw
+ TJL9dff4O08LCvJlKj8DkpZ4Jgs= )
+ 86400 SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7890 )
+ 86400 RRSIG SSHFP 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ oyelf6+jY+6hoW4151v7TP+9er86PVXpd48R
+ m0o0pxU4ICOR4fvc55BVrseXAzI9gGCgX5Xd
+ bRIVuLP9+8qxQ+lGJHNn6y5916awS+RPVWeh
+ dAJvOF+xnNjSiO2JXfGuM5jXrawr4/mrwzK+
+ YKGzMSsqeqDA+V1NOYvfzM9CamQ= )
+ 86400 IPSECKEY ( 10 1 2 192.0.2.3
+ AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6
+ BSGvgqt4AQ== )
+ 86400 RRSIG IPSECKEY 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ZDlc09VMM2ctWqF6pLelK7xOKjQl3KRb1pk0
+ W7tmrHh7TZb+0KkwkNl3LxRA75/xLPqlU/ts
+ iV8zEh7gM7XEv6t98+eqRo9XwIJKeKRo9Dbi
+ F+2EP5q2B/Ja/T9g1aYx84CIe4pNV5lwxFTc
+ RYCAnMs9p+w6di+bVFUkxT9MISs= )
+ 3600 NSEC test.non-terminal.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com. A WKS PTR HINFO MINFO MX TXT NSAP PX AAAA LOC SRV NAPTR KX CERT DNAME APL SSHFP IPSECKEY RRSIG NSEC DHCID SPF
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ zrLyk32zZZwqj4EUGGH03gVAyksBeBYAQMpp
+ yH3MGvQniG4o+VvCw6WR4TgmErskBhhOZ/Ky
+ GtFqN2Uorcl8bJnMYVgQHRC14cPXEYzIRdz7
+ R3/GkEiyoYKrgsbPzLU9HTZoa6l/0Gr6thzs
+ ruZZwhavLR9Y66O5Q/PU/4S0oeU= )
+ 86400 DHCID ( AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 48830 190 32
+ 86400 RRSIG DHCID 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ u7CgSI0G8k62LtPdEPwhu7y+TMIpxVM3IO6T
+ ZhbPR2/6rMez4b5GdNbLkXs5K4mxz3Q5JiUM
+ SNSq+l83GXl3n2qKQzgfKCAyK+4E5/lI799h
+ VPnMjdSjQpUr3SBPqYKVb2mT3zXrYxksp6IT
+ b9m6FzNlGWCC90yY1d+nGT3x8bU= )
+ 86400 SPF "'asdfghjkl"
+ 86400 RRSIG SPF 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ CqVJZgqFQKjUOGfIzuMvoSlSzKhwP7l4kQX4
+ ayUrpMnqaHyQfHJxni/tfa9OwvxExfnY/B/c
+ vh98DFNmAc8FOPAMH6PLiZfxVzDX5Sgi0RrZ
+ +GYDccb7rAP7FsUAcsFreLyKFUTCycVQ7Rj4
+ OK48YTA5EG2cKpFc5dxq6+//RHg= )
+C.example.com. 86400 IN CNAME A.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ JeFX32cJ7aBtb4rUwhcOjmtn5/bG0yEJhyt0
+ n2xJPs2SMZyBvsVJP/9sAhpIu8zSXo1l5Cyo
+ aI6zwonJmnmyHU76jgXjSq0aom6cu6paxzEW
+ fhTbGGfCQ7NsPKgZr+TGNC+cfM8my4+yythX
+ +YDqKajBegulcAsHzEgYWmUoVx4= )
+ 3600 NSEC test.non-terminal.C.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ JqmFzLtyfWfcHMKC5k+5Jrf4DqXCg8PBpq7I
+ wv7ifztFYfg5VhXnp28hrE8QqSfLYv17CzNc
+ TH3hDhqAldwsjPG0vyhQB67zGkOYVk0FbfWW
+ GiY7sP59aKv3ZI27ZhJmf6uA8WotkD2QjCYc
+ XV8x7qTErkf/vi1UiGgMsS3WtuI= )
+C12.example.com. 86400 IN CNAME C13.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ydvCKZnNj+3eaicVopOYSqklt++EOi9eKEcY
+ rD5KZM3trEPRM9RAQuVx1U6/JOnvX7EkgMEx
+ x6FvA4mIS86H87N6R//CSc3ngOy7QU7sSZ2Z
+ f/x7WbOaqCfjYRsxeKkzuIGati5yraxhowkm
+ OHR9XwB0d4ZlX68mOFXuI+ENGXs= )
+ 3600 NSEC test.non-terminal.C12.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ iq36ylDsI3HYqZc2Fu762yVwg7eUm0SMceM+
+ x8xOWdDvR9Yt/hbZDGKLRHGby0TEZgWkERbV
+ CWbcCZWyOdyhB/b/ZSkjL9vM8S9fIODqOPXa
+ azbwjgV2T/kJeJSoxA+ocJLdRZynsBpHpk0Z
+ 6NKTtvCIwPDP5W39S511mXl22kA= )
+test.non-terminal.C12.example.com. 86400 IN CNAME C13.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ wOnjiR6iShlEBNMYQBZMFRPlOvLnd5kn4TqU
+ 01AwGBdIRUm4dSpmIT8LQc1/xiE7QzyfXdkr
+ UsFtSRGaPG0NsUTRz8tIecfsLZNssJUDyg0h
+ puEiTnampZMmHW2NYnPHq5gkKLWeDuan7hZi
+ JRgMAWApVJyYDmE8rY5pbcrFnoE= )
+ 3600 NSEC C13.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ g8p7p9I+6D1fN6mj20dNRAyb1G8Thg7rCZXZ
+ xmgkdEu48imlNVR9sgX3aO6GL9v7BUNc7Vt4
+ 3tyx8y2nSdkM1aGTYpt2DfaiTtYDMlNbPONb
+ 9/i+3LpzDWzczCAdCxWNHtoCDcPi1w+Q76AL
+ DPtARgFO08OeAVtfkSdJu8+a0Gc= )
+C13.example.com. 86400 IN CNAME C14.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ SDz4/RI39SKfiE3EJ9RRn6FfLw8ZPB9DzE4V
+ a0VOrTiYS3KxvJqkBEUwFH2oJfnkc8b5HMpj
+ ECC0ifDLrwsRctXfpw39EEiwQ7n4bccBj9L5
+ 9fhNvBYn+qIWRILSFj3cA3qL6gvPRNTGZHQA
+ ENgwtF5/b6bHIpDb0UFzvIK4L7w= )
+ 3600 NSEC test.non-terminal.C13.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ QoBrwTsEMKvZrWVWOjLO2gJ+X2G6pxwZZMGT
+ /m72nrpeIiYNL10V3/3Chtw0bDVfzUapOdfk
+ weM/i8qpYWxpznWbd+c03tXTqqi5gCIVfRcN
+ 3k5nQxX2nS2mC3oBcmI8BzMi7wX/boNTczGd
+ EmqbnrGnn4qn2vz2tduiwW804io= )
+test.non-terminal.C13.example.com. 86400 IN CNAME C14.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ z8p6Mh7YjvAjBOZhi04dAP/phrBoWSavdj8/
+ RHBgzyOyeG6hRvmmaZPx0GPcHEDI0xMv+H3L
+ tdoMlrMKF3U74u93xsf3B3SAmE65oSISunYC
+ 9R/UNE772S+ga6FU8ufjY0/EFFjctj13RiKk
+ 9Kndb0e5RdWXKApB0N/haIe3fPM= )
+ 3600 NSEC C14.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Na8tko+ZwvXp0z4figIvFX5tqflE99k1RYtb
+ RkhDzZze1S1+nLS+c+NgKrty2PowiWNgTGbP
+ EB6SgLn0NkF/I5a7pl8lrXcgg34leuhinFT7
+ pBD+3ampYx8LGt/2uBfUg6clrnbOkIxsf8aS
+ GVQyRV2z9cRN9DfKBmtkSBoP8zU= )
+C14.example.com. 86400 IN CNAME C15.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ hlmobxbczJOv/RjwIvLccuRRdIxZ4IJghtlR
+ 5dTJ4YLeu9IwCo4eZlLoR3uXRv7hqbIS8mhr
+ n8eqcWnTaFRWJ4G1dRwx8d5YaN5ATgxnH4g7
+ CDTOZAAY8/cS7Ybr+abPVLay20y2Iheq6eOK
+ +dWRCJpgVnEM4zUHrS3XOOVkx2U= )
+ 3600 NSEC test.non-terminal.C14.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ m0JRA/8wf4QtKtI7BGBw31uebJCj+5RQLKx9
+ sBvdjCL1yK9n6kp0OuapzxJUcC3kL/jb47MC
+ CyJtKcc65Ocoj4jGds8Kp7yNrDjIAb7WJmds
+ HnpQcgNyGgQHqkiEuAPwhulHjdIV+NKcPXec
+ 4FnRRErGq8DYg5qPXIguPmeyvwI= )
+test.non-terminal.C14.example.com. 86400 IN CNAME C15.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ QQ5yqUlmn9mgtmacEw9xEQMAh8Ecr92SWROk
+ cVrWavhQfSsSDSmM4ftlHjLHFZiJ7YZxa3F/
+ 13IHGcHesFaKNicknyzJNvqHJvkVvDkLDnt/
+ uos7G/+WDskV8QOZ/OkVWZwYuNlw40z0ex5Y
+ 3miSs89/A192AgVGK11YinQPL2o= )
+ 3600 NSEC C15.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Ly5nI7uorIvY3o8mcDgVRQQnxiFLAZjYRlIB
+ aVD/ldd2iSy8AG2hfocWXHhLbXzO3CTjXjax
+ 36TwLR41UpuqXURTG0Qzfyq0WPej4irAwdac
+ O0JV6gc9JJHFKOQ2kWFN05cBKJyjk/h/3y3B
+ gfv4icRVezRM21p5oy9zKSSP7fM= )
+C15.example.com. 86400 IN CNAME C16.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ xvrk7027MrYKIUHknMmn5TX9F2LMRE1n7Ujb
+ VM1e0OwWWrlnInqvRqs64P1mTdast3q7KzOU
+ n6WSNvIyJtVkJLFnFL2dtm+lsUpkh9tJKyzE
+ DyNPn5PVPya85Yo+P+LQd08k/RhCPgSpe2IO
+ 4GIPCZWVnnNBZQeMgLbIEMm5yHs= )
+ 3600 NSEC test.non-terminal.C15.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Vx6oWYWNXU8lvZbcdFpR2gZlfdabwOz131U3
+ fuQ25ftTekNfDn896eXhwRi2II8LyraUvWDm
+ Bo6PqzqlDdeHyExHYywZ7wqoGZP8afrqLMNQ
+ pLnv9U+ZyXnYoot4A7uhYm6d3ho1yTyneXn+
+ CWWZOcw/ZRON+xWMwFjNXZZPabY= )
+C11.example.com. 86400 IN CNAME C12.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ VY2iUA8etZcNv4qMVtrvN6U+PO6irU2pET/o
+ rufaMqlq7TMryp9mdd/1Ef235TqjSGDT7NqL
+ VAMvtgVvsHTuFCeWIMuDkVEe7KXjdRrYK9a8
+ WohgExsI26dPMy0R/JGtPmeTTSIiS00SjVk9
+ wdCQjSbZ0xl/T07rZZ9fu8BaVvY= )
+ 3600 NSEC test.non-terminal.C11.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ QUm2o5cNBZNmZe0sJ8kS8sgUJga4HXerSgxv
+ WKx0ofCTl7N1J8tNYH9U5KmeZ7fu+lk537aU
+ bmB8CftQX2GxjA8pIxpaEn+Ublf6FIcMtsWM
+ F0MPf9ZXPgbL+Q15RLVX3KbMuXTLuFogUYPL
+ BetQ8cYF77EK7iNKwcMRoKCvIDc= )
+test.non-terminal.C11.example.com. 86400 IN CNAME C12.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ OHWsmRgm1zSThs8F7BZWYghWHBR0kQsj5FIr
+ CP03dzgXwz2UNFYRLcZM9YYc4f8xCQh5IO/9
+ mu8oj+py6zIqbsUsSymsUomT3+JR8UoZ3aeq
+ 2t/YJ7I6LDowZF+KOp02QgOJAl+sEe0ah9vw
+ MbOnwBYlHv40GE80gjsCmzOezuA= )
+ 3600 NSEC C12.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ HefpT1HMMFFsQFpMlqTZ6v9ZtFw8IdSoHVBJ
+ nRnJujLODn4N20eWEbIN8FLlSY4apd/mnE6/
+ dKqSgT7zFTVzGf2KE6iXlnC4xOWZ37LqOS1y
+ JsfQCe3Ryw/l/bbVynhMq64pEL6yCeX6HXt0
+ MFDq3x/9yU7A+DWAS3cEDLBN/Qs= )
+test.non-terminal.C16.example.com. 86400 IN CNAME C17.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Lotbl0F3UliW5IPKJL9SOuYXkqouvoaFmblQ
+ KD091v192vQqUDadt1V2KS8TehyIvoc2XF6n
+ lluDceffB5DRYdGhmC0hEtEilkPaik86lKoU
+ Bx3dSn6K70L+hArtRM7nPTSBY3kEmqcC8jN1
+ JiLR6KRIw5HbFcwHvcQTjdEtlto= )
+ 3600 NSEC C17.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ lkK8Fo2AlF4KnoOPBWV74/8KErQ/6A5qecft
+ Fop4nBvpejfm/M5Y/ABExo7wTyd4E3ZKoMVw
+ ueGQJ/YpuuhPxZOnl8EQFIobNJc7VGZMBwuk
+ UJpVBesp284nVI3bBenuutFZiZk1pQTou1dX
+ m0ei7lYhiavsG4eBSZgZu5nwcUU= )
+C17.example.com. 86400 IN CNAME C18.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ HM2B4S1E3fDwYps9fmPTvONEumtH5mYW+mFK
+ s7W1Kk+E5igkwk4T+eF5BxmGrq2Bq2XIC81S
+ 8USbaYiKzgGuZiiVRg36EotVq98AGFFdgxKU
+ GBy6qjpZBuXNIMZKudsKLOiAK0Sf83xbPw6p
+ BTqhxAlgJsOhu++dDjo1IKhlMQk= )
+ 3600 NSEC test.non-terminal.C17.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ t3N8fQf6TkQW6CIYqao2sYsDGst7YTz6EwIy
+ RGuUz4opWh0S98zCflxkwZ/aIyiaZ8My4502
+ +8LWZ/mXF3E/M5ZjZwnCOk4OdvYcnf/hGBRO
+ +OpAlaWTpj5Bnqr4yqKgo8sayo3sGd8GHO2M
+ KxBXHkRBs29zEoxcWpcRLfr4Bu4= )
+test.non-terminal.C17.example.com. 86400 IN CNAME C18.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ zwLtl1GpP83GN0jN9Gbg/GU3MESgLafA8ttI
+ z+/SgZewG0d3vJc31g/QAGZBhtfAwwgwJlbS
+ 46i05026B7gBpQtdK4RG3l5gM/s4e+mg4xF9
+ i4n5reqqxfmATmTT8u3OlL4aCxfsmUlgSQor
+ WaBoy5mTRh2PuV/2hSkpCGVaY5E= )
+ 3600 NSEC C18.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ tLTNHgrQVp+b/hZLlsUT3nUopRPNqkWfrKa/
+ xO5+Bdpw1VTo05kmGagKmwfMrH0wtOiyjhZE
+ yEkSpAfolVojPw4oDRp4eK+toWwecnkXb4H2
+ E0EWnJJ0BJy1DZeB/qQRQowzE33kQ4KYAjJn
+ 2mmNHFH09qcgq/AFOIsBAYKDVcw= )
+C18.example.com. 86400 IN CNAME C19.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ KoMMbFb+ylh3p1s6JMPBkc5csxvEKd/Z+pLh
+ 3sfb9ImkqqBMnwBQ3sCb4HTenqC8nUJGp2UZ
+ pK/KblgXDTJpt+pC6Mu19TiBmTUZTfJtcetx
+ Z6Me8cjXw5e7/jGToKWE+E+b8sK6amH89Y8g
+ MmAG4bjG797YgIHAE1GgIhYnPR0= )
+ 3600 NSEC test.non-terminal.C18.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Jt2JVG/TPDhXrRtt8x7aYeCBZyr1GprcmlkW
+ SHwdatCkYnckrs4k9xJqKag4EneiDO8sVy29
+ J0WrZDweOasI6LQq5Uh433TrfunUqFRoj7Yp
+ 0Q200jlZpuvHvY0nGXBQeXSWNURNrGZ2Lmt5
+ vTXt/Iu19dWk3DmUijKR1/LqRA0= )
+test.non-terminal.C18.example.com. 86400 IN CNAME C19.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ XLoGu1RuEmIXDuxkqGeIgd5CVX8Z6xc1Bu+i
+ zrgboAj2BHbUmnxPht874ydyMgVD8gbzkGa6
+ Zsh+jG9SILdk37U2HptBQYW1Umj+HvIM6E7q
+ TFVUMe1/OLj619V/HqCXUTjW61MhiHTolPcF
+ 6XLEv+n4xwMY/Or9X5UELmV+1EM= )
+ 3600 NSEC C19.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ oIzaSmiBiPOKv2gsFyD0m2tGyNNzqmoHPUVs
+ idIenLp1uMuGOnz/uhsdsm6t/EFvkU4R43sa
+ tYeCKhJwRKPlA4mMjmPm3NzBgLPrd95Wlj27
+ PupY++Lh4EV9Br61jmWKxQeVnP9hxHaOmOp8
+ Y7OQvVAjvx9iQqCUIkck1BKLB20= )
+C19.example.com. 86400 IN CNAME C20.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ k1mkYGbVDvKjvrQdcnYxqbpJfAQWJISo34Gz
+ IlBrRYiba5f5cWPoN8oMJ843DwoXlSK/OPzE
+ JnnIkfuf5KbOP/3W655dhBLutsT5G38YZcTN
+ E2thTk3R7uYAJWrOfGCSC+csRlpHklVwurSg
+ YUBuN/wE1oi/Cc3y1c2+bZq4AZ8= )
+ 3600 NSEC test.non-terminal.C19.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ FTop6JDiTZ3GtheHGkIV6/tXXjZcCr8CPDYN
+ UZ1pfbc/RFX11X6TZ83H609AMn1oWOcTUKHG
+ SfYLh/amBnesJp9KnGbUrhsHRSK/kV4PTDB6
+ YWdYVDtG1oKzhhcVXL4XmWUd+iIvvVqnk8T9
+ soD8e9GOvmA/GfUkWKkMnEICNTM= )
+test.non-terminal.C19.example.com. 86400 IN CNAME C20.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ enAAqg9puOt2xaStJb64WyKdl6G8HBKR5Gz2
+ W8TdA0RoQzwA4BC3XSfdRgeUkSzOibJdvNKd
+ l4xreSSAzg6G1tBsoAHtNscK9ef9QjrjVQZ1
+ vyN5jJxcxXX/UBOmYfJ5B9ZbqKHnb4DK/2t7
+ agFAzUuTsVKTsYi0MXSdPXknYMU= )
+ 3600 NSEC C2.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ HMkTP0BU2mdNcvqgllQ/ic+nmiI6538k5Bsy
+ vazQu5d8y4yPPMVY9AqdJxgFeRu8YRJOSir+
+ nWTuxWN2I2jYM914Y/3j02cyHyZEOwXVYJ0O
+ Lm6AWBLBhfIOk3d9ZBtnutcWAmKcXRUNkCoN
+ FTaEsRuSyVwAIPrarivykS1jVj8= )
+test.non-terminal.C15.example.com. 86400 IN CNAME C16.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ cjZHkWghypr9G7e8uOddKw6ytvFR8CeMu94n
+ NPKy7LrZV8j6QFqynwM05QIKslV6/QsrzEXV
+ eM/GEi3IOwlAvbQEa9+2kbfs7AylzuFmyFK1
+ ynh7nl+RW9HjDwCuRm1FnTuZ/y8jv3j7upAv
+ 2tM49y+plw7R4JWKB3fZfu2YuaY= )
+ 3600 NSEC C16.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ vX+0BrR270LUJA6xoUCEcE5rb7fUDb0ltRBw
+ AerhoGqRfoS2yqwjjwmHjs4udrLsj+RIFmzC
+ Vbr3UxrAm7bGpVH/HqZl8BhJwKHDb/FaubMe
+ FcGgaYIgk/tn+pbv1DSeTKxQNQm/vM2lHwgS
+ k4MilTSLAe72SJ4mPfvZXFsMf3g= )
+C16.example.com. 86400 IN CNAME C17.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ qsA6SUZ8gTgWy2VoliH9MzqCedp0A7mNUs3U
+ Q9+Uk43DKN78+0FGm3Q4HlBRBKmXZqMeJyLq
+ GiqzMb+kQxhNbPgsvDbtIeRPfftcmhHRJLKW
+ QBsBK2+Be6/1oP0Sm2HjEwVEh9x3TatyIPOL
+ 5Dre2U4ZJ7ZQWeuYUO/JH+SCydY= )
+ 3600 NSEC test.non-terminal.C16.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ ayyG42+mcw7rwv4PWFUQSZ4L9pG4RXcrGzBH
+ 44VkGJ9WRhfdCpClU5i6WkgkfVe4ibm2eXo+
+ KTNDnbM2KQBTTgEiTmxr9DCBeZs+GeYLHMbD
+ GgDYzfGArXkNBRdpHt5QznpU0SXfnca9o4NX
+ dkYS9hGPnuy+2nTi/BW/ZgxXvY4= )
+C20.example.com. 86400 IN CNAME C21.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ q4g+4O6Lyv8wKtzUoGz5ENQk1b1z2kKJmQ9l
+ 11LUkcfVZjvl5X82YBCJ1YP2tGkWuw5xRcCw
+ 0NtfuFWJLlz3i8jpkXs9DMK/a4/X0VACHsES
+ vDckUJwzjcFyowiBA8kFINElTNNxpSuLpX9p
+ eLypUHHAbwDsmMkd0jz/vXIvdDk= )
+ 3600 NSEC test.non-terminal.C20.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ IcKJz0EM6mMQGc5qQ5dk3jLP+DzCJlz5Qa24
+ k1wwvuN+RJ4I39rMDL/i4syQoMAN78EZE4Sv
+ 66a70MdaPv+AZOTguWbFSSXNHBXW5BUJanMM
+ +Qpgf53jZ66sF2n2v9t4GEIfm6frBjnRveDT
+ iOU4JIdX6eJtM86jGtqheTrgRew= )
+test.non-terminal.C20.example.com. 86400 IN CNAME C21.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ D3fsqLSsTxpQ4cC5IJVVKPyjwhoLEF7xj7bh
+ Bh2h4j6Xl/KP8sHfm7Yd+A0Gxz7noBCKfP2t
+ 2YRmFw5k0niIndF2SR2w11vSWCPxEj+H6rmC
+ JMjf0iRf9rA9HBDXzk3vIFZHVZPHs3B7yeXs
+ GVWKsHJujtuCSFATNOg4STckDsc= )
+ 3600 NSEC C21.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ YiY6Jble2NqfWdokFvKFW4tQ0CJCq7XyvicN
+ DO65NuR4xfcC5k1h9J/CPxPbCa8+rDuyFp72
+ L0d7ZYIYWoM0ZsanuLHqlAwg5bDDt48tc0oz
+ ZYKvP4iAw17tJ3O21H8u541HOVwWRF5Tlwu6
+ OlPVENGjxDafkZgHo1yfeAxfP7w= )
+C21.example.com. 86400 IN CNAME C22.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ dJ7gGAPeLC1rGaxG0R7lg3u0DoGwvRHQO4kX
+ DcqwWmG6q8BImKSYKrD9Wwdb1iPUJKTR68Mt
+ czYJE23fLWKWCifefkSRFx1Ff9M17Z4M5zH0
+ LNYsQ/6AJP+76oJXG7bx16vZuvPXWWxrUQZO
+ pcLdExCW0FxLaVbGQMgCNEdunW4= )
+ 3600 NSEC test.non-terminal.C21.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ rlSJ6AY5BSOY4f22caJQRbbMTp0r/tBQCp1z
+ Fy1r10KPygh5H1EdsyUreEUyaDDjG4NLE2Dv
+ HqGKvoE77WYbqBI5m5eoVMsxKfp/WnCbUpuI
+ exzlYpeAUSTEDOcpmwGqIhGLcVv/sVaeQRp6
+ sdsAhkeGOlzbVeuD7RiNS5yFUjo= )
+test.non-terminal.C21.example.com. 86400 IN CNAME C22.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ FmKXcpj6HAdwgsoTR09fVo7a5NkY1cuSFSWr
+ FS6IrPdiP8oDM4kOlP55FmlowttXzl9/X2nc
+ uS1CrQCehRFg/+lC7BivvrrPBYK0SITtd5jy
+ 0OESphgETllNINqh7MbUi7QGEMW7GQlCZpAh
+ iRBpttRhgvJP7GNj8JmB2/+0acg= )
+ 3600 NSEC C22.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ z2AH/FZiUXFzdp5qc9/W8VjFlG7ZKnR98mt4
+ 3rjD9OphTxn9AQlmIFh/u7ajd8h5pI4n3yXW
+ pBIIU6IskNAOTsuOQEHlaoUPJExC3YFOaf2U
+ OouvqNaUuHiE+iGLxUjAi5w8TEkOgHV85ogC
+ yXkec/COhgmzlaio/WtttXYhVA4= )
+C22.example.com. 86400 IN CNAME C23.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ d0pPwKiMl/Bzz5CEYJNxcn/y+SRwhWC3kFHG
+ xaO8dN8uJTtUpGgHGKEMuf4Ao7CbzHUElwaR
+ wINGv0/M0OEqNBy97ZBn1BA3auhVVREucykU
+ /40J3ANNNUqBPtQtodHkoy3bI93MwVO2QLoJ
+ WFXy15HQDvY5b/I0ckblx3dPYAU= )
+ 3600 NSEC test.non-terminal.C22.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ jzZYz5wBV+mnTfubFCSEq3GSLnVU6mEMNoT2
+ jd7xiOSQO1oP2QkuIfF/Ezpg9uSTPtjW6N1I
+ UNQg/6A9Et0xEEkrr3CSc60IHKdJxR+JyANp
+ zO2GmfR7XgFBOeVSOlQYYwGSUvKg7KxNjr9Z
+ U7/yGg3ajiO9nXijiovasjy1dfg= )
+test.non-terminal.C22.example.com. 86400 IN CNAME C23.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ rRWV4phWhoPhmdbUMnmHYJQ5547AJKUVz3mC
+ b0dsrfnFNhXmqPBRdYEDjW1XpbjMlPtd93Cs
+ xnBgpDjZWjPbcolIEhxategjesg3hMTQW7Bx
+ jfOUBJtTVDVok61mYvE/S9WCiXYnKGwpIlMB
+ z/y5jYnlQx9/funI/fwGJG/PGNE= )
+ 3600 NSEC C23.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ aVmG/m+uz3RjMgb6J8NxicDTIW10znm7ml2t
+ bowN2abPf4vda/gEqvJ6d8Aps57eitzWxAtx
+ hzyntAk0ksrNe5Yfn3b5i2fAxHa2r6gno4Vl
+ SerXbceSU05nMbcKfFWgZvJC+ejE3X5WivwE
+ T6V8dMsUsbJbHMxNQ/MrF0b/KCQ= )
+C23.example.com. 86400 IN CNAME C24.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ bLE+a7u73Ijwk+WaRbat7Ag/KsXP8lXcAzj4
+ rJwY3WtTRTJqlfZ9+pVxyWqP7rDKl+rADTKD
+ uFW0xVksNJB/ilmhnfo06EEIke1e4Y+7nYfq
+ 13vUR273fTyMXdc6420Mq0iRPMeIj9/uDEhK
+ BtoUOhh6woC2EaW9voPPI0yj+u0= )
+ 3600 NSEC test.non-terminal.C23.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ htCIng073i73nHFSbMT8i7WMUIksfl6e4/WC
+ 6VVBfsFJf+PAQtkNMgT8cr6C9m+6Mny1vUIz
+ hEIqGCUMis+AuvW65t37mf6oSYNsCaGKGaE6
+ 1oTLeRdrj+wt3OEPLuYVFFZjGB6f6hyg4lvL
+ RoEJX2WZfefNyb+rMs+tU3/Oy4s= )
+C2.example.com. 86400 IN CNAME C3.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ kDrNGh4/FUMAF+2ye8ARKEkmDBxxadg/bETX
+ t0QYC9YDWzI6lGsEtLBDN3ZJzAaZUac9FgU2
+ N+1yrrvJ/iCbQEwuylqGH/CExBUtFAF1ZEsr
+ GIuuoGuwXG8nnTgVzGyWc9vW4s8u1SNaNjS9
+ LHvGGM/B4UIXk0h/I46y8c5pwDo= )
+ 3600 NSEC test.non-terminal.C2.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ CFQkWfGMyaEB0orFpQEzOW8C0ecGOFiwv2DJ
+ ZrtgppkNQ22BUymk67inwyCE6/u/tOPgS0uc
+ +R9NymZ1h13J9RH+c+WevHNBkwMa8IqsI0Pl
+ LKqRDQXcJJG/MwERMP6UMmb4Wz9rU7r/DhN5
+ 7dlXK0mkP6C9jBj66tvIEYluJJg= )
+test.non-terminal.C23.example.com. 86400 IN CNAME C24.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ hkJQ0Ywvm3KiZgSboYZkxyB111oyYwITWLTo
+ Wo+oDpFbk1GTqkmqpUDPJjrSDew9DUr8S1AY
+ uWjEsulDMIyrYXIcNYNKPKs4AssZ8GSqQRoj
+ n+46k6a7FJQiV5XERF1Hk3aRMqR0bRhZ2MQG
+ 7iYaee8aTxDbmp7rRdE0P4O2C3Y= )
+ 3600 NSEC C24.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ VBaNTeTbLKnJs4/RRgRfhYMapIVINw6S7eT3
+ V2YjZwJDzGOvO/nEutzuuVno1KphWPEVC8OG
+ 6Um9szauRT2WTaBcCChZPc4zk0VWjO6b42bl
+ ntgrKZFJfZLKBTDcB3VD8njuhtx66ehLVzlV
+ zhCZbKrnbZ/OOAxhnxWTfVWpe9k= )
+test.non-terminal.C2.example.com. 86400 IN CNAME C3.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Klk2lGfcXAEYBqpgDP+5Z2ynqO/o6v3JYRcp
+ T/+GGCy6wiq4XuoB6Ure+qo419aqzvc3jIcO
+ 5qC6UoRACYzqnJi3VGixxaV1G5Abwj1n3ZQ5
+ QNK8TzQYGqLfokwJKZVSxLfD1v2OBb4Mvn9c
+ gfcBtl24Ze0H2rA3mrrTRFitoXk= )
+ 3600 NSEC C20.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ HIycCOnTJkJ0839MwneKccOlUB1GtAv6ZXVl
+ x/VQOWkA1TnRt9OTEuliQx/DJTh9guj5Tys5
+ aOL2KabztzbxOdxGH88sjtoXQqlOu2KEj8Bp
+ mh7rrT/+F0DM9OQy/q0ifeORu82oJX6Fpg9C
+ KdRmNJ8hHkwOIJxYjGWSzJ9jNBg= )
+test.non-terminal.C24.example.com. 86400 IN CNAME C25.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Czmz5uU/4n/H/aq/Fwr+VWWdt7U9nkIVrE0N
+ 7pKIa/rNFBgmR9IYaeSLfxQ8NxrJl5fSxuXv
+ A+rGKEDeqJxorrgq1OtazjyZBOJm1OEHJCDo
+ ZxX0i3GPxVT+Zb1+bdiPB6Cbw8tbtLSxsb0A
+ uW+212VEA/xDv7GUlSvsrIoUFkE= )
+ 3600 NSEC C3.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Cd+pBopnixnDuHMHwuY1zD6uWhvy80VQ/UYk
+ i5vd5hN07O0LtNC14JbQD7GATxf6KxnV+tom
+ sK0vo+uA6Wnt+Qo+FoRN6/5srI78d3paUaxK
+ o0R9X5jg+U66ahLDxGW8O8ox1Om68m235ofN
+ apDCHjM9XNlMHGqfqQinUmXsuEE= )
+C3.example.com. 86400 IN CNAME C4.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ORfJIaFI/tGBHur1ocpNzDvmQBlO8aVhSllq
+ GSvj8S151MdlUYeTI9yiYzD4hpAuLDv0pTbM
+ iMJ7197eL+cCoDynTqaucPoESOWxMw+RxcTQ
+ 820KIrYgwD7rziDqmnK+N9CCLI0BHy/nzHmi
+ 7gEPRj/4bTZktdsvpTZC+PV6gX8= )
+ 3600 NSEC test.non-terminal.C3.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ xFnTbPQYvJ9t/i3ODFPnIO2BLdXI/kzuO753
+ CQ71ss0J3KNMhhS9SFCtChX4RhWIAhi+O5C+
+ 7P+L7FrAFcENI5AwYlE/Lm+Vqf3xsDKwMW/f
+ HJvpRnDueJPEtUNF1a2hjT9MLQXJb3yIXi3o
+ +c5wicLZE2WI+0tKicA6opLauGw= )
+test.non-terminal.C3.example.com. 86400 IN CNAME C4.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ NcXIcilgrWIGQI6T++wpWj3Vnu0z39Vm6qbh
+ FPFm1gFk3S6cmz03rNsCJhOgu8y/Tto1G4+P
+ XOdzJmxrXUTQbHeWNnx4c4bbUIkuRwNXiIBc
+ QgW180LAhsfezm+5AjhwgrEX6s4WikNq4d51
+ sptd8qI0ijOiCcZMR590HTkAA6Y= )
+ 3600 NSEC C30.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ VPeffKas0zgidVeRLbKPfT3wD/tvB8r6XReh
+ Rkikc+NAAQxUg+AEKC5tNU0aoGdgdoCD4T6K
+ r12ODG4fZkfE1/SiZ34TADpvhn0PvTjrhxgw
+ FbgPPBfUh0Awin/Z8+rjBxBA3OGqzRP7GPLb
+ s3tD/COb2ooBgf/rBZ6EB/8Bz70= )
+test.non-terminal.C30.example.com. 86400 IN CNAME C31.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ jw35unduyitnUCo/8qjjlT36iC8fL0P2Hcm9
+ 4/zubFGVC6yQlsf1P3DeTIbdBhj/3JjTEpY6
+ YUuyZlLWL+az0dvtPkl2jbIs1m4wG0qOv0jO
+ 8Q+MqWwoZ8dpCplK3vnlj7F/05TokEoDx/BJ
+ 8Jx7Qg1Q2okgZQsYGxxQqOXDiHE= )
+ 3600 NSEC C31.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ hcQLdxXw+uXejfACrSBSJmws57Ci3fw3woxv
+ +CG7zI4U1rPqNpyMMxGYjDYSEC80+nWFCKzR
+ GYZD3THGw8NynuMWiGZtu7DOFdNNSOLnqIJG
+ 18sqlRa3eGuy53BtBCps8LW7dzfuhVC7duPA
+ oBV3sKxs+1HN+IMjWLVlyPSLAn0= )
+C31.example.com. 86400 IN CNAME C32.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ GGl6maOnsNFhokQQuSb0Ck87IPPluz8cUj7B
+ R1YO9CFnJHfvNXEAq7wxCKlmIPjk99IYGoXs
+ Tye3fryV3QVDQe2f0IlvpRs8sIjjZaEU4V8H
+ XZeED0qM415txff3l0Qdg3UdL0xf6WwWDuOs
+ Z50zdYe0SM1qzJAHxO46q9b1UhU= )
+ 3600 NSEC test.non-terminal.C31.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ wvlFSmXb5OkMvxyJhb2UELMzsMC2XMeByum3
+ XmTf22cPWVBL3HKllq6U8pdQhnfhMcUOnoRw
+ bnHRfW9xcyyGIGTK+lvrN/JuzzkmrgFo45dd
+ L6kWT+CpO0SzFIST7Af8EmDvJHI6/M55yogF
+ U9eXLkj39EBYNgxmCFftn9wUvZY= )
+test.non-terminal.C31.example.com. 86400 IN CNAME C32.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ XSJZfqQ77v4x6mxmhAw/U80wuuoRcdoitAyR
+ vzKcw+F1wSr++h0R8RAUZAfma9IPYn1NHMAQ
+ IArJNwBDrBpJCApzlaumwVUEG7HrgmFZcgZc
+ Z8ejXwi1A39xelRoOd31gcGMXpkPco6mJtJu
+ avAE0xXKeASA4eEPm2SMtZwJHi0= )
+ 3600 NSEC C32.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ j24n7vF4UFd+DTHb7wVJA08Y0IpWR6oxhDBf
+ XBwUUsGQGTxBWSCqlY1xkrZuuL3ALxVJrSTH
+ UUwVoRcP6jCj7nxyS+GlxUWugyCRC08DbWCV
+ hKztNB2Zw510sZLYQxDZR1OyMaz9QvjCyvUY
+ VEkTKjmJFMMgMiG5dgg5Rao/wkU= )
+C32.example.com. 86400 IN CNAME C33.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ i2xzsyFQG6YcMxBeCx9gqm+D+4U5uPau3VMq
+ LYO2PVnmiatCuzZ8CMbjoWpmkccZ/NuIGo+B
+ eHT81oQEuJ67HEoqqjptEdcaD5roHDjdYQM0
+ VW8xihhvrWevSvFAf3TJvNtx0ExC5dgBxVLD
+ oqLmMfRLC54d8/EkT121lt4JcwE= )
+ 3600 NSEC test.non-terminal.C32.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ wIo0ME2jtQSjmcAc4tFcslj3wYWtMSvxM5Yj
+ jFuZCoY77ymekfyKJihzkKUwNK097/vgGgaG
+ tz7BTa5YYbPVAOfpXKlTMyTCK88Dz4z4omDZ
+ bL5Xqi2JsCy+ShspDV45MIjoySeljHUIzjj9
+ uxUzx74CA60CbeOOwxHOdOaC4II= )
+C24.example.com. 86400 IN CNAME C25.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ gYBVp1OcHWe4LPdhDSFevDGOwvWbgPdzsEIQ
+ RZrkeF8NOSKfn8dzVrQZRYK5Gd8EhCRdL0O7
+ fuWtRu52L+mUI3/E5COBdkPn5VEvnevEgTlD
+ zyNx4kTCKUatkclfolpiMmqpmgb1yFJfe5tQ
+ s79tl1E4d+WbADMn7lIgbCug9aw= )
+ 3600 NSEC test.non-terminal.C24.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ SOxr/8bXKZ2PNb1YkRcguxddLhHI3Xh1+o1e
+ 8LFR2YgCRA3bNespLCfjs/DM4PLs0eDUzlhz
+ 1XskwOkWW0pd1pKZ48gw81395lV/hEbl6JJO
+ ZXHBGcT0dUyv7BwQIlK8SuJCWNeNewRE1hgr
+ yz835L1Ko3PLV13NcXp68B/rqQY= )
+C30.example.com. 86400 IN CNAME C31.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ vxCyho6UPn6+5gDMLOd0zmGpX7ED5VlXzn4h
+ BYzZ/IGkjS2BwcFu5jChY2hWx1jCVY6y+C26
+ OO13EZKaHuPWGxTylSTCNa0Wdk/FXy/srpHu
+ LAf81EAonAnAnA92YUWaiJ84YOjKAbGTK0to
+ bkO6pkXpansUqCYJACFyNoSMjCc= )
+ 3600 NSEC test.non-terminal.C30.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ auPELGv/GyAaTK5t6pf+nnWsokGem8HL2mYS
+ iKLMc3ZOGcPsk0rXQmy+ZlzFmxkk8ngF+cER
+ GdZHYxvMMdcJ5eSvrj3YoFAddsaUSQF6x18b
+ pEjaNR7Zz2CkKK8Ypv2vhdxe1XmlhV3JugYm
+ 7qDSKFVZ9C9PVZcFaMK44O3F9uQ= )
+test.non-terminal.C33.example.com. 86400 IN CNAME C34.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ l9XLuVwibT3kXc8zJQjY1gODdslxwCuDw+th
+ AaE9cy81zOfeQPUOyktmpmKMAiHHpMThAdoo
+ kclMMat83pl9j2DXKtxF6Uw6mp6Z/so6Gpf1
+ zRnqnZs0kJPLn832GgrMBFiGVxFqK5y6AS/v
+ ZtOrztOAoVWFxn8XOQW3g7DJ3rM= )
+ 3600 NSEC C34.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ o83bvjC8XhVPLMKInfawkI8xjdOWLREsraOp
+ s7QNkWkC/UKZ7vjr+21V0zupS7DywkQ0Emds
+ wrHoUEE4a3lx/MOlzwbGggsRtJMh+Msx+KbC
+ PGD0ADeqzDibR/GEbsqlicsPbN6bBJSMJAtg
+ hHmsFytnX1ybsmNN0ZAhnXr9wfo= )
+C34.example.com. 86400 IN CNAME C35.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ gC0+tgCyO3wyru/vzEejh1WTjRF7hnyp3PFL
+ TbNko7au3dHNmE3jrOX/IHN4E/t0EaqM1Cfq
+ znI+phrcSyjH/joNLFxjWo94pef1uJ2g4noM
+ DvKTyBLfImQcSATLDNXTDDaSkDhSg/yFR1qN
+ sbfllYSzcFLvhXQWoVsRfnHB0UI= )
+ 3600 NSEC test.non-terminal.C34.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ ZSgGLRY270uVjBx8w720q4ZMfxhG9hNctL1t
+ mZT2AoUGp7EiUEam6XbOXAsqsuBu0MkJqYbc
+ k5XZs7upPnIpwXf1zroF1yaRP3eNRj16/D10
+ cSxWf4CZ1rfmawxfKw0GyzLsaBZKLlq8XYI9
+ XgnkDdZYuEFI5bEhCJOLiI4J0hA= )
+test.non-terminal.C34.example.com. 86400 IN CNAME C35.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ NZosaFeYVy8lnQXgyb2NbQqINWjjqnMUrREV
+ 6LGp1bdoQy1+qtKpy9H6xGdDYeBxMCKhwIib
+ ZyKDj6TgG7x1rZ/GCXXMbcfH8txDaa7RlHlb
+ z6dr4v2ZdsKsdhFG3PVAlkESRMsu5CJuSEMo
+ gSrGiNnj06mYqYEVyUSlfI8+ZEg= )
+ 3600 NSEC C35.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ NzEV8w/88q4ERTDiKRXVxzUWcvg0a6bTvGAs
+ 2vwWaoNtcPfy9U1EQyJfC4k7tfaoMGR0o4b+
+ GLbnflDpS9wRgNxbZA1cxvynh2gEvFanq3JC
+ gaqslersgKviH4fuRLmWeSRA6y9Pq9qq8y+9
+ wilw0/1hLj123SV8uUyhl/jZezE= )
+C35.example.com. 86400 IN CNAME C36.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ EvBJOfv+jFIjrknKU7VV3Wq1qXAfG6SnpOkM
+ f7+I+uK+kzdd65RubuPXfccFiiRero5l1upL
+ xLNao5B56AvJruxqgE8BNsid5wFRRmkhKJMk
+ WwPhwp9sZWrNuplSf/kX/TgPrGclUBiEv+CA
+ orZcgkEFmClsgA4hGq4kJtJP6Ro= )
+ 3600 NSEC test.non-terminal.C35.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ i5QQPM0qG1rT8xWdx1QJHBYnmBBNZCVt+V3V
+ n+XG1UtDoEMFBAELuXoEjDPT/Kyau5rlLP6Z
+ Gltcl69c1fyglHLguY8CdyuPJHJ9XbarxQeo
+ ImJzamI6X4AB0S0kjw74L+rTx5STC4uDXFtF
+ iOi4wiJ+rVsegIpLzJumr0K1FNc= )
+test.non-terminal.C35.example.com. 86400 IN CNAME C36.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ MLXJpaqizl8qstAUaR3QkSDfl/TQisMzC5QS
+ 3oMbQkyte3BdI8s1fAKHGtRZPv+OUmT/UFdx
+ uxrzka8JebOlJ1cJ/vdx6pd0M/zlGkn6s0eX
+ 8AszjuZs1/c/TM3chLLVZeDFOqo7BOMCmG4y
+ 2SZb7R0G/AH5y0y+ha2URh63OAo= )
+ 3600 NSEC C36.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ bUoJf9G3DOPWZw9kQgH1yCd5/72avRoatYzo
+ MN3EwobQJZQXuQnPuXWAPRsm/ANBkmM6+LHQ
+ vgJ1Gz2UDtQHbis5HXwH0jUuJ95Fv+GhLn2D
+ udEkyvU58XvTa7BoZATVqwoLkaTkWyd5hEML
+ caOcV4HJHDbmTkKG4wl6tY5Z9p8= )
+C36.example.com. 86400 IN CNAME C37.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ J62s/WL8A6SulvprsP/qMcA4vPFSQ8h2dpZG
+ MxsmOpJ4x7FqLRV+0Fh9ciVPG3L7O5hHulDp
+ 5zr/06hsLoOiPbnvnKhN+zAvtyXDVS+PILXd
+ i9C1O6Mao1tKTzZog5aNkqpSEVt4E/9jHwA+
+ TS2tEvTSCGjwJ6G+jBxJs6xOvtE= )
+ 3600 NSEC test.non-terminal.C36.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Ypj/z27uzSZGZcLkGNbGwOe0Ezkx6RGggxe0
+ GmMkFxT9OvjPFgkRO/icpJZaGhJ6X2suo92i
+ i+igDOUYSqOkMZ5hY4YZvy2OkJ/RuHN1Ll0h
+ E7lQctKQ9X/w/KXVJk2biCdTbhYrsnUz0NXz
+ q08k1n+f0sUvn02eyzqd8a4It8s= )
+test.non-terminal.C36.example.com. 86400 IN CNAME C37.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ni0UNWDF115kUidL4E/bXxStuW7a5dVq4K4N
+ VgvYoZSColMl6FrSAPY6x8YgtGH5pALMePvg
+ 590V5l38jnMM+WwkcK6namX9W9QXApwc4uF3
+ 6F4nqyK1NFNmlbQzvi+DZwbbvExd93LcUvLr
+ jyNy54BGZfvUlNa+wTCsDaqmYrY= )
+ 3600 NSEC C37.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ LRStYbub6UIPA/aHLr4/Oty2bSiaoyZr3JW3
+ Mrp4oEaUXJF4ANn7Ca68GCLDKeU5aM/hQV1K
+ gh8Ms/Fll4Bf54/2z6YcZr0WU09aL8xSxwCC
+ tn/qZNtzxzAxJbCFwZcJtlGP9vsG03d+qUys
+ vfb7yOuZsxudhXAOt+SGl4FGPa8= )
+test.non-terminal.C32.example.com. 86400 IN CNAME C33.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ oX/R+pBjcH524Znqm/m1yDmPpjaQze2mtQd/
+ /Abd3Slt66b2crTO+fWz9SQ7zfJQzmtaMcgm
+ lbHf70ggM3i/DcC0ATFBCQJvuX7yoFcNLhze
+ 20x1ft0XDAQkbtQweT8/H0l6nwg3PraZ7Svx
+ 0rt9lkKj/FZ5KcLnHtWN9Lrl5sY= )
+ 3600 NSEC C33.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ ceYE7iiMcQsT7eXJ5QKH3mHzIGWUBTt0smFr
+ p1Jca9UUxfn2loS6q0hTKsNy4b8LNodhLj6I
+ 5MeWhjYeL8W9kXRudYFa0BcoEjVXtC5vb4qD
+ IR8rICG5zWmKvGDOrf4fFE6I7yGtNa7tpMO4
+ cds9eCUae/eKDe5geRZ0jHaAIn8= )
+C37.example.com. 86400 IN CNAME C38.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ HB+SVyFK98BpF2Isk+hS/mHgsZAHPKBAXslz
+ 8vlq86yyVEV0Ns3rRSjtzc8HcrcZNoikNTaM
+ Zp7p+qCOO288kHbOqDY5O/qHgHa/CTapaEcv
+ Fkrpgo6LKPeIg+uHZM+I+CEaB7W3nyti3vCY
+ I7z1xecNt/9+BSMghEX8EkbUeI0= )
+ 3600 NSEC test.non-terminal.C37.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ wZ0D6paF53u/yRQuJrvDCqgAKFe+eo+KRTSj
+ pnZG8Vxgvt9rtG3Cm17ftIuuuEjdm84A97Xo
+ FSnLKEOwjQoV7nKZa5dYJ9Ay6/EKKMw8q9JZ
+ lXZZ4gzJLRuy+cEVJ9waKeE0F9s9d4sGk/s1
+ yEmPdXIe2wJ472uRMIg1M1o8BIY= )
+C33.example.com. 86400 IN CNAME C34.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ dVYcj/YiNH/bwwUEd8i0nweQw3xSvN32/+sx
+ 0Z0zhoMhIOvYpjZbsE52D3jJG4EDcREumEQP
+ Ye2I2CPcBWCJEr/Z44sjEmX3Z7ltHdlYxIc8
+ tEyLZvgzT6PJAoU5/wB8SIpHIFV/lzE2S+96
+ RJE8rp0JzB/1EnhaGGqjbrxrJfQ= )
+ 3600 NSEC test.non-terminal.C33.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ JhjpFtrCwQtDofrZUxcocJr6k4JtNTkQvP/P
+ xOpRxnQz3E/9dY8hUogtKZC0HJVGAuTBwTie
+ +2Qu/PfDxwc3EEWtbcjflDJCbpY5oy5LKr5l
+ 6BUdDaGrWKf115WsJspVROrp2fNfZeVRD0Si
+ 4ixv9JQ3f5QMcbE8FMX3Igpklbg= )
+test.non-terminal.C37.example.com. 86400 IN CNAME C38.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ YgdM1snnCkbeQw5Q2YWqIp8dI7BsSabEIk8y
+ qZkuwfidM2tPTnBJlt9Ry9wg3Q9aHogPu7GA
+ QSBCM0NfsDrtncsxGzX40bIDStT1dIsr6BJE
+ jzijiBlyldXRdPX0rWnluf2d93rTMh+ZVE47
+ llnMs2sjtM4eRqBsBDcZoR6f04U= )
+ 3600 NSEC C38.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ G+Q88jVXynkL8sfXsIuzllJ9FTtXb2o74zZJ
+ 5gTlxVVarNwO0lYTpGqgbZ6K58RXE9krNz0o
+ qIKsigz1wA8ZDJ/emQcsWyIw8Gdf507sydn8
+ 3N9xFLvGivBCsuMUQeN42gOBNhmmncajTsFp
+ ywtNJBu+GXAADduGQAmo0Tjp4lQ= )
+test.non-terminal.C38.example.com. 86400 IN CNAME C39.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Dc5ZnZoMZ2Lk7xIXFPu9JQAzr94TdmYsosFB
+ Bkm9bBMb+ZV8g9GKXTXYpKD22N5J50g5w7EP
+ gXbs4SKUQ3aYlQE6rJzcEucGtYAaTWaO8cPE
+ tOzvXcn/RCeif7GuPUlh14M6PDAForLcoasC
+ le4O6IjOF0v3136T0HqVTK9VdV4= )
+ 3600 NSEC C39.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ ztZlnI1vVUKhtR16eZNhWkJIUU57ue7Am2Vi
+ IhkGV2NER65r8CU2RB0w9StqinasQlDnj1l0
+ 2kuFpn+/RkxZQ3NjXgpPLoCFS9P/ffPAeN6c
+ sLxbpV85RDZxa4x4QaZwp7Mxu1lnI6kM41L1
+ bTjwM85A3A/AZfgMIzxQ5YFMRHg= )
+test.non-terminal.C39.example.com. 86400 IN CNAME C40.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ EpkJIeFKf50ZZwAe5JGnIuIlCGNNL6D6oJax
+ 6FPa2N4GvC1Ppfs/eixEC/IKiclTUWmh0oBk
+ zZff7s8joTD47cPkQ+a6eY+6pNpn29h8vEkA
+ uhRgWKi07XR/FUVfCEY0h/fGCgKgDoEfxbsD
+ OGAlbRFnEHoimdzxnUU/suYZogw= )
+ 3600 NSEC C4.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ pOEucUBeuxkb7uvaJH/FyVxu4sqGM6GsI4+J
+ ltaKcwu1ucEp1GHCrUFmSFP1y8pAPJlq+yg2
+ IcnHy2BcRz6QlByKRO+iRwMn7HU9+GflrcK9
+ 3vbDhWFzkkT1kI9KflVUtgvxbwoIyN8R9kxL
+ t1+2l1crLIrs1fiKxKSbpLtL01I= )
+C4.example.com. 86400 IN CNAME C5.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ xlPwlgE5cuXCKPFYnA51jNLUz3QgnhjkzB2V
+ jH/xQEU0osobcTq8oqNS65dPps8xMdU4LkZ6
+ fJlXgTUfQ92tx0BAQiVJcmGnOp5rb/eiompJ
+ eGwzkZj89i80lD1GiehfvsCN1zdIIdZDQiUy
+ XHJv2QbF72crF6OcM7DoZfK5wCg= )
+ 3600 NSEC test.non-terminal.C4.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ DCX2xmdfjVSQ+34Iyqze0HAVWR1UlJQ6bvQm
+ hueDYIjRqjtog+7xLKsD0XyFGkYokNMm/gDs
+ eMTX0rfM/MEjhGjSG1Jqf4uyz7aLwcogDWII
+ k/9hiu1aabJ/2M5uNUYR7VD0ndkC4aGvBePl
+ 0d4nGMe3LXXhcpSUfHEblbj39Ys= )
+test.non-terminal.C4.example.com. 86400 IN CNAME C5.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ JAHcZiOe20fnNheDf4LJDcUZxgHTBRxmxYAc
+ FbRN8Lh3rMrF53p22OE8sMwy7nqnq5Dn7X91
+ ay2HzyDvDv6izvkaNxl81WEX8cklUmFRTa+c
+ orgs7FVzxajnOwC/TH+hKMk3aQGodra+B1fb
+ gPpugnaaAnTTzPq4HH5Z5/Z1dxQ= )
+ 3600 NSEC C40.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Q62qdAcJM+OQ1e658d/5D7PhjWEzGD1U8kVw
+ 0dyqBjHTmD2dGT8fuN0jTRzXSLo67v24i1XV
+ 35tPrVlEO1TNc3hrvlqyteXtYo8OKC9g1hiJ
+ CueAiedTlhGVLNOVCz1N0gD+XmlZfFVmMQsb
+ WP2an9mtc06wf7OqOPbdRVBYey8= )
+C39.example.com. 86400 IN CNAME C40.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ab2D01aHm4cPobTq3k4FzbYstK+aop5Z2fbI
+ 7ljP5p/7Cvp659QuU1B4bqkKjS2wx8IHIlpy
+ hZCYk7/ic/nbGp7oAnEENhExurbdFQQJU3Dg
+ dDuzvzPEayQYIXy/zqPLAXZcTfHuAWwjuSlT
+ 5M4O25sq00aSGrLjeGo+2zVlIrw= )
+ 3600 NSEC test.non-terminal.C39.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ k4qZMnzndAT3rPPZO7hRURZpEcQupg+b1v7M
+ YFQ8Zp1br7A9e/jNdQziDONB6y4x8khxTGSL
+ lbgJuq4BHwvigd9n9PZYlNqvGBLd4XdUihBw
+ nwRe5fV1S6dJj2mbQiZtGvuK8udFf/XCzSUx
+ uMRSAJmYJyH9+06FjdNt8hpL3dg= )
+C38.example.com. 86400 IN CNAME C39.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ MCE23pfK6/YRsNynpFofte/Q5/LgXzw6Irfj
+ qDKHaA9Vs7KAFq51v6Dtbo57OemiRlnebGwi
+ gpmqdzlapPbcuSh2zEgSUdZcnzjd8YSDb9Ay
+ 6Rme/4qxhUR7CMCUhVvOAH093neTrgNgRVcu
+ IaKpeVYiAW/45G0rOwL5/saBtbw= )
+ 3600 NSEC test.non-terminal.C38.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ iYlWNucYfq3tYnqjGtPu4qRWdXwGpHGlGVsG
+ aVnnb5F3V5191PocNAlY1K/ORxR3JxwBYi6H
+ UjZYrEvK+1UuLRjYTLdJWg66gp4kY1tpTl7I
+ YHIrQ1WFPWWqDXlPDR93bFCJ8oV+WSyknuyF
+ Iat1WCkT2f2KbJkCWUlOmS7HQ2o= )
+C41.example.com. 86400 IN CNAME C42.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ bSuvVTFqytT4NtNkEsLDcO2xvgpmaEZOED23
+ wCpEFTjkZQT3qS8cnowGshWGIT57v1YTH6S4
+ uA92zvBOHsgT6RljRkLlkdseRBN8I+EJcOUU
+ 1KTvRO90FQjpuxDVAkt4BrYvV5JtOpKMFC1c
+ NceY2aLJNOR5Z2dFJPALBykhIH8= )
+ 3600 NSEC test.non-terminal.C41.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ QypweRP8qWSerm9LvRqNWSXyfc6uJmAlgBr+
+ lc9YY1+JYHCsa8QgolF7vkZnBn/Ck5/vFvt9
+ ZZxer6kb/D5b3gT321hS1JosX/YUU7oiJp5d
+ Fgbt+6DLwB/sqAF9NmbY4s3W6VaWYg+Bl6Yb
+ VesIf5gMAezJtPkCBbkcjr8eq+E= )
+C40.example.com. 86400 IN CNAME C41.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Ol8L9Sy5CbpadEvyeVhsfxSCRTxOynPPCGei
+ tSNazmU3Jr+aDidycVCvbIZj5lX3RYOIF3QW
+ s8by2rvv2P8ceFjNk91Vn3rZeZASOZHMRjql
+ K0Cxv40Kd2qF9N6v+3JCNk3DqNsTNHb0HlhL
+ LgEHOo6mKaIafgDDZAc/FiUiAUQ= )
+ 3600 NSEC test.non-terminal.C40.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ s6zkrFyugb37qm28cvzQEuRdrTqv5lyIknE0
+ 1Sa4alTuP08hyaJQ2jvLjIJCxNekonJAFvM/
+ yGoBxr8UstaKkLVYo4f3wZ3BH69rcHR0CA2B
+ JGJPFjbFBevQrWJTkbyT/0viBEOvs9kqJXZL
+ VqukkkcpZw0KyWEjjH5NC8CGLc8= )
+C42.example.com. 86400 IN CNAME C43.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ZxaQ98LVaS9NvP+w36TC5WhcsHuzPVbRB6Y0
+ DjeARYJL1hGgUEK7XNFN6LqkXZlDZdXIxDHE
+ fp4lJCplId/k7z/0GCioZ2136nch6aZN18xu
+ enr5jpnzDxaYioJz1BFU7ybRnEB1XhiknREM
+ 8/+jdF987gCIk7wEur7vllgujMo= )
+ 3600 NSEC test.non-terminal.C42.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ O75FzIadBOmR0C7qdlCOeCkEMx/jJiI+b21J
+ ZPbxI46LEnbrTjPsBdU6a4g5lHurSINJel4y
+ Ya5khpxigAjtsunmOiHr3NrR8efr/Bz0JKKN
+ bSRZZCmoymIiGGM3AtbSN/n8bGMyz+C1W2Mx
+ taLB6Z75uxTH7RSaKoR/BNRLRkE= )
+test.non-terminal.C42.example.com. 86400 IN CNAME C43.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ oXYGbtjYBTpMxLiuDZ8N48trN3jUzcWSDQXj
+ 1+lfNtaJYBv0+50duql1uW3DVtNE9B9VSJen
+ KEinAe6aUti81vZb4ZMGO4L4V8E2cfmLeQNK
+ fGh2NLFb+oNiJMPk966I7WOEjCohYi8ELfCo
+ HH6wBOZqDB3E/FdTJJ7mAnUjJtQ= )
+ 3600 NSEC C43.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ oqkkYLvd3e7/4WJ8pkUqohpM1aCOOsIYRved
+ h/7F3fnEDIM7SviE68wP90ch1UZkn81T0iWU
+ meUJoesWhTBpA2mM29teJvNamEh8tFUcfT5x
+ ZE6t4fwNpNUiOU51UJu7p2rVIZ0kzQ1c+AsQ
+ G3YEcuSmOEJV6/p4uGIqvfbXP04= )
+C43.example.com. 86400 IN CNAME C44.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ RzZDlADhgkcbBMlcRF+b9Jobh6Udvs0ks9Mi
+ vstPki65zmQW9gZWUasgUMIRAXz5+623b9IF
+ WDivqFm4pUYq1iiweW41YGbL6oR70em1zH46
+ w8q486jUNlWbZGFSOT4ltIopwlYUQSAyJPfE
+ IxD7JzMSfpJ25hnpL6TG3YlmCKw= )
+ 3600 NSEC test.non-terminal.C43.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ bw/Z22tfd3MPJYoXsPIrKzWgl1osKGAPjJsN
+ Ls6KnXsQOfR1rv/REwq7Kb3Tna8QRq6wL1sg
+ 4K6ZTIKT7vj9vAJDB5ovY0pVeJTpCjdqJO3R
+ l/UXpUZiaxwmAtWUH8dBV/fvuJz+eZElM4WN
+ PO9Ob2GQq5z3tRcfP75HpZjMnDY= )
+test.non-terminal.C43.example.com. 86400 IN CNAME C44.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ F+3JubBGS2XBVUWbhVFb1aW3zreH07h8huCJ
+ 9qtyo1070x3k/h/8UPZb6l/eAr1WwqT0WPn/
+ 1BRhSJskwLfHJLz23YnKf2SFY9H3mdGZgZcm
+ +IELmPJaExn1hNsHlLeq+e1Nel0JmerFx91h
+ bPMZdLGuuYleMP6/1Ns/SuSy5JQ= )
+ 3600 NSEC C44.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ e639nuGt5Dbl/dmpRdXqekvtbfPD6To6SQJD
+ 9VXZaMRHsX5TXhw2b7XOyexG8hMpDOf2QMIb
+ DIWrxE0XqfETV7akV8I270jwjRiIbFYDEgdl
+ COtNJBYhZuzNSznzt7iT7fU6HlswUnFQYDSN
+ ipsToO4Bv7gnHjpmGtCJVR5xwTE= )
+test.non-terminal.C40.example.com. 86400 IN CNAME C41.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ TaV4mzo70lt3aY+oa1tTFBWBMhRVoc70WCHq
+ NMUOJRoQRdrPhZPADX2ZogSTi+exMisSt+Np
+ 2rKkkS01teSjd52Y3kM40lNyZ+ZURzPBJ+qi
+ LONsmE/9pUWX/NZ2xPohgqx/FNITIqgl+7hr
+ uozte8zWDe5rxVOO3YSHXSUM6wQ= )
+ 3600 NSEC C41.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ QNcQZqfrJJZ9ND0RKXdbcBjVE0szVlf+DCln
+ oIXbEg70K9RBtx7+kowL7UxSER6sDCq93ztk
+ wZWgPrskMZpSoXcSRSZyGVPtNC8Va/jUvX12
+ Lh4U0S2kRdpYHPw0ijcSagTWclMttngSMviX
+ iIV3YnTdVV0FBsHJ4ZyTT3yMNJ4= )
+test.non-terminal.C41.example.com. 86400 IN CNAME C42.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ O4uILstkjzBIKDcvGNTjEduKZHQcR3oBJlfy
+ ACXDaTFyShj4LtQHCbgm8bJVEcTxNTwyRAeH
+ MtbwmzAZ5L5hCiy+DBYvzBDdwucmFrkPV3iu
+ tfeV1V99NEEVFVjJGqtEyl2eHb+s6Ube+NKE
+ vW2E4gFG64sZaLTkemQuxqF2bOM= )
+ 3600 NSEC C42.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ GpGTUeRPOOweU8o5XDLCgO02Z/D1oRmXg4te
+ ajEf425NpmDNo9x2FS8K+jm6i4280sAyobk2
+ Zuq5hrA++vPzqdZtX1JNXl2u3xGMMEhb6FbL
+ /DnjUtDIH6H0pp675mVokj6MGG5k0H2TthyE
+ pvJ9ZR0CBb+RsROGuFKfCjH3x9U= )
+C45.example.com. 86400 IN CNAME C46.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ oEOm+BV0/vojmQfvSN4K41s7bKnJE1Z3B3Ic
+ dQj5cOL+AIZchO4DiDwVpgaMI8ks4rEjjaAA
+ AiOJ5LceWNva2YtCU4NtQQhKoTN1g5g9ZYy+
+ 1Y+B0szCi42ZuRm5kjp/l+dtgYHfuHD1ZvIA
+ qSuooxE7MwsCZn+jiT4WVHzhIQc= )
+ 3600 NSEC test.non-terminal.C45.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ EnMq6VpLuTKEXl3rinyCDTb7GcflpPIKOYkr
+ MaqZSul7MJc+Ro9e43CFTvL+M8QyZ2XPrdx9
+ Ln4as0gnza3N185srCdBMOIZh9GZjTYJaV4F
+ 4fqIkm43r3ORebu4Y0/J4GjV46TEtOFaUv0d
+ InbqXvzxhXDnAW6HLqIkKDKJqSc= )
+test.non-terminal.C45.example.com. 86400 IN CNAME C46.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ P9fzyX/6o9pbuCZmQOWLrY0LwMk0MPnG3syV
+ zWLAlrl+7VptGu4Yg6fLSeJmGyc/OM6xJ0np
+ PPL7+taPfSaWyblDLW9SAQqe0WOzJV37Hj4V
+ BIZfVUfUT7sICFSxi9gUnR8607/wrWFvIn38
+ 5pbym/Bb/cGj79b0gSkdzZyk1lU= )
+ 3600 NSEC C46.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Sx776XCx7j4qqUYZN3dmzKU78U9O45iQq4zt
+ 5RT8K/8zZRjVSk5/PTPJsifOnHVpfcCD54ZT
+ WG4RqkeDinH46jUk1p62x504+4gllJvprJhd
+ 5sc76X5gzeSqJc9rvWHpFjqVQz2DLtAQwZza
+ uXuMtBtzzVBjWi3QRgrmDZCLK10= )
+C46.example.com. 86400 IN CNAME C47.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ V6SYuepLYiac1xs2cB+uAXa+HaxyqiIFu4ju
+ +f4xYdouR2TXfW8Zf2LFM+vh0X3cE8S64I27
+ TJd7Ddnhate2FCvBSHkylRaC8L79ENIU2U4Z
+ T3CyF4fT7WHShKzgoqmpyzi1K+2lYfFqe75w
+ WQvSDmDZTTlnrTrUatmyTWiUrjA= )
+ 3600 NSEC test.non-terminal.C46.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ oEsrxq/nWiqkbSVr3FDwAtktF1ywiqg3eg0p
+ DzNlVsffOW9HOuOoyhzkQ7ZfpLP6wRq1sWLO
+ R9T40PnSw4Cnmv6Q1p4m6ekUJ76sdOsMw6Fi
+ L+f7ZOKumceVKKxRReJddjPigWITmAPCZ4zP
+ +/2TihnruNI0I1g4nCRrM2dzuXU= )
+test.non-terminal.C46.example.com. 86400 IN CNAME C47.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ giiqTT4lruXdmDYKZPOaxVn2pk8XymS/aIyp
+ aHlRfV5uGAKofzmMAbbPE0VYHBDZaJWilGOa
+ UnqybuF1uiHIx8u2HpOPCV0TV0LUpDVCVC/R
+ EF+Gz6Ppp31R4/v1fQLUmWpuscuic+t8QcnK
+ US769b/ioMhRDDEhNa1u3OMFA1w= )
+ 3600 NSEC C47.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ jpDaUkBcopAD480T3rt3VlsbbwqL7opc4YCi
+ FzY1StUu3emj4D8jr3NfWBVMWNE350tOQQZV
+ IrZtUhTWuv6YMPUbV5Q1/lt3ICVLDueRSXWv
+ MovnC2EBBVHzI9qom1PPIgNxU2Sh48Y7iTtI
+ UEn+g+mzQeCRTV0F71bjU/Ioxj0= )
+C47.example.com. 86400 IN CNAME C48.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ TJzeQuCUrzmTEHZ8QSzrMh6z/GFOX7g71Sx0
+ zB0rngSYY923lKS1PxMx93+i0XcW8q/1w9+d
+ ReM+qUIIJCaa7yEDZI1qplBVXYKbEdOO7uKc
+ pPlN9jTN6SyEMfGIKRK3nHfuFPVWN2WCy6Xr
+ fTlqW+ctq3dGpHplxffV4N0jyY0= )
+ 3600 NSEC test.non-terminal.C47.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ e5UJH3Myw0x8JReinOHQ/x29hgSS3YZMKiX7
+ BvHJmhsDuNiElFEvAfVqZijU8IS3TwqQRJq0
+ qefqNtnW6jY38GsyQ7B9fJjcnC+KIVnNtrOA
+ q8xTcGjILf2ZtYtT8uUvQ7CIldh8xWFmt5XL
+ ePxUnWXbQzZBVUj6hgg+qGCjuUU= )
+test.non-terminal.C47.example.com. 86400 IN CNAME C48.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ DlRuPxygmP5m/dHI/LLFwmnYBpgbnbcyE91F
+ 8D0A2lENUsny3OGjtwR0F3Z06JK+PsX+tEmK
+ sTwix5i67R9rAuMef4+BXEVYnsCSayWx+cSh
+ QXAHI+2OxOt0zy2dtAjid6FwAsfUqg+JmsJT
+ k4d0ddJ2DEiQdjSu8oiNpyN6sWQ= )
+ 3600 NSEC C48.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ YOmrIVOWSLCphgFPU6VlghU/+1Rd78yKkL/C
+ I4iHB9Ts/dmymVWdDH4f6R0s+NtUj98RxoWi
+ wnJ4mxSi95ZJvxRqAgf5al5Q/23hTM+IIBA1
+ uc+6lWXMk51xhEjAovENFBd1VPHG9U9dhooo
+ MQgDm1RVBsJ6R7X/MUvUuJ7CXec= )
+C48.example.com. 86400 IN CNAME C49.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ mstE8tIJelsNslY+HEk8T+S88IF96MEctQ4I
+ 96/Dg6qJ6aj1pRgId3Y8GgXgrHKDqmdrzgJM
+ g//rYXCL0nctFXZevrXLG63ZXVgcAJ381/tU
+ ulP5VfaFRYGN65Yje/NzHPQaWuMshnODbjQj
+ sV8SQWNkB+7jZFIFlMCQH+B0CJU= )
+ 3600 NSEC test.non-terminal.C48.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Jq5at4SpvTRt0XIEGr+F9EynvMUMDRvwpi3Z
+ 0dtq3525j/bEiDiXJsH8CPsrrrAonIFFKcYU
+ 2RbtS3SiRbrHmOWuXHRlyinEdZsFlzM9jc0R
+ g9+1kNwnOV4SvrNdQQbm9MYqHwIjWwUi5k5Z
+ VDfsTkaBVCOH+qxqNzv/p0vGqfQ= )
+test.non-terminal.C48.example.com. 86400 IN CNAME C49.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ CNI+oxTaXWMQGarEpaZiY2G5mrUwMsmC9e1k
+ xV8HxEpt1STVrohXagCdBJIqaf3MWj0NN3Vi
+ suC3fXtJ72OfkxJtKe0PRk4AjbCZyeB+C0a1
+ R7QHl3RIFll9NEUTGtz7P0dodWV336YkDZ5y
+ fzEtrecwyeX8+hOZ/iv1vFcX2vI= )
+ 3600 NSEC C49.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ TrOIztZBcvyT93kbLDtfBTQFsxBpytWAjUCO
+ FPc6xb82vd0qq1tkjkY7GXzbb4cX1V+2FYmR
+ gBLHLal9TlIrdJVOTQdHQKwnHo6aadGd1ayi
+ QsJvZ/400WCDPVvE8pQEK8MKWR18r5YpWa5L
+ cQQKgKlvQFozX6CzJ6ItlJYq5FI= )
+C44.example.com. 86400 IN CNAME C45.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ BfFkRZHcn+j8Mxe1Xg4vRkBl6YdAyeJUo4cO
+ 3+K8dlLr/dL1C2xZQmwOePkhG5udeMMG/PJN
+ 469UTII+woyX6tHfXYoP0wmsRLruu+wv++0C
+ zlkcTXdrR7KYyZZ1+sm+VeQl12n3f3aXLQq3
+ 36LpQALyvtHBZ1Ccph7zk1h+Aog= )
+ 3600 NSEC test.non-terminal.C44.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ gCTROnakx0cguzal+4is88aIT3szhqm0PlD4
+ 2gSCSNthro2YyEA1x9anQ5t3d+M+iebLokyN
+ RpDqY2WLyqknI9iPFSpVYilMl7G+JWBt5o3D
+ 0tB8nhVlA4B83VqnQsQIU4Cdz04JrhHo7Qq6
+ bPO+uU1EFiSC0q9lTqcAKvbeikc= )
+test.non-terminal.C44.example.com. 86400 IN CNAME C45.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ tLav4jfnrXhG9RDhgXfFdV6PzZs+HdjxV5gs
+ arEP/L2RLnahWcpcAGDvtzBYNTr7mYIkr9+I
+ M9nLtKNtyWT4THtPTJFjizLh+HaC49J5AVSH
+ Y7cM6m1uwhqvPBDj4LINsR/xz8SvQx2MZMnW
+ AIN8+KlDog+VxggDtMuMxBVevsU= )
+ 3600 NSEC C45.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Gw6d74fziYMXchEMuCKvBbgUbHFYCytfTpcz
+ msEU+7EWjncgPWkTHwHejkjbmYDL8v6dByOu
+ hB890KlGkINTXp7nD8T9B4efttoE7OgnIqYC
+ S6mpqhzMg9Bf2Cd+e7s1Ok5siREZNO1AZIG2
+ izAqkLq4l1j1ug415YIOUWcyKuE= )
+C5.example.com. 86400 IN CNAME C6.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ H0KhqMgZZNHLAYQg2x4EWkrfLjhZo2M64nOq
+ mLZNgDr2ubtlCvc1RmchGLwzglEicA6D/lfn
+ u/+etbbJHfNq/6ERPzRwje341l+OZ2TKXp8z
+ IbtOd8m7m1KVTOcdsQPjqLzjpoM5/DZTJqbQ
+ nK7sSKJuT5tVLUcfQ293zEXMIVA= )
+ 3600 NSEC test.non-terminal.C5.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ XBzbnJBPjiNe0aqf9i++JBNkVgfKzJSOWxbK
+ WdGBAE+1oqrwkngVCADRBhCVIqQMUKfJtaYV
+ R6yncIT6CNR78idmVGCZ5Da1yJGQYL3HsF7s
+ gMuTkBp9cO6xFuFkOqsRfc8lN+oeas5Z1msV
+ Deh04viwZMfoKgFWpL2MtC0ohd0= )
+test.non-terminal.C5.example.com. 86400 IN CNAME C6.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ FDq6kVPWy+nYGNwmgV6roNqzHyyh4HI/R4zU
+ VDNIdTqR+ItRvhHjh3g7gc9II6TLPDhgefAi
+ 7sehznPvCn7h1I0YixtoDrflj+IQ/iYxJLTA
+ h9bVLUfmpfuBCxmD4KZYbSnVeVdVHvtepruL
+ tg6/YvVbtfRRcroVpXlTr6kGJgk= )
+ 3600 NSEC C50.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ ecs97WZFw5MGHB+zUvO6jyKZ+ysP1+agTLNB
+ hjBirXZwOowbmC/CekUcWvuCPkSIpCRzYBFR
+ b6kY3Q25wikZKjwW8tPR0qYKBR+0UzFVXjG/
+ ZHyk+4s1Osn7t/ly4yu7FmihAldhAzyZrMDX
+ P/Q0R0tewwQbKlHqtxos6mVPyGw= )
+C50.example.com. 86400 IN CNAME C51.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ oiIkJXCUD53GZK7WEyfSi26asLcAG/XTWXY1
+ mKkz1iUvJNYfu/P2AM0wYutS5tzPUb4ZB4Y6
+ ODti0WQy2ycW4bRvPUZI4Yjh+eHWCAi4slPd
+ cQoQ5GSx/ddGy8ryRgggQH3mfSSZcylw2MDv
+ CJkcNqmG8jbyC6W8295lHx7Btb4= )
+ 3600 NSEC test.non-terminal.C50.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ ngPIRnRpksfj6eLSAWH4srqirM0+0wTbpBr1
+ 1TF1FjLCvjEQ5ZmB3f6vrZAQaTmrQIa8jVpY
+ 5gTPB8hpnLOnkgA68Z3oSLlbAQ678XaoOHEx
+ EBaiQhryl5krfDhMgNqD2U/5804u4lDveiv3
+ ZSB/SQVmDepFS5ZmmVIhwDqn5+U= )
+test.non-terminal.C50.example.com. 86400 IN CNAME C51.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ tKZzKPoJekvNQK7D1l0aa3b4zKh4u/X3GY01
+ wpLZzlto3YiDMINWJN3E0/KdFojFEPzGHrYp
+ SHkcuypmdthaKfOS73qqiXulW4MyC/c87zlo
+ epBmzf7mcpaJicDdYuuYXcVrJg1w2wYWcQJf
+ 7cb48UeY395LnG0O3nG7w3VJ4fs= )
+ 3600 NSEC C51.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ PeiickCPthoAdhf83mJ8wIgJz/dp8q8RnsqQ
+ 2GHWnrRgVly2PRPcCfw/eLLjxF/QzM841bcS
+ ts/awWM6vQBXlYp7g1+Nyn2gEazc8/Uxdz80
+ 3DU5RB650jkOetG2D0BMDCQhwMqYW055Crkl
+ zwEGhuuk3u8gWdqnYV+/MjKCA64= )
+C51.example.com. 86400 IN CNAME A.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ uYXTGlmDyvUA8cur8NjK9SfKU3rd6/hgM5vk
+ fZ+8cpYtolMH0cno/miTU5vIFYrIDzWM0vYM
+ VToNDGRdDTpr6R0zozJ1/tdFbuuyUAkkf4DO
+ 5UGwQ+yxr79dGiiblPM4xA9+dxHq0/VPwT3P
+ ydGYJ2amf4uow/tiqkyEHqp+FvU= )
+ 3600 NSEC test.non-terminal.C51.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ nAJDYw8PW8PucLWxBiM6tRqVp9E1PFyUQZBE
+ msVtpk8dlsx/1Lro1BtWAYYLjq5p4Sz23220
+ ntbtjkvGTQ8JhPbCYI+MlZPGqhUQnyXKkDaJ
+ SMVBcfP8H1J/qB6BFapOGHj4X0DzwV9yw3rf
+ owwNiYRuY3CQ/huDquMLpGDr5z0= )
+test.non-terminal.C51.example.com. 86400 IN CNAME A.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ USwIlYP0qfA6t4qTDiYeOQ/eIKyNRkcj4fxs
+ GVwGf+3z1dPfwlJn6aDaDtSag3gSNqOVYc5E
+ 6EdDE/g8hkGLMQe14qDzXulCTpG8eeuGhFJg
+ zYA7JECxj7a3whIIyogLV1vLeb4ps3jOatQG
+ 8sFmcsbybikTOKFbZekJ7ZLSgkw= )
+ 3600 NSEC C6.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ vPoy5ruzXlnKfKolW5Prtr5b9seVOiQ0Jd1X
+ Z323gO0MYyPbR+QmedhbMas7UT6tQdSAqyk/
+ BBb60ZcCXGLLqE2QtSUJSjo/ggGSgKcKiEd5
+ APQbiitXL3LSdqW9swQO1WQYcZcvfrKo6RUl
+ +56K9UBDsBU8ZvMCurGPwwMH3js= )
+C6.example.com. 86400 IN CNAME C7.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ U3WdoX39xgpI6TU2FpTQ4ukCRbpqwYD9Jtu+
+ WBFSUqieidiYWBA1kQMuBKKdr23JFc9xUMx8
+ 2H6dDDadKK56Bs/lc6lB+GVFHP2g4cgkQRzY
+ 5hl2GLhV8NV7j9h4qZ/RxfCAzRaN2aTS2+oy
+ OiQu4yCjj4XlavN5yoZkwcU0DHQ= )
+ 3600 NSEC test.non-terminal.C6.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ mytT+aw2B+FSYEdT/fHfNIbkuRFXE1Sg77K9
+ 0fxuBk/EtXC24ESHFGezdSvc+c0kzujU6DKj
+ 4ZK0WEGuoPF4NF8MPU2pzzwIuv0QzaCpm1g2
+ qsCZaEJd0e3BggYwUx/SNnmOPP5nWvVmH71c
+ I9Lj1//Y5djz3ticeLSVAT7kTyA= )
+test.non-terminal.C6.example.com. 86400 IN CNAME C7.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ CYLp5AzdlO5+RZTj4UIZ9YqnXM7vXtttEPq0
+ rnXRZjn68xrN6o4X6qhY7C2lswlz7d//e59O
+ nxoJQrwsB6EDbphecQd/kWDMYA9Ss1FLRU3x
+ 0lfONg2eaclZMexNuiNgR4tiF5dpytoCWRLk
+ eeyk5JP4tC6KzkzYmqGQuHDimXw= )
+ 3600 NSEC C7.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ K8D/dJf24FcM3T6ywA7yN3RvKNKjZHSkVZtQ
+ oQtEbJXF1UaTm2C3B5Z2X4r9P6B6nZvtd5ur
+ P7RikH0oKn2zqDPfYrOaO7SZJf7QQtCmaiaB
+ /uXusM9wSxT4Fsgc5CZyFHfkMMsTKO/3oHuT
+ PBNHiVu5g8yKn3E5X6UZiSXoj1A= )
+C49.example.com. 86400 IN CNAME C50.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ xYTSz+cEvrlbVeS5HFTLad8VVOvNldTgZeqX
+ egmtynhDx6stG5rGdaD51zdmi24smEYo8BPQ
+ jtCU8moyFKbnIAscv6V/re4KGP4YPyRzmPUI
+ ihoNAUSaP5jksxUmS3+DWD8PaSfA74L+PVZr
+ SOsZM+cGzGnVQ5/Ejp1lUWn2y6A= )
+ 3600 NSEC test.non-terminal.C49.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ EJEqqBgAtQNULlby/XYv4/G7fly7PAJ/+F5t
+ hhydTQW+ey8By32q3emn0Anquww5BKz5qIni
+ cfZ9z/lKvuO3syuE+VPMlber4/bipZOEIuZB
+ GjzPlJbTr4bCt7V88fTCLKLKVSzPr33o33K6
+ 7l9tsW8LvqgIKFKolYLGaOMqgkw= )
+test.non-terminal.C49.example.com. 86400 IN CNAME C50.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ZhPb7s9CoPsBWUq+IGnga13wYRqk0BYjpnpp
+ bsKai4kgQM1zUXDF1nu+wn0jEL6kPz7kcxkk
+ hWRD21AHli8FKJix0sKI15sHHbl4J/NUgtU2
+ tZNqulcYgmBhIblKUK5IAz4RWITAP5LN0yN5
+ ToLSPY+jIplvhESXHDkpaQl+eCY= )
+ 3600 NSEC C5.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ ywRHS4r57DkU7gj/UFVoQMRmbZbyxRaLwjeu
+ FqN2gFrQCkmGTMRNul5HfOfsPI9x43a6Nugs
+ 0TqKVYBB2/06/5QBz4minnNHkQ1uJH3FD5z9
+ PEeaXVpZu9MFLw2a8jK/xpSg++x3SO/F537O
+ n1ogyfuaserb7IvI2yx3ly4CSEs= )
+C8.example.com. 86400 IN CNAME C9.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ JQhBCOWrOMe9jGuLF+f/paJ40YYPIVV8uqDk
+ Hc575+FNz5JvqVNkgAx1bJvd8gPtCaF/FR3Y
+ WhXkOOcAtPMjhrY7T/XLvLvgdn8aTgn6fZ8u
+ 6caWLoNeT3OKLPitIDnESe34VLWCQ/UlCgSp
+ YRHFKwNrhQ7+OmOeGpgLT/9HiDA= )
+ 3600 NSEC test.non-terminal.C8.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ QcnC1ITlSYRwTkOo1OH9HEctrQOvxdrN6tQr
+ HTOBRUzyXRo2Y1h3C26jOYRJByr7qXKNawjN
+ Ter6n6IADEPWRYV7Zg0Vt4+qwJuMQjqfuSY7
+ 1O22AohxORNNWwTN0xY/VZ32ijaQSt2o7BhB
+ WihKVAfpESOYX1jtQ8W6F5zFDSw= )
+test.non-terminal.C8.example.com. 86400 IN CNAME C9.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ kdd1XSlB+0KJnqo7un4RkRkv6+K7gOH+s6fV
+ FK0vycWI2O+q2FADBbmwv8q8kOv9vd/QsO76
+ lNXgXRMzklgxBBY8XRHQ2yY1ZGW0RabQtKFF
+ YGI3gcyKrkZ4YztYLrUIuylC/aPHQpJ1f3g9
+ dTakqNvJHenZyR4di5CTRoC6mAA= )
+ 3600 NSEC C9.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ G0si7vZjW87SMbVguQyB3akDnM9gAbIXks11
+ BwwvPFkKqdUxMP2Qs3IrzIKfhvQ4tADv7Voi
+ XRB/soteQwhjyYhPB0v78JfwCW+QwpDvxDRz
+ rSGeyv6eVB3gvBVcrM4RHzl6nFj5fmNUzmoK
+ CFdIfGPUeX6JLUO3W+xKURG1Nao= )
+C9.example.com. 86400 IN CNAME C10.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ LGxxUe4DNITcidfQR650xyr1DVBp0D/rUCDG
+ u2oTV0Xb5ouVH0gwgXhgEsJvcOrEgk4MiHy4
+ jhw4XAC/gmQ4meR4TmmRwsas7LaNbW2QKItU
+ DsWMw+YZEOZ7KhLgmWZyQpYleXffL+8uKUrP
+ fOdwL8Ww9OBF4ETlnju+rdEc1VQ= )
+ 3600 NSEC test.non-terminal.C9.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Lvswqag0N0AEJ3kFXHQrM2Kz5wUFVcq4zDQV
+ ZtcJ/iIb2QPDPG5yT3pxU5HIoZ1gjOeXfNCv
+ lJ7pyvtRpW8bGQj0HLd2gOuUEh70+qo2aekv
+ 8QJazPnS5CBPjWZvtSGmP8QAuj9OdHirnLRm
+ uKwac+IOo3oZfWGq+s8as5Fyoww= )
+test.non-terminal.C9.example.com. 86400 IN CNAME C10.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ D4ky0PqnI0bp2ktdwXEZ80xYFkMGcLLANsIr
+ LqZvATEMOjuVzbUjJMdNLKfLmBlmGgRr5sVf
+ 6gaXA9/eo3JcPH8zMVqmyvY6gD7TJcXtwaQM
+ RP3jbZkSiGCtWMICKLj907KJmgDnMXDQCVtt
+ RYbOGuYLCbHVL5rYJMRlCESUNTQ= )
+ 3600 NSEC CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ EuEUuQ4r43JFOIPkECbzhAcYpaGriljm6pDj
+ rJy1KVDR/wxXTH8f/uQ2u+BtcxpjyyYcG5Kj
+ k71Rt2WC1Flsn6eE+2yKL/n8LQOetoq4waAF
+ 6c/lndg5DHzk7Y7GyI5wzbXdA091dyDblaS3
+ +AhdIja5AST2+Z+969qqDcf0/oc= )
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com. 86400 IN CNAME AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ C7doptCNcuiuKJ2GrVAt7Qy+5LCN7FZi1E2C
+ Vqs66YPNUOb72yqT+ecRaNhHLiHIqwbMN/+o
+ E9S4wfs9q2CwVwvyocJTKPyN0K0yddn6qcnP
+ BRHrUgKJISxNMcYJVSwdK5+hBUbgUpObWt+3
+ EOlwPcpUrKHfKm5XzllM0DLK9KE= )
+ 3600 NSEC test.non-terminal.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ iDV4P8UjilQqgiPtMpHLM8o+HRinfsZh5zqP
+ /4O8JDb6f8qVsqe0DV/h0sxVOjfV34Xf995y
+ xI/Unu4aG6YKrZOjuXxd2LWvyuKMsPGXMoxg
+ gRS6OnFdCQ7+KTHOOed6QVkCZIB6w7FdkKOn
+ KSn8QQnuL33RFPkPDeCWdZaEGXs= )
+test.non-terminal.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCA.example.com. 86400 IN CNAME AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ pd+sc//VDkJp1+BlbFho303VkO93Xt4X5Qgt
+ ONFE+VKy7RQZO6/1rZhrLjg40ksy/z0XJR8a
+ /jcF1Er2uyNOvPnTwkrhlgn+QlvbCv8I0PX+
+ kbFpfCEBLJf2LIrVm5hVUj90b8eMJPVs+NEQ
+ W0SjcTleg1I65tbc/EWrR0pKvck= )
+ 3600 NSEC cname-point-to-multiple-type-a-record.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ OGTyOmiqHSbO4VTI1ud+uSjCMaCNTFU4htwX
+ QaOxwgZp7vRuwXWmm4/bcULrEGO0ApbfO/dB
+ Qjqht4tCubrfohjTCKgrdtnW1oTDCX0WEbny
+ Z7FiyfkiWGWVPkkeR5Yp/jbvLNfpq3d1gwuf
+ Gan+0OJn6SOHXSidukU0OUmxvVA= )
+cname-point-to-multiple-type-a-record.example.com. 86400 IN CNAME multiple-type-a-record.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ HRwBdthTRu9uCH0bRVbhESMAMklWD++Ays3w
+ px+IJ5BNFUncLiN4tOF4B0/7fYoMnUA4Abvg
+ +Ge4La3iDNmEZf+39jVpg1rd7q4xpGk9Z4+K
+ IL2gGxfDKbPv47GBhyr+p3KliUMgSiXmM87j
+ 0N23b36G+/pMplZU7WHaQRIYjjk= )
+ 3600 NSEC test.non-terminal.cname-point-to-multiple-type-a-record.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ erY9Qx8ZeDbt5bKHRc9AUDbHCMZh8z4ojg6R
+ alodTp9OnabVliXc2ISy8jadyQy0JCt2ESFC
+ sxL44/MysCk/R+Nwxy8iwME3BaPjURwzwERV
+ NXoi6sw9nNZiecI9gMelVzH2BsNNf942/zsc
+ xJvymebJO+5Tu2eEGbWS423gU4o= )
+C7.example.com. 86400 IN CNAME C8.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ InFwe62Y4wTPpTE/YwJvm1ZgdmRLfF3TSmk6
+ mIgPYhBicymtPKdgnwV12GYKOdhbz/5Zvr69
+ AneCf2nM3amgUkgBe6aD8U599MIu9ksuLkfA
+ NoKdaIt3XQHFzGar+mvf83xnJNSTm8VvHQAf
+ h9KkrBB+N98zYsq8YclkJxgrwBI= )
+ 3600 NSEC test.non-terminal.C7.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ weOiGDryL0M7nv1pju83+o+SsE6Jmsi8+Fdf
+ PO7dXTqCbCQdANicrF3dgRqK3QkHcVzKgf40
+ R3UFoG8aX3fyYKlEInCpfO7dOADGnzbtZ5v+
+ L9qgHC4yIvJgIcBAVx2/xOVol7BcWyUYkDQN
+ g+C01TRDP7ogAHJUjwPLaHarXbk= )
+test.non-terminal.C7.example.com. 86400 IN CNAME C8.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ hH80bLwy2ntrP7ZHkkkvUf/S/vQWx4SYNlyX
+ /KihAsyT6wGxDsQ2RH/iIMTzsHUdLcDMf4gm
+ dqg3BgJSMnM53xbmjjJroTdM1lz4TcE+doiC
+ KO+ikqNOS81WnjV8l7ivqcJEJwGPb4TXoOos
+ FTQHBIIT2sVTRnGYcG+BYx0xLLc= )
+ 3600 NSEC C8.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ t8/6dGfb90qFvsKF76Jv1K3tHNt2pIFw9iX2
+ fvM/GRn5zJqALRpDJX1CTQTSTitHgPkGn34q
+ 54rrssOha4mfnd8E4VSL6FNP7+R3fXadB9pZ
+ Krozgbq4MJBFFSI/OlYucFSpK0rxXuDZZxlX
+ dQi68ViBp3Ghx9B02YbuHP4Rtbo= )
+test.d0.example.com. 86400 IN A 192.168.1.2
+ 86400 RRSIG A 5 4 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ d7amEXl2t7Fltx2oqQzhgIXUGF4zpU3PGJ06
+ K2qZPJ5PwzQtrpAiywNKFuk2F4sGInSjPNwg
+ SBWiL0h9DDKNwc9iKtlPEcSZ1q6nz/9+V0IG
+ d0nF7EJhDGAsC0Is0/ljZLfJcliKz+NeZy+7
+ H+q28Aywpu5mEv/fd0o6vjgbuFY= )
+ 3600 NSEC test.non-terminal.test.d0.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 4 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ eD4bO3QmM8GdweXtZ9tXTexxJMIEY53S7meD
+ VLkmpXZsFYpHXJsbWiEJ58yf2pfl+XvcSTbx
+ xMaqaCih/8wWY9XTH45FUUf5MMZpZNSWo/es
+ gMxc9h05pL8zb6P3EdCYSvHqpnWESiKxOuR/
+ Kmc2Gwa9e8dmiuDp4x+/RARlaNE= )
+test.non-terminal.d0.example.com. 86400 IN A 192.168.1.4
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ YeKJu3vDpMw9BcUtEWqm1BpKfRfUW18Rd13k
+ j0Cg2TgdcnaGmDwwXUdksrGkB5L/eeSlOkx9
+ OJUjnNM3LmoaDkN5OK80vqiQZf/JxTyySyYi
+ wo9b7pPr+jADB2PdXZ1bJcPClfD297B2//3+
+ BZVRHL6FN0FaPlToapzZvmsaPhk= )
+ 86400 DNAME sub.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ bAcOYw3hxMzXkRwMEzJ/ABMEEdNmg/LDozxR
+ 3zElZoY65UP/wkZuCeP1pIvguEc8iGZWeg2x
+ b08B6BjFfvIg1A57v/FAoOXFpAPpw4o4pa9e
+ XbyA/BKuM2jRvkj2EBFV5rf2M5pTT1GF1ttJ
+ DdVxM2zb4XfI/WVJURIlb+6ucug= )
+ 3600 NSEC test.d0.example.com. A DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ uexyxqwdJTN2si0SJjP0Rk96XS29y1jJauEn
+ /xcPSP1oJJapU6L5SzFu7Z8orT8CB4ALw91Z
+ YdPQm2epXRYomyj3Kq5R5zXGjwS6gIv3B+1J
+ mp+zHclVRsYY7QLDQAyOHkK+NA74seHPgD0b
+ YefqdzOI3QOSkQJNvUqTISAriaU= )
+test.non-terminal.test.d0.example.com. 86400 IN A 192.168.1.2
+ 86400 RRSIG A 5 6 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ QYOdM+npbMtsFSB+JeoR5JXk29yxjAcq6IoX
+ sqGKkKkyToC98o50/XH8bAxFj0q15r0TvuEO
+ e1G7dIJM00fF+/fBYIOJcXJ47Qu7xASzBYA7
+ chF3hB94cvNV7kABKFNA5E1+DFSwFIfFmClK
+ JLe2E5+SGwZfQFaSb2b6tjJaMj4= )
+ 3600 NSEC d1.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 6 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ EJQmAI7vp33hdzMgolypa9+qbqyxIspqIwYH
+ iCgs0TFxPQpXc1lY9SMhZX+8VoSZEArwgTsf
+ Mx0UkrlMC6sXi7A79Rdwmd3r4BrK4bdMrBKu
+ IrVLv7MCRUzUkHdVHYAbi2+3IA2l+/6En6Jm
+ msRMZCOJNR72sLqs60bURgXI8CM= )
+d1.example.com. 86400 IN A 192.168.1.5
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ sBA6L5PbxANfhF5YNHD8QHVPrjQGaymqnXlA
+ nIEN3w4wWf27y/+slfvyGDg0dL07+NkmJnMM
+ 7GXYljy7m+i5TDVzrVjvUyw5r6ML+/jXD6cF
+ DhUcozPWBPn/tGHQJm8W5ZYW7yyzeaBQZwT7
+ IJkYZ0bINEQ05apv2gd4GHnYmak= )
+ 86400 DNAME sub2.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ M6Dy+pDGgna7f8jB+DbPEz203TGCYYZijbYw
+ TlTw62kUbRUPrJbL/fQkE0rU9Pc3GJEtX4Yl
+ nhye2+iPZIelnQblQcF9Zcg8mCWx/P2HT8UH
+ B2xDuH7HN0yuvHSXWVebEdlR4xhGAyXSCRkP
+ rM4giGRyMaKuwl85WFdrDeyL/tU= )
+ 3600 NSEC test.non-terminal.d1.example.com. A DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ R6E/AnwSdFbKYMf0khvZ5GlmWhtzbkTkoirv
+ 0whLSIhHvM24jUt3ZbtcIn6xi+hcnBs0MSO0
+ wnsoJhGvM37cJH/sdYrwkC/W9jajToJuydhG
+ fe7jwv0jw3WcyeG2BCoktpx2ooaFciCxrU2T
+ 2ncR/dKM29LQzVULPBP4rgcXtkk= )
+test.non-terminal.d1.example.com. 86400 IN A 192.168.1.5
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ EITEKYUvoytFHIeie+a+hKTi4T24/CCMV65M
+ qG52AKrHK9x+LQ79FMFcSAmMgh3z2DDQ9dzy
+ jy8KxUVuC67AsXNcoGEehtup1GEmTAMV6BlD
+ vAYdgupQjuhcgZRix2HXDkxpW+fy4wqjbJz/
+ 3KeUWWIglCLBipPPjNwckD7zmvE= )
+ 86400 DNAME sub2.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Ati6eUZSBzXnqy+TQPkqhqdMngsYJSZo5vxx
+ JSMieiy5+vSptViQanXzkEGJeYMYg90TcpBm
+ fUGj/p5vvK5OtNZvawM1sXR3mLBBRSibUO5D
+ RhmzNWuJ2vGCZn7/CEDZ9X75Yq4cbg7A3+AU
+ g6RATYnQ2WptsSukGV/LBL/qBQQ= )
+ 3600 NSEC test.d1.example.com. A DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ q5n4L+3en23tfTmj4ZmQvBq1KTB4KL//Ipjl
+ IrIHk71WV1jjnVHFzr9/baas33nDMgnCsAWi
+ x+wmB9tPRfHqdJrbW0f4LGBM6cTvc574RYTK
+ 36CWvPmTy2GwaY5BTZEzqililQjOJWpzGJG1
+ jaWb26cNeZdOGmTviCsbMkk4d8s= )
+test.non-terminal.cname-point-to-multiple-type-a-record.example.com. 86400 IN CNAME multiple-type-a-record.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ s2YVJouV6wX0PRwEl2QHeHk6nFLNxNhdrxgC
+ HcfpI2RHamYHK0eaZfiuEUfXMRcht9Lds04Q
+ qpv03hEl95MxRhH8PqRuXBDf5QY8Sym+WEL3
+ /g+UgoSSpJZ3FAdFCa4iOx4x9Ms6WH9E9n1h
+ KMr7L5AhXVMexoY5a3VEhmGzPx0= )
+ 3600 NSEC d0.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ UAxKCgnQ4DK144UA7qjlFZJsqbbZCPEa1YRE
+ 5rdt2j1oxYstEFrfYIJlm0SDqQ3MZxSnwsQW
+ oW51G65Asb15F2lhYeEf4b+haXDa+dYW7xPL
+ S38QEKfPlUjb5Tp+Bzv5TT1phE0nalPWgz/7
+ FH3beyLu+Evk7YxyL0qJHf+F04w= )
+d10.example.com. 86400 IN DNAME d11.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ N8yrn1MJ0KvV68ZB8FQIYFbmH4WQgRqjuolh
+ QU5PGbdNHPHftS9OtqIRmJ67TtX353V2HYrk
+ j11gfQDOJhXAPkmpbcocNE99270xPJlNgvoX
+ 85v6T6K7MT4k+AlKwNPdiutjHlrJnVqr/lvy
+ NszBTro5qZuKvt90xguql5trIH8= )
+ 3600 NSEC test.non-terminal.d10.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ xU/8mB/Wj7cpidggEOGleDIolVYXx4pLjUhR
+ HM4AV8TtH/A4/mlxPbdOP6ZQ58O6yt5ax7VX
+ LxYVf466byp/zbBxgQFiXoCYfGmSFR5szdxw
+ 6REV1VMQlQEiERXJrr8OkNhf3mF75TufD5Oy
+ VqLwVXCrNlDk8wNhs4KbiQa1LVQ= )
+d0.example.com. 86400 IN A 192.168.1.4
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ oQ88aQZo4/NrYU3CLYAs0dZ/nky/sab5qMMI
+ QhlAQ1Cke24EhXg1qDKqDs2VxmlXU4G737Pg
+ i16mMrZh4pJpTcnbk48MKJxN750OsLjuPseu
+ mzsjUuJF3qRxnCxgVzJeM6BkLjN3ws9Nt2KG
+ YppKHvg/vAdm6AcKMhS/LKVWvJY= )
+ 86400 DNAME sub.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ VUYk+8Cc9GVU6urdV9NQ8sO7J2LHdEhN5QP8
+ k1TCiibxXulrhn2EDKhoEqfYHJkChZovlNDH
+ RlfNoiYvXCjpm8En/5G2hmpL+DdSbUKJO0fC
+ wOgzfoS3u3AUCgUBVTA8U5OBPHzoctp7/J6q
+ jhRxjVhz02mLNXh43dELM9dQeQA= )
+ 3600 NSEC test.non-terminal.d0.example.com. A DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ kY8OOpMJfUPQ6rfHeOhtE5DNKqXDKrXbIcXc
+ E8NA/M3c9Wpw00d/VEzSqPP1+mNXX9fJoaOe
+ ukWZjsCkv5QJMBTjYQVt1UzQmDbVt4Un/nJW
+ eA39f/ZXzUm04xVm+EOhSN8SV9It7w1RMX94
+ t9S8xI87nZi9+SeI/+49jeb6j78= )
+test.non-terminal.d10.example.com. 86400 IN DNAME d11.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ maQTzVPNfa+5C9Rsp3VWjNuQ4FuRjJ9SnIDj
+ xWv4KeMekO1qZBhgwME7OwViVlTpbIZje5lG
+ o/NoHwb8GhQLn3tsh1VpnAuD7hifsnQX1tyv
+ LVYee2VZ4H0N5N+qtfdoZvmuAghbxDOybLJX
+ eZZddeEx+vDMjK0rNiEtn/P5Os8= )
+ 3600 NSEC d11.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ oSGx7H72TvxwlepMMJeG6Dhy+QERBE+6iDDU
+ ufYYmwVT5QbTW3cETV2ZEmhztVSXU+8QnyfQ
+ dnSWeCPSBtE8wy9F11ID8JL19H6GhsIM51Hd
+ yYwphYDjakzemv6AZIpH67p6O/FOnLIDwJFs
+ /N3uOriCS15WJE1XjcxSbJwHrZw= )
+d11.example.com. 86400 IN DNAME d12.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ pQcnUhuzKnyV1dz/VU8TfsAZ+nPhbqVrPco2
+ MTbDaPSLCvZS0ulKr9Xd+7b7QFtTxNiX9Sm6
+ fMzVImTeJs9B3f52yIww83fnHMBNMC4kGgaQ
+ HuiNuuarYYx/r1fEXl32lo641DimcwzkdvXI
+ hJdRHFMLc4cc7G9mur7Jz6E/2u0= )
+ 3600 NSEC test.non-terminal.d11.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ sFRbkTOCCwr4q6uaRJgXv/CuFG4soo2KX3ry
+ KhannLCoaIj3ubwBlCODhEBfEVLvfkGUjoPs
+ 6DsXaJZAgtKuienXGHF+N6xruqAivCAuerF8
+ UK1pI9WKRaGMop9ZhzUtPdTSYho+2b9oCVQ8
+ fJ5zJuCTWCOJUl1pVQf3cYpCIJs= )
+test.non-terminal.d11.example.com. 86400 IN DNAME d12.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ZCsTTfwiwj0ZEbYFvRLUFrCbV3Cf4/vv4Cm3
+ ruCXQgW3FNwbBTors3SmPdGj/iZt7XJCezKU
+ IojMdcKvD2mYZz9GxcA1wEGa9H5bDT888Oyj
+ 9Lg7PNdx2zF+13t6Sx8VqS3c7A5cStnxQVXb
+ 9lqDxeGEI2cDsqc1lb5ZgqxkpoM= )
+ 3600 NSEC d12.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ OJG1fogGzZ1j2y/5n8dIwl0YmcPXDLN02lqe
+ b3/YkF+CXHhbCKxhNT1YWM0q1Ex97SMSsKm+
+ HtgO9Lt3bIxMTkz0NyffspS7YkQYh7LBS2E8
+ KhwEXgX1G/FPeC4F5d+7kPdoIWaztLtE6Lr3
+ UlbN0LlRTaPC7fey9X6n3K+8DPA= )
+d12.example.com. 86400 IN DNAME d13.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ lz7yCcwp7T5q+EEuvANyZGkTu3aksTx4xPz1
+ DabQhnaJTagbLobu00sKMsCEHEQ/m6HNXJ2c
+ Rak4p2JS6T2GIOyDSurRiWbiMmwHEb/wuOOX
+ Q1JKM8zwrSzkZ43sp6OStotN6NpTSRFOxTyS
+ cWGl/O7YP1JTms1SmvEui9oY1TY= )
+ 3600 NSEC test.non-terminal.d12.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ XF2aBu99ZxNwTtBXg2vlhcYJgo1p5JU03JvW
+ iCnjEVUt3W0Tveh0qvjuM18y6bK2PMbmSW/i
+ aiJWf6A+f/LkcmrkIFWSOOO5OMISy1ew1XtI
+ zHQOvVQQN20q0zrVpJPUjheYoTyBDXG0NAt3
+ JVxWsrxdNwuX0rfk6IAvlZQyGEA= )
+test.non-terminal.d12.example.com. 86400 IN DNAME d13.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ RVwECJFNIUFt+LUau5wq7zH5I8wESLQOj4mx
+ QRAunw5SVIr/KNyyFA8QAtKn+H0gefywZyjk
+ PyYmDKIbIPu8hnBBcrkPOztKUzvIzV/MParQ
+ DnlLlXDw2JQVE6HE97MPw7mPtnb8Bf+4ti0L
+ QKDQ7dhU5A2TsW5VMlgZAOWKQdg= )
+ 3600 NSEC d13.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ XOx2kjlmwt/F7sNwR70hKcWNOuxLBO+sG6Ec
+ fQoZy5dq+U7DUk5opqHBnTgLTpElu5xpt4yB
+ UdLrZ6Yw9RgYWyualSjRl8KlarQfb2G+UzoC
+ K7yj35zj3zVq39dz7EMB5J7JxmMFIsNjHdsY
+ RWA+UVOZFvDkG8rUpWrUs5yKVnU= )
+d13.example.com. 86400 IN DNAME d14.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ jtsqmXsjCvfCRTfoUM+slQHIuAUNWawsExt0
+ kM5CcGfN1pZ+ZZtDPbU+JEp3A0WrVyRhXghu
+ QYHm0aRxJgeKoqyOuw6IEpzE5BuPj+VEJopp
+ 1Q8ie+Bcl+NBr4RvWhje4v7df4WQ1ViqeUey
+ +DT/n5l5BCLR9E1mz5OhQaobHSM= )
+ 3600 NSEC test.non-terminal.d13.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ vugIOlfbCi3BSPxGVKivU3W/lxneaodwLxFU
+ qI6MPkrerZSa1keH9RQpomM/pTbttE2iYeCg
+ yna0JeheXgZDUDMb/Ck57IVFUy4HyJzWOC2m
+ ZoPq7TktRPJLcuKD7iYTQKNO2FnWztQT4j4z
+ WpqisRrvcnPAmPqWYKYqxOVKW2A= )
+test.d1.example.com. 86400 IN A 192.168.1.3
+ 86400 RRSIG A 5 4 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ NpI0Q1mX4EZwg5pWP9qVCEYFUbmJ/PUoTIC0
+ mnkw1qvsJZnSQScZNogcC1HSQ6fcLQEWmwWP
+ 992edBYI8rCHku0jFm5FG2C7rro0eZFYGs8A
+ irix6jJO2YQfyxlWjl+WW1T8GSvB1NctlNf3
+ Zbpg590SaPcTOx/3B862E99rjJU= )
+ 3600 NSEC test.non-terminal.test.d1.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 4 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ F6m1X0oeLpcz8Oksun80lQQn/frSOkK6isrl
+ q7u/lO+JaLEhmpRPcFVz0zjO0yX6gVFG2pOA
+ /UuqiIPpGsUui2uDPPPiVFF1kasI0tS4jBeO
+ 50QfJX08DjOyHAxxMcZJSrInFBFfW+Yp0jL0
+ S3/yPXpz4ziNdJpP5C4u/tgKoM0= )
+test.non-terminal.test.d1.example.com. 86400 IN A 192.168.1.3
+ 86400 RRSIG A 5 6 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ KMVn/uUWxGkIRnzcsfNadUu2Xirgh2p1Zjm6
+ TSyGtOUTf1Y5JImsHOmlJbbg1JH1bzBVOvGi
+ P31QZAKn7XdkZQVcnMfEzK/KjYhinYeWCAxY
+ 2UrQrsdakduooGDRKcYFNrs6ZWazwLA/QwnQ
+ CAp18msA0xlYvwWwVAnKi+54Kt8= )
+ 3600 NSEC d10.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 6 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Ka9/TNblKzcJYDkV90jalAvXZcNDQbnr1aXZ
+ 1yeHGNtBFvO+RAJgq1nE0BV5tT8BVD1KC7iQ
+ mR2ejl7i+06aJS16sn60kwT9fRr9/Mkk7WRU
+ k375Xej29lM3+JgWTyWcP1iHVhzYOpuPomsd
+ N/HORL0HS5ymMvXlk0UetLQQQQQ= )
+test.non-terminal.d14.example.com. 86400 IN DNAME d15.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ QH/eV7byBiJKnouQxYqhFJz1DNCS4Uy8kvDV
+ 9zohKkVlqxEq3HzE2RdG+2DpqJPJhpkMdhnb
+ wWmxsquHv0P4d9HhS3AIrNiXiP2fujh9sIiN
+ 1ySkWFsldgsF2QupkGbeQtKLVU3cf+JrMnOI
+ NjCMBG93uXw2sZeknPszq1s5OlA= )
+ 3600 NSEC d15.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ V5n+CIgwa04IVSIwa9gPekLzJp7rECoPWx8Q
+ ia3S8ZghzLAKT99vVcbvUdckbB/Qsh4o9jyV
+ pFb7sTzALBoOc1CqDx32anol3qIVNel3LJ+w
+ 2kGoAY5XEWC5TYr7O3P0IFsAP6miLaEjX26H
+ bmHaNF7omRtFmL7RigRLJQiNiQE= )
+d15.example.com. 86400 IN DNAME d16.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ CyLqrwZuL5yPAsxRIwqXk6jsmMIhgVNOqpNi
+ iuEeYjSSG+gFFBxeC34dX9/6kn6pla9fEa5q
+ +dxcKGqBbI6W2EuTLkvX/Z77JJBVD0Om0qmH
+ U906M9R5GZdmT/2VpYgjR6Tw5jGsxlczMhQI
+ 6zAhR/M2/TFgKex+rGMMBRDH0Mo= )
+ 3600 NSEC test.non-terminal.d15.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ MOJen55j8/qZFSNur74yJOZvkz3ECUDyRTiY
+ y9NgRCcMpweHzVk4WyA1+QyqpApakV+Lks4c
+ gJlwCDg3Zbi+9dDiSq1B8wmvTXlpHPDOO0rw
+ hK4o6Dlyc40UDddv6YJNF0jmsX1F6jcGHn1g
+ hyl+xn2IkeqBk3hIph9quhF18+A= )
+test.non-terminal.d15.example.com. 86400 IN DNAME d16.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ i7j+ye/1m4ZWC+1RYOcBENaSp06JadRLkagM
+ EUIws2vhB9SqtfjDog/qalHm8dVZwfFhzKrQ
+ NSo+A1Ft3sC1NfilIvi0yYqwsT5j1WRpeV0Q
+ T7Qe1SBL7DcDOIoVsyKo7QDabLJbF060e0Wg
+ Emfzm/J46/jDtHhOm/KFpsFZJJ8= )
+ 3600 NSEC d16.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ UHG6zpseu3ud73F/HWeU+MQPVpBgQp2z/hO5
+ HcqM56Af9CXBN2c9s/WuEtqAi4KMOxM9YriW
+ RAembvuikFRV6JQ0H+ftCoI2waF4Zu1kLcwU
+ Y5NA/xS1xLgKSdrO6JgA8RlwcOVENt33tG9m
+ UVkV3XyI8Ppxjc7tneTJhEoiGo4= )
+d16.example.com. 86400 IN DNAME d17.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Mrs5QfcEbOCIuJPVSlErmXgH628g9VuPHugW
+ TQRp9yB9oWO710r0mQzagWfK/9VFA5cPnoNY
+ MxrBWAN3a6O0Qwo2BBUmPgxUsWUJ4sITCDt6
+ ystNF2BsYqMv0h7tYnbLMM+AfuojoHY1Z8TA
+ AISB2lEn0xa8HI6X3CXjrNIJxco= )
+ 3600 NSEC test.non-terminal.d16.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Hbdv3SdJolh0xmSKWSLdqZRzo7V2nWAL7ykn
+ 5RAWJveZvpeZNLAafdePTqygEfUb4FrM2dqr
+ jm1P1OY9s3ECWUngYn6+2Twn1hS1IiWs1TkQ
+ 0cf8egUDmrQ3jkNU213GjRaj2/ir6EL/Y66V
+ 7sI6DXH/pVIY3JBfgGXbferkn5U= )
+test.non-terminal.d16.example.com. 86400 IN DNAME d17.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ P3L9ijIF7sdq4qbsFz2+wshgtrnL1Xj1CgxT
+ sOv24t8YBfUSJz9udgg71rqEJcmYHsdRxyh3
+ m6A3pCvWLW8CSMfedp4AAner+2WFYZ1CUJlm
+ XdCNDnOVQNKagEur4yUjLLZRjuTtD5JRkVp6
+ qu4EM5u/4FR8TGPg6orpC5wgkJY= )
+ 3600 NSEC d17.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ lsG2m7m5cPvmyqOcMiUKRsoDwnLs92Lz4lbR
+ 41lsyaKxk++AlegRyeaHW40e+yIj0Nk20olb
+ LzJopJfTgLsjUOf7ClMoQkXz8+yCsDWqbmlj
+ ifM1EODugwoxIq4K+N2zQ4tGMvNLxUvwqMPw
+ j+q669F0vkZPezOPuVWS+kTSrP0= )
+d17.example.com. 86400 IN DNAME d18.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ BaabmIcyWNJcSmaDlM7/HF7u1ZgyPVxwJCTv
+ V2L8KN5YVQaNwiVJG4zyoe0mZ20568eMxIVr
+ 4KRMVdv/NXhhKJDy3pVaWdqViSftBpudIooZ
+ TBy9SgIe2FU4nU8KbS0qAjRzrfJ9vNktKSTm
+ ZeXCxNd5HD/6//F4T+DUkS0usUc= )
+ 3600 NSEC test.non-terminal.d17.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ dUzhCcQvrhwUt06MySWxzwGK5sAmLhR0XKHk
+ XQotzD7u3kRwmSbiCH8BL9cynW0kiTZbdOSC
+ RToEgEPRFLiwrOYPxWVLzgcXuoXZxvHgnFRh
+ 8TaEispFkSFRqCCPqCSetKuV5S1IohCDGqLq
+ yXJ0Vi7H+8dKchGgnDSjJKRSWWI= )
+test.non-terminal.d17.example.com. 86400 IN DNAME d18.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ CJNF09vYxJ0UbkjJsd9P04a8Jpu7/kSKLOG3
+ 67ftUASZoalm1uGIvJMjcXidBUod4XXT0ljh
+ LTuezFj2kKbZw1YKcqDNv+Hy86Zhi07v4FnC
+ 75n5Rm2IgyfunvaBV4VnckiDRduFbSKJQaqx
+ GhUBQFzdApDuvaUcrdw41aLFpuQ= )
+ 3600 NSEC d18.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ KJFyF3FBlv5nkj53t8xoUKf5P6aWFQmKgqxW
+ fts6nY8fjbmAvOaf4iPwgx3VrnQqAiuOBDcL
+ oiLnY6YN7mnn02Hs8YLH8qVUB+BaPnRGaFZM
+ /8BY0ssr+oWihGlgZpXJjuNgC6kWNEbbrI11
+ 6V+pImV4fBODjh1TM0wrnjSvm2M= )
+d18.example.com. 86400 IN DNAME d19.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ aF6mM/YQOWcrjMTe7MBaSDmuO7/6qFwHAUkn
+ hooppu+GWnuYr0O2tKvHJ+01/Nq3GbVPddno
+ Qcg6zYk6Zy8cfzaifo/b3HbY7/sHKBZjhW4H
+ CkqApNcBu5bj1DvcaNudKKzu6EEfN9JzDXZt
+ sfSkTW5rSneiz065dhGrbm+LD+Q= )
+ 3600 NSEC test.non-terminal.d18.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ oJgKUcyZVatFOgKIu/jq/2nazUHSk9yYiEWj
+ mCh6J/Qvy8REFqWrpkeGu0jEw+Pclaal8QKY
+ XFaMCXHogWucpwulzuFDS9r0VMHxIjPdUAoL
+ 8M7oxBV3cjQogG29tay8L67XCEnQ1jhNW8t1
+ 1oJ/VgTvsCZbilvCtRYEjKOcLqY= )
+test.non-terminal.d13.example.com. 86400 IN DNAME d14.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ neQfJMo/qebAeLx42Lb1u7u3EBOnlumBB3EM
+ uy+6lk8Xvxvj9mHmcGhD8E6VNhJz20fG2Cdj
+ pCctrOFr+Bt129aNALiYFvy7SllM9t/i2Fca
+ OH7UnTX3ZGotmAlsEDOevZwrC6Q78KCB4Glo
+ CHMBE8r1LkHZML5zIJJ60LKqVpo= )
+ 3600 NSEC d14.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ txarW5KbElkJuR0U583IVm3vNRUK6U8PP2oH
+ fAJ0f0gEJ8QrRnc5xOpFLnT8bCzZKpvpTCAG
+ gVEqLzO9scIajvMXM/MrWNFI9D7V3KTSHAmR
+ FlhqpJnN2agXMduEGxqBowKpGxFqFjISPg+5
+ AiPlg3drmWqO+0KZW6LAR83nj40= )
+d14.example.com. 86400 IN DNAME d15.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ QIprPxslxv/sZo3JS/sykg/RS8z9ZP7F3b9L
+ YBJBfTQ4vLaqnfUY6EA7cf1vvL8eigHJNXBw
+ MZGsp5S8O9dgUhc/TtBkiWXn/Q14/Wdy1l5b
+ gZKYTOcGhD+tlTgUooyNU19APMX3vEDFp345
+ JQyVw3zi96zX/Mw6dIzBcVPlKkw= )
+ 3600 NSEC test.non-terminal.d14.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ pP2JMjQ5Zgc2AWXkTlN6qCVl1SXfgIk04e0a
+ IEsqJyEIvVZdzPCskvOc2GHvaDJ4pLS6mTQ9
+ V4OWdkDC/jNesmdsY9MFbvArp31pHng/sYv4
+ RSg+cC1ZjhXxv9HZcQ+SjYiDZNpo+ASokpR6
+ XBXiZZKAZJFH+GE/HtjRIr67K5I= )
+test.non-terminal.d19.example.com. 86400 IN DNAME d20.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ D8CYk/S6rFMD1jFoTxBWIj2HQsyvFas+MgR0
+ gHGMjv7qgJizcay8CD4Pa2bBv4r1pGeFzL0o
+ OE8L6nrbGdklEDilJ1vnMZLSadybVFx+rfjR
+ 35g3oJhF9pxx6Q9mSKA2XGKMj37J/+VSlwNF
+ ktW8QfN9xw0htvcQ/ovy59L6YQw= )
+ 3600 NSEC d2.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ yhfqS6E1PTtvR+6uxLuce1dAASE+WIBXIxqb
+ Qqu1gdor2LJZdhNo2oYADJNOpaO9ZUNURB0x
+ UE0UdDaGF2EZlaA5chh1BzNkHhPvx5vfUHQy
+ 7+jupjFFDmjeq/R2m2VKD/hzstFElmOgrRND
+ wIcZSTehIOEZp1m02Fyq+NCayvo= )
+d2.example.com. 86400 IN DNAME d3.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ jGJ0gTTY1xQ0jKTXs9h/9NQYZufrY2wJj6+N
+ CZhLQFAIOETNBrgLiglcBde+4b8YMTFIb1vv
+ jX8dOZWQdDdmzdkHTOrp9zmFQ01E/hOMvDuQ
+ xrQzRDWer9ekzyvedkX1KOlWnoegs79t4KSr
+ ZILTZDUJXhBvn5T55DNHJq5WYN4= )
+ 3600 NSEC test.non-terminal.d2.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ lo4Vm2n0t1q07yfsHscXC2XwBdqzsNTJqy0F
+ OEG6oXDCujR+q1DwFyWwZ2iK2FjFfrmpus6o
+ wE8PkxWCJlAhlGandab77MwIAD0Qv1z2fgjd
+ eMKhN2tqdHIGScr8/NszhC5Tlz939iB3FQyL
+ BTQecjH3bb3mE/GjASjKKP81AIw= )
+test.non-terminal.d2.example.com. 86400 IN DNAME d3.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ FiOhlWNxdC2WCLplysnKFSXko84vNOUS0bDp
+ LX+d3oBHBN/+KKyiIwWJdwfFQ5ZhThsOQwGD
+ eTU3jdXddMU3L3B/XgPDeFAYjocyzKc5LI1t
+ Yjga70MUEcO6BUojB2QG/TcNDtdVEYiv7l5b
+ d/P/YefpjKGyqGtuyqV3RBNqPNI= )
+ 3600 NSEC d20.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ pd4iRs4YDj/nR7rcJxeRUOFwHcbXDwA8IZ/X
+ WhO7W6M+6wfYUU6VW8dAF+rUqHcUd5+p2l+M
+ LT9hjUxqI/nvufcAtOO0Wd1JGvKlI70s9vR6
+ /VAbIg20abuK9tWdjy8pyh3kY1Tkdn2Ah/7d
+ yG5O86DDTRrVsz5SGTecj4w/RE8= )
+d20.example.com. 86400 IN DNAME d21.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ CeRBWcoTPJayf7tSPHYoHafVkFfDaI3z7XID
+ /W4f0f0LWluOdljl2k/9bXk8OY4ajv1Pc5Y5
+ IPAQ6XMUhVYxWe5o0U3NhCP3QZoissiN1a+z
+ GUCDCREsn/OvuXdvN9tIfJmbqnb7SC6fDTZh
+ aEZ9iqRVNk/N5yrxWHFMFLyXuSg= )
+ 3600 NSEC test.non-terminal.d20.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ ntRzsAX8hoSRRTd1eXlDeVbuWjH4xw/BKx5K
+ Gz0JtcvbOkf3cmq6RjJs7lsEG0J+Noptbawh
+ vW2yj/nLWantQQF5b5t36LWrfsTLLPOZypQE
+ m+dpzV/GpJURrwgDn0uxm7Y+68RRildmzSqz
+ C7UDx1p2PswGGIXh3Dm/2fVrWiw= )
+test.non-terminal.d20.example.com. 86400 IN DNAME d21.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ kV6a/gXbO0vLbS02YAs8+hf+NKblK1v2INBx
+ JhLbJi0sM66LxnoJIke4910DrKXQtojP2eqN
+ 1KP9dt8ND9iWmGhhJMYqfQMJRDzzsl8yocbw
+ 5sPHv+ZkZb5YhNal1LRJtXBZrIF73CIr//nA
+ UBZdn4SJBp/IW5aEuUeboGpBLJc= )
+ 3600 NSEC d21.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Lu7Wt0xdCaEyXHI0fZWf+ePvmw+aop6NPVoW
+ xjuf/YBLqJsUoIFYJAiFtuBPHbdhIxRHVvN0
+ oq2qdV77lw8jbHLEgnoLdlP9DX88cTioJfs0
+ vuLmYhe7ncex9jRttJWPyhF3PwMuIkNILeU8
+ f26nDo8nlLay3ikFLqZGb0Mgip0= )
+d21.example.com. 86400 IN DNAME d22.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ pqtitqFpSeaB07zL7eZ9RQVVW4VOzlFNyZjL
+ cE2KtU5mcTWUhr1zukR5GeHbK5GyoLQwnPyU
+ cFJBLt6XfTnXZ70kZ1w+YfFl/+skBgPRDY2C
+ 9Qc2knigQo7noQlYIWjamzCdp9zGf1Q+jb7+
+ iWq7+qLElbcgUrb4cwX/g7i5xMo= )
+ 3600 NSEC test.non-terminal.d21.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Z8+2YtdqGek0KWucd+dFVa1D9IEyFdTOtijr
+ 1erfz9TNnNJqT7oMmI2EWVMOLNfkRTzghUGG
+ CwXTZzHrRlKs5Y2ImUaTqJ6pZY6AjOKmBwQF
+ uPJK3Eal+mrWwOfKwPZy99z+1JwKrPP4GfGC
+ 30CoJAfpU++EfGB+Yi8CaeN/IkI= )
+test.non-terminal.d21.example.com. 86400 IN DNAME d22.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ NouUFKVMDc2EFJzTKaStH7y2SK5idF5XhVqp
+ jsp0A1ArQJ/OumvCHP/EDuZh3nazfjlRmGWe
+ xXPtjKVQ9mo6MPEs3C4y5H/hkyfHr0SLBJPh
+ ca5Lu2w5YssTH0ycqVcR7vaE1FITxF1J1m+O
+ WUNgzJ8C1Iaa9NvBuxIJTxVJb2E= )
+ 3600 NSEC d22.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ C8sQzFd+I4yQcCjiYQ/Ml1WL4g24qN95wuJu
+ 14KNhLoDIaJdlrAOgIACRT9RgSWTM69eeFVk
+ iYn4L7t55gj9eBwAqHY2bWWVqkQ4WkAmD596
+ B1zLhRWFL0c0RNkLgWpHuxkPg2qugWCSHdjU
+ nbXhNKfhAWYYE7PmZC1r6cLasHg= )
+test.non-terminal.d18.example.com. 86400 IN DNAME d19.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Euf38MjyhFOhK4YGImOfcfXajRNocQr+04zT
+ gjI5O4lzb+tYJKL5nYSgXLEZ0KdNCttabkT/
+ JEa7Sgfc3B7Ggi6f/dt1VbsR4eYcX0qd2Mol
+ lUNaa2kCdpkFcWPtvhTuHiFcsxacZumk93zM
+ CFkI5H9jbF42E/ngB1kRDOiV51k= )
+ 3600 NSEC d19.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ ao23NWDq68jedx4get+cBEPZlYVNb8Kf7Zer
+ GcrGth44a34Tp9gOUJeMlhmp6UzmMIo4BK0/
+ 0rf3FuLsReUfUGQjEJKY8FTgsHpM2sKhV8QE
+ gPvlLlzmCrIz+P9HqPsEhnCDgCBM/Dz5iZzQ
+ h5ScTjYMSuTpdf1fTHrFBvjGVMc= )
+d19.example.com. 86400 IN DNAME d20.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ N0HT/PTQBnVNU4A9eBQUTyumrav6Ri+C8cy4
+ tgL4Uw1wcll/H3ruCsDqERZN4RA8xKCl/C0I
+ DjAchaz6BeFUZRp7ETgWJls/qmsH62LCnIhO
+ pE4mW39A3J1tB4a+3C0ZnbpaQxU51PhJanPt
+ xWDDw/CoyWfwOhL7dMtk7cULpuI= )
+ 3600 NSEC test.non-terminal.d19.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ tseJRrkaCddU99/+haOo7Y5v6pBj9+H8kzIo
+ 9WR3n3EkyA9fuRVVad2Iv4OIkuJugeCFAOb8
+ otWAH8LSAMc4cGZ4qndAtGz+/Bo9rgTShAvk
+ q7ClqBDzQd+G1rmMp+VdiWJyWC28/oaE+Xhx
+ zx9bHoILa0Js+eUKGxC/QrAb1VA= )
+d23.example.com. 86400 IN DNAME d24.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ JQgGS3/ep1C33sdFynMolV09rQiB1bvIU6JY
+ DDd4zfdg6gARv3Yldzz5imAtZDuPsLqMOC3T
+ X0VsQDoqtPZ+rxMtF5U6KhYcyAhzk8o0XhaZ
+ nkyky/jPrh8/A+ZDqg4uDyTZ1yQjWHzQNtKX
+ eVyrdovSoorPhxIChMv8C2JzjYI= )
+ 3600 NSEC test.non-terminal.d23.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ R+6R821usGq5plO1LRKnAhr3TVPG1xVOKpJF
+ RahtcPiOgoSt/FbnuW97VEjmZybVzOq+ndV6
+ 3mJE5Q+/eIHw2WegGP/t+gA4/9q+GEyI8MCK
+ nRtoRN/hRzKkx0Vu2S+0xUcJ706S2rHNgUfx
+ QAR+ZQI9ziD1wKwmqGrezpTIxKw= )
+test.non-terminal.d23.example.com. 86400 IN DNAME d24.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ bcaFCvHpwUr9kCxPk0IPISNyyoOsut3IU6K0
+ fvxJANFVZSC5v1qhw8xI4wXKwIfgDseuvV0A
+ nVkS51MhOBufduGzrDEEJdJo828vslVqfZq4
+ M1cONAp5dTwHnd0IBB0xpiPKy7X/eVgpu+TU
+ htZa7vHOtSbPsg93PzBUDxu4Yr8= )
+ 3600 NSEC d24.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ SZjgbOAcBDMqbl98hQioFaBdclLDvPJbY19h
+ 0LapWiC+Ul2hk7Df4606+VV65EQhng/emr6W
+ PmG+wejcalWnfldCruIZ8Qp7HK3YobtpkxRP
+ FZcq1T4ye5kTSay1FMV5exqMnYKmKT4heZKC
+ 034OjugQbpwBt4bC7FngmlliVm8= )
+d24.example.com. 86400 IN DNAME d25.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ QKTiVsUl1e6CXgJ+dwuTnvLZiw0VVmQwe4cU
+ JDXLtdUepOYadkKrzds6sf5ryozOrImuKear
+ c8HY3PwCv4wh0OPpwm1moTTlj6qhNsnJZn3b
+ n/INtiSe1Um/LzOrdSVSDJDoX1LU9xivbQk0
+ O1gfIylB5E1s73QgrOag0PX67Vo= )
+ 3600 NSEC test.non-terminal.d24.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ f1FbCR1voPyT2gOIOXlWXh726ghR2WgHux7N
+ bT+EqTAdd1luHnclhU3Uvl2/00k78JBjwYID
+ CM3q0IHHN4A1A2OSEQCcPVJEHb0bXzCsZS/Q
+ fyNprb59q0aJQ8mUtO6UpvRbJu5oXcgDXbSQ
+ pRJhrM5NyUe0UsPkG5pSRC7byqk= )
+test.non-terminal.d24.example.com. 86400 IN DNAME d25.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ AvgJxGCFXcJPQKBfo+wgLP9UDEKafiG4SmXp
+ gZrXhomFJVwe7MSlogv4p/sH4NoBFjywB+gD
+ pTZfpT1WeMh4t2mgp9OeTyo+OKWocnwtee1s
+ tadIlU4d2MO2d0Eab95JkvacYUFNnpkQ8V7q
+ ChOUt6nHYrfveEz/j57dSsuGCho= )
+ 3600 NSEC d25.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ SLYSGumPiZQGIm7sZWyL96JheM79UcNL8Jby
+ 8Cjtfanxj15Zz8jKnDUyWzeJIHGd6uKjXi/K
+ il7LLGm4hxknlWLM63WG9argPYHDkSDYlI8l
+ ov1fzhrmFW04P8tHgebaD+Om20QALnUVEda0
+ MITe4fWITnel5NsdHDMPLEGZbkE= )
+d25.example.com. 86400 IN DNAME d26.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Wi0oXidrnrmUW8YRMi8lY2ldrW8fkO5OYjNh
+ UNefjH2hua7EesiT44ddHepmYCSTpXlOE6bq
+ JV5kjNZjnsmz3bp1mypx4AAqK8j1t5y0XTdQ
+ o/uMsCeqIGZdiDE/3GZXBfZUUDv90Qx7VzVA
+ n89hAFyTNOx75vuBBY5XqDgkoVY= )
+ 3600 NSEC test.non-terminal.d25.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ kyLYfp2vu7r/VJ4r1KUy9fM1naHRfzz4u7tK
+ A/FmiEahM0ta/W94Rgw5N1FjTetvLY+y0hGH
+ cqctmOofwY+2p9GN3PAnDNIp3LPr1YIGzHa3
+ k/KhbtSgUuIl0ZfcYEBfMSNg1IEEl0sywkQT
+ UuP5mKjLyvgKkuJQRjfpwElY+Mk= )
+test.non-terminal.d25.example.com. 86400 IN DNAME d26.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ zUyM847QI5r3pGcqbmGdtqnhSMsD2FwHdhKZ
+ ckRsy658jtUad1RLS3B6B+3XKzZDWZWi6SvZ
+ E0buEaY/FLkFT883K4W/fHq08Q6CChvzuTO7
+ DqX9HL+EkwHzzUUL5DlVVarvGQLP7ffwZ6nx
+ N6OSnIevJdG7z9gdiEOJfLo+JfU= )
+ 3600 NSEC d26.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ lUdwjulwBDjxnKePZOWPD/J0AUHL8e3phH7Y
+ XHiG/pIf3AyzvLNEGaZadW8ClAzzxh1nNF8K
+ IS9wKoAe9deOYFFgQuzyGtStawUu6t5QUfgA
+ ByIhZ6HCAyLIuDdmCDSSZ8O2gFlxPwAQA9tj
+ 7iToyq1Q069cfQgcxXg2RmMn5Oo= )
+d26.example.com. 86400 IN DNAME d27.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ CmrEWa2V9yzrdZc9JZBQT2+YZK0WUPpI9tpc
+ 78RPO3yODziMMO+2SiJCYJL3ZMaLKhmBwAWG
+ tAe9KUpRbJX+JG9hj8Mn3V4NqsiIkV7oBQSn
+ 4GqoP0SstezG16XO08QRjzjxPI67e5BePAnA
+ hozhSzzEJGnSbS6DkJYmhl6hX2k= )
+ 3600 NSEC test.non-terminal.d26.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ l/j9Rb6vuKOSEEhjh6iWUrEYoXgyEuQr/iYK
+ aVcYO2ngJj+NKvj+VYuUr7JN7YVbDsJS1i16
+ Dw1zKrOWFWqwcSNRU2nR3g8r/VLMIJWf+Akj
+ XMB44pAKSfzsbwVgvosLqFkzqWK1URrxL145
+ i55JBk1VkxcC4w2nhkVkgpvd4e4= )
+d22.example.com. 86400 IN DNAME d23.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ HdJSKEfomKEEZv+V/pu+rJuyzLCD4DjVK3Vc
+ ZPh1oF51SghRi7wDwma5saguexv9HJhmhIvh
+ 4lpHD69xvGvr92vwkOzsQcmqCAn0MdRQKsPi
+ QERsDU28Ln5AopdCN9HqceEoR5ydP0jNvhll
+ leaEs7KHl6GPOC5nX9RTCRyX9h0= )
+ 3600 NSEC test.non-terminal.d22.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ tEydzSTde8y5Q+grtET7M/H5Edc3URPonUdN
+ 83vhA4DlseCHIUfcn2KVlJHis1HSJj3ZOrDz
+ E0ulwuiIpAojuIDPuuvmdokqkX95gPdxcVEu
+ OJVsghY4ZNJv7gTY041+QRY2E12tp3RsrJRd
+ HLFMbS6g3OzK0f99mcahOPoshrU= )
+test.non-terminal.d22.example.com. 86400 IN DNAME d23.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ s2nQeFH7qxKNgI8p6/oNqHMrUvzqztAFEunW
+ N7TDFuhAxqGHHdXUXcQ5iLaG+nXlHqZjVhbZ
+ g5XFU1xyKzvr3QsiGxMcCbfMbimNFUBe96+O
+ wlEH6zH6LeS2TfU6RbXWv5cly7mKwAB2OZZ4
+ JqSGXh8+VST7cMdgQTEyOsCst58= )
+ 3600 NSEC d23.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ xD7AHNZABsho/o3YWgedJz5mGH2WkAqTPOKK
+ 0xuUB0q9q32Gfwe1irIzz+luKEJTUf6iZwtC
+ O/a7TqE9xxVvPwB1eHd8tTfqkPpsTn+EwPFC
+ gWjcn0cWzXJGf/YCHEN0A7g1LD2JbsuWKePm
+ 5Mbk0/VtV7yLGQDo4GnHseCelAY= )
+test.non-terminal.d27.example.com. 86400 IN DNAME d28.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ PjMS1OoEIDXiYsozP8j27urT+eg4UjN3ZY1p
+ RagCESTRBSAJMWwK+1DEvCnWUZejVR9C+nwf
+ DDBaStF0BlEi8SHWjcKz31G5Wp7feKvefcWX
+ t+iHl/dg2yOeNzQZqtpDNnz1fk00APItOe48
+ XHw5pAmPJj2TSj1ku+EfKlN9Yns= )
+ 3600 NSEC d28.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ ZySZ8MSmvWeJR+tRt0Z+UUArRdNH9srsPSe2
+ gzJrncABd5zHmclMxZzv3P7C6Wpyp+ZqAIHx
+ 8b9bsLaHSxojmcexKUu03KLgxRmUIGufD9e2
+ vz360CdkWSae+Stn7i6qquaHFxPqrrrdHLDz
+ N/El76yaHLCYEia96ZSSJp1lJXE= )
+d28.example.com. 86400 IN DNAME d29.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ MGLs+RhDBLpNZ6TzL8qCeQEoNUvbg5IU9vb4
+ aWE7yJC3jB5HsgMLxmGTB+MVEUfbzoEl0vu2
+ XFFfIogNOxg6MMwfTc53ca2q0Sm4Kvy8U/Wn
+ AP5BQBjr2mW38UFwSnrcWDN/NPuqt8iuw9Pc
+ xm99tv0BK6oninzVMlr4VEdUv64= )
+ 3600 NSEC test.non-terminal.d28.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ PHnJMjYlMKrKoIffBA/zqr4Je8IZn0UZvKoL
+ I/mO0Mv3wSItH9EhbH25DMz7VZAFykAUbBBE
+ nDUYGeKAp7iCHcToOiaLSAghasipw3W9ONK5
+ rtxnUVh7z0Ha8OgrzWbHJjb2qGJFJnT052KK
+ 8lcG5IIghBOZsWJzRmmtYLC3bls= )
+test.non-terminal.d28.example.com. 86400 IN DNAME d29.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ h1Hvv9sNDqbdmHnpfRMi7AhMmKL9/79/7g2L
+ /8uKxI+T5847tpV8xmAXONjTDFKvqY+cSESY
+ QuxxeZm0ONkPi8eZ0UsibkYBbVrzWoA3p2wO
+ NmUXCgozIPfWJ6+qb+S3T/Zllo2x3LtIDqjg
+ jR8wiuBHno5Y11dD/1RsyZxgFXI= )
+ 3600 NSEC d29.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ RsxrQxaCgsM08RuCavXBFAsvCXhSqM539ptz
+ c3Z25DX4YX1sdKb2klsu3S8u2V8dqm/lWeOL
+ YNvtrt1h0QwZV1AbbGL8MD4bRx/FKwc6DDbT
+ Hu8HwIz1Rif/hj6ah0ZBPe4NX+xF975iUCk3
+ 3algCJt6TJbOeTGpm3Qhs9iVXi0= )
+d29.example.com. 86400 IN DNAME d30.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ qbj0MsF0osT4ljhUHI4vcCLXH/mRyY+tzhF2
+ GD70++n053GA4XRhFIVmTkPDf6jMi5KeBng2
+ PCjDKqZ075sagw1KgJ863vFgvVtDFqpcZyMv
+ p/8YoAGGZ6tfzesdyQNsqvvv0IsFlyKMbmf8
+ VwEMu5w46lN6d8cdkgcpdu1qIeU= )
+ 3600 NSEC test.non-terminal.d29.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ g8NPe9d272SXcBLtLSa12a4MwPWaLCJo0L7C
+ 0oHI48Mf5hFYgd6c5eAus63H2xadWVVIO6Kp
+ dtRMVh+7Iru7Ttr7p8MQd7a/1kHKJUypQGnK
+ nc7kO1EZw1zSlr67xlky67BnVGtPpdTEv3xX
+ 7GlKQYikDhkS4y50TXMdgu3Xubw= )
+test.non-terminal.d29.example.com. 86400 IN DNAME d30.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ YMZYBk0UsQKvcUKCNoHB0Vr01SlJUNJKFeWw
+ 1m8ODQvM7iBD/98P/0blFJhRpU+8L3luMOQH
+ jw2WusX6tiQ01GigPId1Hx3fffRPMgFinUSU
+ Zqdbblxkcq0G/LS+cMw0U+P0FrYY0RLbG0g3
+ t3BFNIvc4zp93PLpNkoANvjkPuE= )
+ 3600 NSEC d3.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ SMPFxTYXw5SCCpGZN6iX/LXGchmLhYpkVF0W
+ eMTpE1b8qXNSEYOX/GcoPTKpq8vC1b5uDsTC
+ csQQvfw109jVQTQKDvj9FcTdBROCqu3+fMoA
+ NqOpQ9myqWlJRqh/j4/Ma5LXAPyjHl7bn/LR
+ /qm5cEOUuFmKN9SxCnt4P3wX6b0= )
+d3.example.com. 86400 IN DNAME d2.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ vLfN0um7RTzBYKsBXcKhjE16JHQbT4KWYMdG
+ 8OY80qoDPyJhOwh/IVwyphVx1mJYKGo/xSme
+ 1H5krPXDQAe1nHOzm+ccv/zxDtOe7bSZdKqG
+ /vwTx+HmFOSI/fhW9vDR5lzlvY9heK2NCYfc
+ ZJKpI6cu/kF2ERZLycBHfWBszXs= )
+ 3600 NSEC test.non-terminal.d3.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ H0nHHxU8yoDmQKxIvRTBQERmc7TlWmKT08HZ
+ 4Q0annmyAYwEmcMS+825ldWmv6kb7q/zdsA8
+ +h7In0slgBDh4m7MSQpl/1h6zkTWugBRZUso
+ GIn3XfX6GKtPfQAWLOdPjH7yoTmV4vHCgJZe
+ StXXGYpy5MCfBIC4pymdRouuydY= )
+test.non-terminal.d3.example.com. 86400 IN DNAME d2.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ PbkvKAx1BU8RKRQhD1KWxhuOYE6VMbvfh16S
+ uCeBr0Wlp0ldtnaq2UXxormGLVBNVVWpet5m
+ OaRY3FviXV09+55vBimJHkpsrD/q5y39JqF3
+ WUd9SufzwaH5lGR6FnQqpZytrHDFRHJdymwy
+ /xbmVB+iiMCpy17yAIy/iOj53l0= )
+ 3600 NSEC d30.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ b0IxpO3mLEovw/45lknt9Du/YmsCr0+tBGuF
+ TcXB+6GII00Az+Gwkg5/NWMJuDphGCvsxdJ3
+ 52dbP3pBaZSsfZrLjPHoUwF/8jziuAvsxE1L
+ CVFwtKuKh3aKP5WBav/pJ/w/KRryXqvXtGR9
+ Yvx2zQ8bpmX5EC03/Bv/t4rFoAY= )
+test.non-terminal.d26.example.com. 86400 IN DNAME d27.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ oQFooT2ajjBwlLFYJyT4M4dEYfcQZ+1YZdBK
+ YU4fDY5O3XaLW16ysOy4QSk35LJ+NudKb/Gn
+ 5+EFyOuqQ0FO0nSWb5XDFUv8bHcqkcxLixZH
+ Otz/gTKBBOG8ox+FKG93/oQLabaaDXnvagFR
+ 9p7zJagqCBGkV+51yyOf2UCOKeA= )
+ 3600 NSEC d27.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ XDEw3+UJZOdBknx0nXnPSCbdJ0g/oyLvV944
+ zmc2D53GYPUCw8CbY2ue8xs9SiIRMmlJVWv0
+ 4iaeRzDE1OEK8lgRYmPQ8A+I3PrAOZacEdLI
+ cZQTFTwIL2wG6ifI/gSXsuhbsg+NT1gPVIJd
+ 1Efc9qWvujkQ7Y3uOfO0Qakbv9o= )
+d27.example.com. 86400 IN DNAME d28.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ G4dzVFvM+hAc7F/m11AnrNjcRp/7gOa/MV1S
+ EXdf+geKzDOlNMSuDQYHebZ1yjyO1zSFc/TS
+ jA6InaXNXRf1IiLXll0Sc7bXqQZAlUDshpp1
+ tQU4az4Z0mzdRdOsLFysd/9j83lL755+g6TY
+ nVn/3KHbX6gRaPcyEOFqe6gR3ts= )
+ 3600 NSEC test.non-terminal.d27.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ ariwMYWZb8te6M9WnpRi8mLaNGPmiRfy3sY/
+ eEwwohC4eSueB5/OAO2Fa5EzNpCY8OiYc22e
+ sNou+IYICFTDn6smP35QV1Ltbwsk+M2VE2yz
+ RKWuXPz45qykVFudloHild3qcNz2RI+3Suak
+ rZ+OxgaqtD16nXZnxjsDY8bUB/c= )
+d31.example.com. 86400 IN DNAME d32.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Ibfxt0RX9+Euc0P9EWzoazqm2pNZYCdL2cwg
+ GnH6Y/oVk/KhjQi5UziXTQob5ihX82vLgRaP
+ 8MeUaPdACmy7CsuDandsRSVgc+ZkwOaaiFOo
+ 4xguJqmWgqD2FBSeI5qgtrlL/wjW6V+CsF0C
+ hvWmGhYMkaOOKRPE3xuqoOO5ye4= )
+ 3600 NSEC test.non-terminal.d31.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ RmoQsqaU9WHhcfkWUbJa0+yZQFE6OwWUi2oG
+ meLhay1FZ6YubZDR1/o343M0UzenV94+Drmo
+ GjDARWjHrN/fFHbrhf0paTwDxd2hCHKfRK1o
+ mGVGJzmmGYlTju9FzUsDhFVY07Ea5k2L9+VR
+ ZUklKZ1gmEVT4t5/uSrS8pT69LI= )
+test.non-terminal.d31.example.com. 86400 IN DNAME d32.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ HY+yqh3Ssj66gyTGxAHPW7IoZvYNeu7pauFn
+ 0WgRSwcd6y2CzyDrqCPRBoY6uZIQxbqw6DOa
+ T7QpAGzk+gaeBfZELmpvfyLTsbdd0+KzTaer
+ oNlveT9pIoUYyvF36jOtkq28Qh8YwCCC2GtY
+ 5sxQYDX9njTriEljlVtzeQdRnbA= )
+ 3600 NSEC d32.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ u8NHxb8uh5qwmJq3JoYOF0056NNalcvUN6eQ
+ ZsAt+84JPgUkjR+IA2BODjgZYF696VdS7FCU
+ n6jU+5UO0ouMjWBArILglX0AttVC3SmmKyfs
+ UIBmBK5caCEtEayaxJs9cKJC9Dp6bpivzvbn
+ iMHE/Dw1GLkl6AV8g8aDYs6WUo8= )
+d32.example.com. 86400 IN DNAME d33.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ AS5nd7zJuGcT/P9QDrE+JwPMLCWhK/scvd0K
+ JbgZvmbf7dJyDXjNwOx9I8X0JFH3udhL+6J0
+ jT8vaZYTkgCeMIJc4dC5W1rouAHlvKyQgM0S
+ IeY4bmc4UzUYbmgWoTlPzO4GsK2j5owkJFlt
+ UbGmbpoYuY2uFuoM+oqk/lkO4XI= )
+ 3600 NSEC test.non-terminal.d32.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ 0EsOsK7NuVQjp5RGvix7beXoTYLvBVDfyFlC
+ xuaMDE2kcdi0UcW+fiq8/WzToZKd8n5qsSkh
+ XvewdNoRsfaVykgj8+DjpfZ8etohf3auQVwT
+ p/qyBZ+dZApLhoqqcxhKbgfdyq6FmQCwX+xN
+ csb28Hsqz8U1xZcEf8dx9N1FdoA= )
+test.non-terminal.d32.example.com. 86400 IN DNAME d33.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ H33EqQS6p4UdDRfCP+bVqKAAOTSKR7ZAbwJM
+ kebVHincfNSPp7yl4HrOQd8i+ywEAVjoFtqq
+ X7KdvUSv1Reskw5kt2Yx5msbJMGJdIljg3F/
+ gm8afdHNLVo3ilf1r04JCJgEq4jpck0NDFv6
+ KW3sJ+B2q8SsAgZ/NsqKVkByn8Q= )
+ 3600 NSEC d33.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ QSmQHU9L3+67wx5YgwUNt9C55W+sfJKojkBi
+ BFkRTqUDb5+nm9aF8Y7Fc0evCBzRr0lvAjNi
+ m9z4MjQse27KWqpk+7cIDZGSPaRevbNO3Q8r
+ 15d0HXxuqdzWz68byabI2NuNbCVBFVCU7urM
+ v7y9OghP6sdY1Eju1OestuZ9VRU= )
+d33.example.com. 86400 IN DNAME d34.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ rOYGG79dKzxEZ5Ywazxq8942tWOeI/RMGrRS
+ Xe0fb1svHYV2UTX9ohX6LclTonzPQwVlOi+N
+ 0VjBECDZsH9GXlIw43yifybcSDV/VM9fahku
+ tStuDkhNy3RpjQ5PSWr+aXbuHM/AqNuCJXh4
+ K4F+AhnPBDE7Xucd2oT4gYEpUzA= )
+ 3600 NSEC test.non-terminal.d33.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ TzGtSlbvxKvpEQWVpOR7TmxqnGZVwvqXGgw7
+ bYT1EgPqtVVh5la+0MvszHKf5xOq2+S5UZjA
+ bgMh1gQ9rKw4IFelQ9OPBZvCwd0zWOj6T1+y
+ cdRtvRK3ZvBM5eJ1CAhNyuSVkvHOk6+wGasK
+ mG6KM8GMqNN2XhYt6Dfvt9klNKA= )
+test.non-terminal.d33.example.com. 86400 IN DNAME d34.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ zveTqBCx5lN9/4aqk2f/MYW56/fmhL+Il3Ll
+ 8Tx6RYnhn6qQZoeqy85dyLvWI69uwPOlKVS4
+ MxiALMn+c3rYyPw2zpW1jMKawMAQnt5OAcE6
+ UtpQcpmYuizbEvSKU8o1jPjFU+Kn4iCaz8Q3
+ EETXBpEGDSVDkiYHxWsJVTSevEM= )
+ 3600 NSEC d34.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ OtKiiybJm9EwaRKR5yZ+EL644M7Sd/ImxFL4
+ rUsoFenyBIoMOiLpnXuBsGihBLIyvg/ELMZQ
+ Y2lSZBSIYuP+w75nPZw88R1spi01+gx7QrPg
+ xiPM8Y6OEEVzHzv3PiLwkwbyGSu3Di1yv+jm
+ NKW4X6tnslpjFozJ3bhXT68vanQ= )
+d34.example.com. 86400 IN DNAME d35.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Hl6mLcNObM99IzWPf9uBCBJbDqF4qMeMzIIn
+ MtTqk8+Z6jVKoJ/bJm+VXX0Dy4OBuwfrB3z2
+ MOaIUl/1RIkDClFpFPYVWSbuUkELw0T8UqTS
+ TDReleobOtDuV5ckzQENxzng3BXUzQFI7yQq
+ H72kUTUhG3HP5EmuaIObw/DeEak= )
+ 3600 NSEC test.non-terminal.d34.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ iJEzg06kV32IjiWQU1Dz6fnEyt3hMviPuRiY
+ WKvrwZex8JNN1zO8MNcdvOQQPnZwmrgarVRG
+ ncl+U4A1jO5/5uCsEBXMzwtVqtbHjXuKUPsp
+ L/7pqHUeg3pRR1aRropTb7klYmHL5AHxGu+2
+ S26IrGdvxM8R+03D4ks2I5YDHVk= )
+d30.example.com. 86400 IN DNAME d31.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ iz66peI7dI1+8Bo4F0HEtmQ911g8wD1ILT7T
+ 2BwdixlyrEy3pCOYMs5qyhjUTX4GMCOT597A
+ osCt3souLPsDCShBSYIaf1WbTUGM9zEeoG4x
+ dDTgpzA0kmKcY+mJzQKriIJ9t+J3Zogjh3R2
+ 5RtDTefYyk5FR1drppbln7bnWhI= )
+ 3600 NSEC test.non-terminal.d30.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ CUzjs/O6TQRAivsAxd3zoc052NeN9k0NlpFR
+ KLWzOFl3yQaX2b3zFpXJhcC8WgIg4I2cMaED
+ sOs/98jqIOTv538WGRablidhEZAjkguo3YnU
+ Yi8foZ079frfWbeL5stzUn+bXiAFX6SIaF4O
+ 6D7qcScSFFrImBr5lUU5C7mdVwc= )
+test.non-terminal.d30.example.com. 86400 IN DNAME d31.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ miSX5BLAnSn80bdr+wNmdSM7iJ0yiBnvNFWu
+ aNKgv3nT4Ae3HSpEmNQObKOpRlA7Dfkw1XKB
+ qlJZOEjSE4UNTBG/8Le1G0u+qUuHeqHHAxUC
+ S7KoR1tAcwSkyk9vSSai8t9zcGzmZ5x+wpGx
+ xk1htlq/xigbGHAXoOk4axF96QQ= )
+ 3600 NSEC d31.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ UBDdtVUEkXxnz4jmt3NRE3uodD5UxczcOV69
+ buELznNVoO6IShDY+s2sWpGzIpQVdN32CMoe
+ uAzWI1PqQgu2xGMPLCynsiPrYVDkY2QK6FCX
+ GCSAncLinwYgF4mJ8rnwnx4O1bZtOxBPA6e4
+ 3YRDR9IxlOrWojTn3+0eg+tuiL4= )
+test.non-terminal.d35.example.com. 86400 IN DNAME d36.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ mUffclGpyz7Hf7LJVgwMBm8xIp/2YQ1HIHU6
+ 5in/8qj4Lt/KbiMkKUoylsEOk1EZCORgbdUr
+ c+sD7OL1IbtNRHi5zezIY/J0/Opj9rNvqDx5
+ LdqQLUZATVknm8w7P0zKAKR53dJsfBorodV/
+ 3ojk4cOJ7KQOmOpz4VZ51aX4P0s= )
+ 3600 NSEC d36.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ j/oECHOEQmCSuYZY+7ihr6kXkAU5HGBVRe2E
+ 1cvSu48vm4u0kpez5lNcgIYidXUGkkLgbzRZ
+ g8qerNrUolUGW5Ou6eH9vRyOKxFx7CN+r0VK
+ s4SmaA7Z9QKoDhMhTL+6L5MD3SuPmTfHeMSb
+ YyYEwfVZ/BQbUivC3f73uImgqa4= )
+d36.example.com. 86400 IN DNAME d37.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ EEdpNIRLMORdKGKHVrueW2QjjdTi9L832k2O
+ ZP3mH8b3VFKNehdpo35k6Jc1zHFB2/0TiNkX
+ OqjkgiBi/spmXHDop3T9o08R9egVmBkfxpHz
+ Hos7tfjjPlPExmYnLJto6XDJHEg3dhVQk8bc
+ 1hkPCXCoQUo8yF6T2KEix9H6GAs= )
+ 3600 NSEC test.non-terminal.d36.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Mksb5Hs5S3teHznWOMMqESYJhjr5FHikFxkn
+ uDgzRNbHUa1AQeMn9LBsO2hGA0xsZQ+urmJk
+ x6GaGTlQfL6jfqe9CeEybqnCAWWqBw9GtW/d
+ LsO2pqYPwmHe8tChw2FukH1F/zzn5dX4NJvS
+ hzkeZgM7IDJ57mcoOoTvbtK0FqA= )
+test.non-terminal.d36.example.com. 86400 IN DNAME d37.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ DXzD95y216T2+S/ZqH95hGzcN2yohQ/JEgCz
+ 4shcLhfyhBCF8UMQzuAyE7EK46nglb/9p9rB
+ r4bEYGOTbzj+Fucct6TLkToLPtFmYw35Bl+d
+ 6XAGhllLeEJI6tLHD7IEGkM6vpwqiJFZyXsg
+ E21gaNSNX6pxp9RCQyArBAxgg7I= )
+ 3600 NSEC d37.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ EEpCIFxHTpKCYFf+dKgCzqlj453laGc/PTA5
+ 6+jqWd6iGPTBGBOpyHm5QhgDfVVlpsu0G6xW
+ aBLRyN7zhW1gsCm5pGnymBSdrQkifHE4RnzS
+ DridL6bOrSGteU2Xgu2AnH9GLTttfs+CixcM
+ rh46Efjw1Iq9N4NYi+mzgitZnaY= )
+d37.example.com. 86400 IN DNAME d38.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ wNK3wDPNSo4TIM5spFthBdQRvL5ksyo4pbgb
+ rChnKVgaN7WlGUKUbh7ILq6L8Gx3Dgx+Gysn
+ 1IntGBz+5LJRwbFJ/mkBB+u/BpotekSNb/nS
+ f1AloGCtSTjRTp1aPE2P73+Lzqh8QQwx8SKF
+ zKLtZ/s3sxGqwA7q7qxbUJDVito= )
+ 3600 NSEC test.non-terminal.d37.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ AvLP/8seBWJHTYbBh9UahXM4N5vzeyDpKVKn
+ Y1lBfkMmiLb2db1P+Tvkvzo6YxRuETkZFUgA
+ QOFRAyYhqNgII4TlnMkg26VcyojGLZO+k8ky
+ WAnK3Py0jSOZlri9J4FgjqU9xn0Wuru9XZSD
+ kpn3fzUAKhU2KuUcKuXIWQUvGuA= )
+test.non-terminal.d37.example.com. 86400 IN DNAME d38.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ mzyChHLbPJW9yGrX73k7fLCDdavZBDJOtTxD
+ xixFNsfDLTX1LZTP4QtQw4sp+gGEOGQZbZrD
+ 9VLGnKTPSl9PmSKHEDpNsHt1LgJQqyPu8wF8
+ jl8muCjcw8wB4idoVRqoQ1tuQNdKazHmGE1/
+ zMNrEzQbDafDWucznGK9o4ngDqc= )
+ 3600 NSEC d38.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ oNwmaTIxrKdUB3LTHPeKtBHOPA0CAaNRYuxL
+ e8XY1Ex7SyARjaYD323Ch9nFIXezWXmQ3Y6X
+ xbAhLLJ4s3+osk50TGOpSUsmuCJ7oDYdKlwY
+ QDkigNBMUvHf8m2fnFJtfIjWTYZbAFktKABj
+ RIVqNpPAEQG1EcXHXbcy6MoWCvU= )
+d38.example.com. 86400 IN DNAME d39.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ hGzgFbxjCnZCM0FfXpyncpMaYDIul1IIHqzf
+ rd/c4vtU4mrJ53Ix5KI/0xTpu68CjJB1mIg0
+ J+1qbhuFYlDO8nftcuafCFXZMaTGoCtyL3Jc
+ pnzsD9mkhHrhhkMh7770ZXi+MECucoHx/siB
+ 9h1qsqll4wma+zcU99sfEsckRGU= )
+ 3600 NSEC test.non-terminal.d38.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ gss886rBEatmhX/pYGp2ycafXr09n51+q6L5
+ YWYrAD9IR29qUXsT9LOnh3gkmIawSiG/u/7g
+ ufRi1LRNyWuDRfLrBBmTmQfkyKTfPKUYWQwu
+ 40Wy9cZ7juNnW8lB6CZ/OWIFI5dS/+QDd0Fs
+ 0gD5G3EJsCPI/6hJWg+25cSMS9I= )
+test.non-terminal.d38.example.com. 86400 IN DNAME d39.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ vLHrl7gZRn11fvjVGVJp3sEtJyPWjE3usU1f
+ ozehBRzA0xuigXcRyuJ9OQHgA6Tmjxs6dsTG
+ ZOlT6kCWeVsFsa24Iiuy6G7ei+wPeYqbyZUS
+ x9lpAJTDjmi6T8eeHGNazC3mNSjtbU6XdEui
+ i21Ob89nF2XsKBA3fDv48w26whc= )
+ 3600 NSEC d39.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ T5iOV3jE0Gm5wcijUk5Mow6Lrzps6FvxQ9lI
+ N9675nomxuo/BD5Y/p8MkHKaQUhJ0cSQhNgF
+ 8dsaDzc0M2zJAHJlT99WHGhScJakBTo72x94
+ LONS4EQCnKoE/wYdfPClvWutp5spV+7WPb6D
+ +n6CFEjx1hPNdPsRdgnhb02OBjM= )
+d39.example.com. 86400 IN DNAME d40.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ NDIEHtFYWSI5P+8qBQj3ZLvWOOXwCNOtLDwh
+ 76QtFxqu7S+kHY7b+w9RZi+WR7cNCimBD/pM
+ 341M3ymgfKx6Za9CzbvCR/fHwEfdm0OTJfw+
+ UUdMdXk2gHOw9SrP8T237XUuWIz5QV/ZekMr
+ LgtGGmZ419qpiBVcUgj6o9P3Nbc= )
+ 3600 NSEC test.non-terminal.d39.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ SB7LxD9BBhASAyMoFI0BfVUIQbV669zfGa9P
+ E/VM3el3yuBYDUPpKQaw+ovjujeDGvIbbz9N
+ JiVG3g5eXiQMHony7Laasb1Y1VkrnomH6G0S
+ Kx/wPJ/gSkJq94uVM8vMwTEBYsUzTTFiwc0U
+ r+9qAndCue2ggO8VoFjY6OQRnZI= )
+test.non-terminal.d34.example.com. 86400 IN DNAME d35.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ vPUvcC15rTnoBEejvJe/7n8aivkRzA0PL078
+ QfkS3CNxrVk62Ba6MI1NY9+kQBOed0aY4bhA
+ t7ZEFwaiZtVA5owcRCWymiDZ65gX2gcmNppL
+ 40DBp2B5xEGFDM+337w21Gakj+f9US/AWllk
+ BY8xlgOTxLHTRkHds2PEeooUz4E= )
+ 3600 NSEC d35.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ hk8Napz3hJKVPlbN/5vNiE/1U6BnOGKdxj1e
+ yx4U509Ik4oc2XL3BeXPO40xPEAJKL0WMtUa
+ xOO7SwTNgLy/B2/1KZwqk0QTzteyr0XtMX9i
+ K7bPvVH3M4gZ64zDXE0OKngHs6Aewg0H+DH0
+ ofslIkrEmzGor8L4jJcO8W11YIs= )
+d35.example.com. 86400 IN DNAME d36.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Ep2CPSeWtSOdFuFenSMD1iShq9SzYF784nVJ
+ rOMpUHXeJ8qUV76Sb8OFinmh/nEZAP9KZIxX
+ krkV141Pvph2EuASLuv6+Y7DV846dztz4spW
+ xXLLbX7Eg9QjzHPPAosExomSGa7+y14PpaDc
+ JfFJu50nx7OQTkr4HuGCW7EMNp0= )
+ 3600 NSEC test.non-terminal.d35.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ fm5PRS8J58nYSs/52NPOREfn0+cQNcy4RLGB
+ us/hXZ5ahbRWlpaK1NgeDbYqCfriFpCSWJCl
+ oLMVKJE7NiawaspAe5VyiFY19vK7gpyIRjox
+ C5U/4GqVmQ9gSPykfsH9J/XFYfOjryvGFgZ9
+ p7lLopuYxhpvZVTCYBjlIgSCAD4= )
+test.non-terminal.d4.example.com. 86400 IN DNAME d5.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ W8QnYa1Uyb1jOYWIOB73ad+E8xj0UaadmzYH
+ eSCKl0YZ26L9d4VXjzFOrLIQMcxBk1pwY+Ll
+ iTrnQfoQC+MO42ZRNBWaJefh92VnBgZYnL/o
+ 6N782Jo6RTCTuzhODfZGMrvZdGOaagqk8BiP
+ YPob0BCP/4A8woduHq4LBIm572E= )
+ 3600 NSEC www.d40.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ oDZcLuL4ICNIwh1eswzWap3ZQMRcbgnc6Ofr
+ Db2N/qMtQ5LNQJxRgdiFA64WBEOq3AibOMV3
+ L1MlIDHZPOjnHOnm92V3deEQH2a+d3xntH71
+ z3XzvLE1Qn0T1ehvmiPz5WVxUbfD82mKNE2u
+ bqIhES7ghFGPsZwMisWbYnnbSaM= )
+www.d40.example.com. 86400 IN CNAME www.d41.example.com.
+ 86400 RRSIG CNAME 5 4 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ o56J8UlTilwtXGyMxQzx7RG7ZI9qQaEvjkj5
+ afY/GDbhDnjCWt38VEmxiOYT32qf4X+7rBsz
+ spZJkWYhJOkgBD0zuqWcIv3dRmCJyK9sqvaK
+ VD4PKpsx0n1E7n3Wt+SIaoxyB75WYcLXrIcb
+ Pe82mi//bc2UNDb7KO5P3MtxxXU= )
+ 3600 NSEC test.non-terminal.www.d40.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 4 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ q6XtW5CF5STV+ssIwjCzO3+WGF9wk+RGKhSJ
+ 8Swr0N3FIG8eVM1DFCLtntBTsWLVUa8GxtIg
+ TqNCo/QN/akRk5XAWdEPOaH8VH3j9TIiphJA
+ MEtmiBAau4FsNxwhMyzNoRU6K4eXrmthi5Qp
+ T00cwCDBU+L0+TgHG74aQWsXaro= )
+test.non-terminal.www.d40.example.com. 86400 IN CNAME www.d41.example.com.
+ 86400 RRSIG CNAME 5 6 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ xs27GJvBFwz6E8giGiO4mfvvG5YfJRGB/hco
+ dGvJSExS09qpZRj/Pv79utnj/e/SZT7KVByT
+ smzRNRp2zCmXhzdd5ymOBKSzz2J0yjCAxyJE
+ ezXZ5BOeyMD39s3wHNRla/At8xWntVtZ7sTV
+ C0ZcVhBwjTBdEqzEqo4ZWVvjHCQ= )
+ 3600 NSEC d41.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 6 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Y+ShsKZVeXkOQeu2aSlFw3sRb2hZTf7D2Kh1
+ synhkjSbfGud0RM87gtlLisHx6GRp/kM20G/
+ LXim42S5qRA6PZXez6SEVPHKsCHW2IjxkO0h
+ QScT7R44dre9oY88hX+QcMFsZ3c3qphJJPOo
+ VpawlU14Y9x5K2eFhmZRqwXpltA= )
+d41.example.com. 86400 IN DNAME d42.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ HALKMA0yTqwQOizKYXQ7jTFiq582MkO6yvI5
+ TKZLd9MzuIjuFLw/EKSPcvhKCIreS9Gqeqxf
+ icDqiTd+mqVfI9OVYFivI4KR4+cXxTDTlOhT
+ SxHfUFT+KWHFKP/yEQzlYkburU8Ku+S+HMu+
+ FjupYSTIwyJTOaEYcVKDQGJzTtc= )
+ 3600 NSEC test.non-terminal.d41.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ AHx4C0Bie3OyC1B7vaAGl4iMfv0I4y6cCLng
+ 7N+BLafBgondhrJvwwZiIugG2A10ogeilLm5
+ JfVb/LfVk/m/6hOM3o+HYj0G4HnrjLj221Cr
+ jNnp8Efs6Ey0Za/6NfMtdMIfBnpuQo0b7976
+ tH6KMUhHgJ0OjtLxKJ55p06Z/wM= )
+test.non-terminal.d41.example.com. 86400 IN DNAME d42.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ bLG7GY0x1i7OGxX1wvPG6PwgZnvm90kaKnnA
+ KE+HgbcwEnZMD7fmWNFG/+ASyh83waBVz+RX
+ D16XHdncxufxn0/P0Vm2x9JjWq8PTfSgmMK4
+ mh3up4ojOCzO/BVmBuLgjodF5CzELWALIPC/
+ gw7eW0INNu1AEQuR5+puB7+dlAM= )
+ 3600 NSEC d42.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ ky6mcrxja84cjouSZ2ptv5/6GhnUSS4iDBqh
+ 8xuSovtOMAi80i/0e9xaXLpQaZtw1tz/uATT
+ nJVGJdtz9efTahahKDUgRK4PoPh4fcJHhXi9
+ bvS5oh2RnfVJpGCWIQW6jIo5LWvi4cZRKPt0
+ V2X4pEkOSAMKmHjUr0kg0+srGxQ= )
+d42.example.com. 86400 IN DNAME d43.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ v9BTtT4w23lhSGF3/sdWPT4nGIE1Gk4uCXRo
+ fgNTQvgGDZXzkR/HsWBvLBlOr75yKxJLYAKX
+ yVoMGIECluCTiMD5raK/YwRrBzMqDIie9a/v
+ rGljVXiLz7+evSAhVS2Dsh/Op8P489F00h5a
+ t6c0F6bLEwNTBuHwrlcPQx/r6GE= )
+ 3600 NSEC test.non-terminal.d42.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ GEy2Wj8dgY4Pi4K9ZrAcvVNq6t/g+RWJ2Vi7
+ 5gUNkwuZl3XaIC8PNgSgAX1jbT2BD+R7IzTp
+ UDCsTyLJ7j8TnV5vIQIS4cPhQmLSYPKrrWtF
+ TbmSkewgiwMcSjBBn71KXvglYeq8EK9o6X1o
+ VrMIF0kHnZljfhzpLNO5PaURhaE= )
+test.non-terminal.d42.example.com. 86400 IN DNAME d43.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ MRSJ050jY2e0PLYWyaqz4dO8Q/iwP6ba8pMI
+ bAhM8Uk1JNOrUTcDVU1wLsO/kpImRJ6Xzs/v
+ zmiFM5DocAN682b3U1tT4AHOLcerYTEGlpkW
+ SLjp1pAF385dXIAnCv+rtXCMfyPC+YgpxMzB
+ SXTisFNd8F5e3u6aUqPedbwQmyY= )
+ 3600 NSEC d43.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ rmP0fij7DUJPiaMF6ZkItdAGUNa1FtVp4vyk
+ MpRXS+MttP7RZXZ45lGMEs+ymukukQOB7mV/
+ ZEja7C2Z+fo/QGHwskxI8aASMRySE6vjfYvV
+ YdSoXVEWBXMkemw71UOn0Oys8xl2iVEnqovq
+ ml7/XVe9Bs71nDzIHnucqU1/57U= )
+d43.example.com. 86400 IN DNAME d44.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ CD2maM1SqIdNxtcGjc9kToOkRq72ixZQcjJC
+ Ru/+Q8eo13KM6Lm6+kDFQ8HysPxht3eqrA2J
+ G/JW/Xn5g1y4DBlGcae29NQ8D4iH+noSarbm
+ N9ISn08BaNLPv5OeehUKaOg7Nr84iJiZRQ+I
+ wk94NH9WdZ4/Nruj0iyebJlPr6w= )
+ 3600 NSEC test.non-terminal.d43.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ iXuTUyGAg/HMX1oxcTKlABI7bzNg3/t/LMHE
+ z3eK9K9f2CgOIijVVrvtLxARAzTMT/XGK/Pn
+ dluny/mOBft371a/PdTybtLOkcE7/WpQrMmY
+ GhR1a8soX4LPNV5G0+jfI45P1067+5578Jgf
+ jXG5hxRCzs2jB0bd3fhCTR0peK4= )
+test.non-terminal.d39.example.com. 86400 IN DNAME d40.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ LipatlHHUbS8rIX8ebq9YxSTmjzkMcMuy8VQ
+ uzCsZJqJAMJErVmu5Dg/ru705ZDyuzpd4tq1
+ UaES2xEhyK1eLrPv2uCNNlRFjikCQVBHrpI9
+ cgMuycS9F0nDwVTchmonKkj+kPoQ34CbDFnq
+ 6DDUjm/2SPm5bF+qjCkV5IbO800= )
+ 3600 NSEC d4.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ bzj705cPqlP13E8HzF9sXJAfFS4lFL4D0+Nh
+ DRHFt0BrTHYD6bIqtJmHs0CmMOr2FprPKdX2
+ bY7p/Cv8qiVQnItaunfHXh67Fx4GTzYg+ejC
+ CY/wx3tFtKmTI0ia/cNYAZ3NT0NHWziPYGxQ
+ 4i3geIjS6z61D8o3iCp7N3qKSQk= )
+d4.example.com. 86400 IN DNAME d5.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ LZHOm4LZy2va6O3JRX/BtA8JQWk4G4qta1kF
+ SgY4dXycWGor7o9GYL+GKN7NCFvdLn1cpQ1X
+ RlAerWa6biCX8cwpBssCRqrwupQRoekv1hbw
+ LC3j/knlyqO0BkvQN2PExdQRJ5QS2V4maAGU
+ kDIPFy79GAJQ93QlDT/zifUxbvQ= )
+ 3600 NSEC test.non-terminal.d4.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ s1b5b/MYKL4vxdg5RUvf5/jWottjvPclybpS
+ zpin/ipti7UZuh8d1NNyQZ8J9jlcVxtsrBZh
+ LxUWNdOmEXVbYIdMy9OT7Ifve/HCHGZUIR/b
+ xLT5edj5gWbo1FBWsOQP5o8iMGcvxYXxEytB
+ DG9xU+8hxwY3rFi9E/fnTCTpCXM= )
+test.non-terminal.d44.example.com. 86400 IN DNAME d45.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ cNiTpXMKvquXDgxXLi5jat7z7ZBCipMRx5Wg
+ BOv30kuPnhi5410N8oe9yafxdp9ad5SJaaUs
+ 3Sja0LNr3DdP5OnZJwjRieNLSrOnnfW9IlVn
+ AEXV534mK0uWd8tVKkPfqGhYm5E9+VEMBJmS
+ P+mxUV9qN4Nfi5uKxsvsGP/X2QI= )
+ 3600 NSEC d45.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Z6B+6qoJby9830kXX0Ipb2YG0x/DPh5cT4iW
+ SkrW5HfQblP3EyC9jz+hfbb6ykU1hG2Uwebe
+ Rnh8s5KgQy1/QxjGMEQ/6X02whf2Av+U5LCy
+ 7M+zSKkh7hZOdOznIjz1mku+BI/rX9yC01Ub
+ PMZNNWxR8BOrASwp+6N9tnZHRYQ= )
+d45.example.com. 86400 IN DNAME d46.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ jHb09ju0HWTRdI9jAAiL7UOlQYO0svjOysGy
+ P8qHc/XStyVhstIi2tSMUUPZpeFlgJ61iiC9
+ c90OQNMveAgtSkbKaQ1r4cMaY7gVeHJuPIw7
+ mVxpF+n2B7xAWvvANt59rW5BTN/bV6UatVko
+ mdPFYuDvTlWhf6IvcYDqfsUSfPk= )
+ 3600 NSEC test.non-terminal.d45.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ ihkftxAsQSjHaDWKofsXfZ1vZl/oPixqUTTx
+ KJST/31O/47trPvgx6G2LSOpEgsy2JbEmC34
+ yy8hfQef4JylZ2b6mcyov/AhNC6PI8WGvAMI
+ 9z/GI+4x1uXu+Rgs7cjoAdaUnFOocvUj5W9X
+ i9VwC3mHSNmy6ClEZP9/79Z/eXU= )
+test.non-terminal.d45.example.com. 86400 IN DNAME d46.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ xUxK0dYE031R1BCgI6228gsDBNPstInJX8NW
+ MaAtbTGAvvf+D0XMFIWu4r5NVp3BvEnHeLzN
+ fGzFFqcQiQRBe+GF7mVVjqARRe1gWM0ROcqK
+ GGZFa/05+Bes1sSXnxbMYm4qX5KjaedbQL6z
+ CtAM7U3ToR8g4bue3G+VHPvlSgo= )
+ 3600 NSEC d46.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ SjJK8JkHuqV/a9lehj6bgogqLcBu50PqW3+X
+ 2F0TvvJOsEo9pWPMuyqaAiqHQpXUPsBm6fAZ
+ EMQWBN7T+tmImuaRgF7IXraUyGAst6TmO+L+
+ Vb+KwlYI+FujaQ/oFAk6yp6HnVYKMqV4ShOq
+ 2r/UB8WuN82ul5rNNz/h/pCUD2c= )
+d46.example.com. 86400 IN DNAME d47.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ XoBrzFfsxIpgxA1nOaNxYIOySa3spCUJ5H17
+ 1utUO0op9BgzdYEvehRIkBJjplv4oFHVcsJs
+ jCboFbv3tMCkj9A/Higx55xfNbHRNcmJbeJu
+ oi4ziXjUfSEqy9o3ldjgyUKY/BJIPzWo3O6r
+ p5Gfp9eMOnU3Q3hzsEV23Ifx94E= )
+ 3600 NSEC test.non-terminal.d46.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ ZVeXqVWde/EPyuz1Z2SAbPOr1ZltIlH066p9
+ 4PpYMzpTOzRqOstifzv3+LFMXCFKVLV7qHTv
+ jhs6F60VlvNUepcyRRuqkqXnyrP8Zh2Y6k6p
+ qG347nHdxWpg8PKEV7RhMFItBeMph2mj8b/k
+ CH37VhfjjRuXT4sxGY/gCa2G3es= )
+test.non-terminal.d46.example.com. 86400 IN DNAME d47.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ V6C/pyQIUWDpqI6+MidAIFN29TLDte8VgvHL
+ NKWYiDO/rxL+GFwOo+or5LgMQcQCJ3/Ld69v
+ 9yAgzZ4VXjFR1M9/AwBo/GdEgrEvDoP4/+s2
+ F42+JychSrlnBT4Pakb/1zXyEVIKjrvMo+bo
+ Pgc4cCLG023N+gUB2MDehLYTFWs= )
+ 3600 NSEC d47.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ IvZpHSWiTu5qRMoqnRxOQahb8v8HJ0VQVxu7
+ moqeOaVHI5LMpuH9nUo/rjLhAj/7NdTsXhJN
+ 3qAoB8NMilH8bYENG4F/RmptFzOgNaNk77X2
+ N4C3YTDiRfE5k/6atFX46cl2kR6gIqE+F1jk
+ 8x4xihPr8bOl6oYfPF1nmVRI19o= )
+d47.example.com. 86400 IN DNAME d48.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ xixHWa5CeM1lQa4R03whZvcZgoNZcwby2wuq
+ ACK754tpdnMbqjWqe4ZFASLZFmSVgWxfNhUJ
+ od0mOng+z9WFLY4DtHcNNV6BQ7NlxStEVTut
+ laA20fOiHd3iks4JZ43VeH3I2fi80sPN54NU
+ 1MVkEkaYZ1HJxLC4KVR+fWpBics= )
+ 3600 NSEC test.non-terminal.d47.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ iqG00wlMWWmZ5tov00IuECsekiI1EQjPV1PX
+ /wrwXdUseDKRLPH5kIXi+OvyMzXX1X9AcCpX
+ VV18vEHJWWcrZu4Kfir3w1oen9KIMdZW7X6V
+ 7Mwj+7Vlh0DCBQRxg8diQFjpvrY9imgaou6b
+ aVEM4+Q1MBFcD2423lYPoVZlNLw= )
+test.non-terminal.d43.example.com. 86400 IN DNAME d44.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ I0oUhQ4wgHV7a2epnQBs06nXHcqiUz6G5NIX
+ wSlT6PN9ThHEn0HFqT7V/LQMGx8JIN8/1Yw2
+ Q5u3ZE3p2xrKjv81ruqCzNZLENsBCH43+DqY
+ 0SYIQbtmugmLfmTgSXU/MMP4ztY8VGW+qo2h
+ pgHxz39cq46HcsbGj++mAVip0kQ= )
+ 3600 NSEC d44.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ rIa5atSObcXoXVmVYyuklbJK+IOU8GftEjrt
+ dbTPjk4T9DxIWuPRbKpKT/XHDLNbzo8aFRLU
+ hXGDaFmzngPZwv3Wud1zkZq/ns4ZQTlfrbUI
+ dwapcU16HWHAwGWMtpIruqu8xPh0LMcGadl1
+ yEjA+q0axbLcv0RyfnmDlw/XIYI= )
+d44.example.com. 86400 IN DNAME d45.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ yqPQHJ9Yr663aXtjK85RGBwHcGNnHH9lenLV
+ GmBF3ZoRLqJrJ0A49z5GUyCg3qYeHKxq+66E
+ Z2pTrojdrlagHLJlDBb71glOLVcm3a+37/Hk
+ 6kn83JYYb92f7zyqZpPfIn7iYJ+qIHiJtksM
+ QIc+R8jLNGxBl2E1nMnbo2wgP1Y= )
+ 3600 NSEC test.non-terminal.d44.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ EGZreUf5MT0iZ0CNg7TCp3Y/pIbKuhh9qxol
+ tgid9AZjULjbVpAXE0BZ/jcE1AcafnloqtuK
+ acDy2knb+cgnEzMh2cnX1EdQzEHcK3l0t8gQ
+ e7OiNNoPqC8PxAPB7b6lkABdZ4+7S3so6mbB
+ UyqVmU1SjSV6VEr8FqbXBGNbNEw= )
+test.non-terminal.d48.example.com. 86400 IN DNAME d49.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ FDdsMgd3r/K0KejqZiwPG1VpaXsL7oQ19AH6
+ X0KACB0eTD5LBjnsxsZXmEXfhn12aTSAUmks
+ cQWlUfhgSURi465V65PaQUjJ7K6bT2pKw/xC
+ LXLEN51ayOVpMCb7mOXclqK8x0384d7Ydoj1
+ Tznde21GMw3KfAQLxU/u8IdGDC0= )
+ 3600 NSEC d49.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ PPToUHTj59/4gaW/e75WdBnu2eTEsmjlAhwF
+ qYSAp/8KtxHgWi2pT2ieCnP/cmTjf+DwznUp
+ Wtjwbas7em70yRghkVlBxc0hOvwnY83JWkCY
+ xtQqHFyi1zjLv7xNFwklQVMuuOXXNZewgXhi
+ NfGj6OI6hSXYrN67ha+oZp9Svfw= )
+d49.example.com. 86400 IN DNAME d50.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ NGeU91ToYc/kcz30H1EyQPxUvGGIEqTMdbQN
+ ke8xZkcQFrN83pU1ticZb4kGIsM7FtD5jIIT
+ dL4mNIccp1n2ZHWj6f+8v/rUY3UoZHUEElQj
+ j2WJjWyiQ/RCeniXPouJhTBUJJSobS7Yv1B3
+ rdMJCS+xD1y9EUm7TqjfQ/gTvRs= )
+ 3600 NSEC test.non-terminal.d49.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ qVvvc33dbxJckhXaK8iS2wR0jyxenN0v+HQ0
+ JuM0abF2jO9PC+ii/as+nlJqx5i2JpbEfcpo
+ qbHaVIjk2fmJ0bpXzInqy8FIYx5Ecj0gvvOL
+ udoaw1uQyjbDuld5YS6Gp+6A0zjJEv1rM2T9
+ llhJqDNM2B4q0YUrlXT0Iq4885o= )
+test.non-terminal.d49.example.com. 86400 IN DNAME d50.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ zFySwAcqUfxAT8EjN/zyvX4MLYegRedzOEmD
+ PJ3WLeS7+Cf1adj+pnVngtU3b2O+n6qD1idx
+ Jk9/2hhs6QfdXh41M5+CmSHWmBOB4ZolJZaH
+ 7LbGboddB7Pgg5ii7aI5PestuIlxti0bDni4
+ CT9y5NuSDkh0ZLANu87nPgyvFHk= )
+ 3600 NSEC d5.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ HrjH1Sls23W70YGVTC+oArCIolJJnkdXwmpd
+ mFIvA5DcxxXHL1Ts4VrN4basf+V2JO9Z+qJK
+ OwxzyqZNQ6RaAKfdMyi5r1KtXOiwXv2CT4OC
+ zkiwJ0LIPM/LfY1XYWfuscfwxTf9P0Ykno+f
+ /k6KRafBrVwW1FfsbvIXWk0KbFQ= )
+d5.example.com. 86400 IN CNAME d4.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ P27Ws10dDQwqUBOvgj35x+oiTRbeb66Bpm8h
+ nh0EX2ggiAThkXRivtUpq0YGoalowF6JeFbh
+ kwTlnLUkap7Si2fLl8oEWfXtjnn3nKfiwaT5
+ dsgtBr5SRnW11r9KmSPG8L0epZLrx1JErpKf
+ 2GeJ7DBRCV898dMBYZ0SzP7Hm3I= )
+ 3600 NSEC test.non-terminal.d5.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ yOkGOgkwjs2N+CjTu/wXW4UfEReQrlljcGFQ
+ IwzPykpswEIcRwNTzDEon2pVNjkPVWR9KrYS
+ 3pDMAC/mJ1AMRdpeYBa+V3gXYLHr4gRbn/+Z
+ g5YlPvOapiJzQ6fpSaH23wRzJUlQ3YBjCCE4
+ yLfBfoFWa/XFkqPJRxZn9oTv6fw= )
+test.non-terminal.d5.example.com. 86400 IN CNAME d4.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ IKorGneADWlr8jw1na29kWTMKsLt5DEHkLV8
+ rKr/uJQyOL95NFjpK+TAdcMq9Hpz5GGdyyOP
+ LT1fgGkwQCwWI+PS3+0LGBbqhHU8KQ5FAhOP
+ 62Dmnw/ZUkdGuyZAM0PgkPH7VSnChS2asa//
+ qiEM6+mkIAvRLo9PAqDTJoG2Z4Q= )
+ 3600 NSEC www.d5.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ fsiK64ApthpuFVaGIDfA+IZB+B7W6hESuNgv
+ lOybm0u1ErUP3GjbdbzGy02P82/pHbyRgDr9
+ 8lguywf+/otpRqiXvryZ1zl+dJG6SY/oIAEu
+ vssgVAwaYg27k8URhth1t++7H2O2cPQFdLj9
+ LT8wGBqCiMM/zju2tlvfsvL88pM= )
+www.d5.example.com. 86400 IN A 192.168.1.6
+ 86400 RRSIG A 5 4 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ vULD1Jbi59Qxhz03ulRXo80nB0txjAqC271K
+ 5wD/Rz8/6o3OtnM0F+ur7IIKJJQexl12HddX
+ sl+b55HZhfuLwerz9ogoSTIqaLZhWr/6kiDD
+ W64amx9Q8LModApfqhJtIwXIafAbYB9PvJiQ
+ vzG3On291i6e6vKFZlboEJrgh9s= )
+ 3600 NSEC test.non-terminal.www.d5.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 4 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ pecgp2ophbepdqcJs9nBnFBPh8Tkatu9SPq4
+ 9HAIC67BLvbWA5PvL6fWa8gWhc38pw3pcIYP
+ Xyy4UIkfQ5VB9X111xCx4IKPbLu2a5oAcM9/
+ DbhPEvica24qm3oPQ9BOK/kgtgIlHKxzxkCR
+ /X98KIc+AEtBmOgrVxZaxmoTKbU= )
+test.non-terminal.www.d5.example.com. 86400 IN A 192.168.1.6
+ 86400 RRSIG A 5 6 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ pgSJ/3TEinAqRRnYar7+7t3Hka00MnAEHTlO
+ GH9Wei52rUwMfvWJ8CG1kDKIEJXNm5ySMkYO
+ 3m0MAhfXgFzJB1e+SxDMDh7cM4ZWQH0QEL9o
+ HxvaWRo6w5hDuNhHvvX3F5Wcv69w+yCZmEiK
+ gZH/DXwsvPdhBKvCVUhV2Enkr/A= )
+ 3600 NSEC d50.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 6 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ L1V09ZExCtlcPEZ6Udz1Md/T2liWu0BH2cCO
+ SJCcV4hjsQx4gZ0487lDp1L2+AB7SVImpU0N
+ 5uqf0mG/j7fSdtYOfcTuYxasTHN0SY8icv9v
+ nZzz+IZc/RHLWVopF87yqD3JeWpdyOABHfVe
+ 0Or8sNWsEBwO0R6E0xZOckKZhYc= )
+test.non-terminal.d47.example.com. 86400 IN DNAME d48.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ APHibTalIK/3/jQ4N3rw2P65yx/8lPb8V5zo
+ CAQCj5W/dHiuZ6Pu1SQ8Z2qEoEsw37rEzJ1s
+ EHWNxua2aGh0pxaeLcT7Zjta8ZYkGMuKUniq
+ 4l54AaYYH4bBvdeKVTMTdMk+GML1sMx8YlWX
+ 53a9qrohmSLw2O5gs+5JsYr9JwI= )
+ 3600 NSEC d48.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ eTUXVw920hwLFlxNJfzIZxbSG28/LRZ/tSm/
+ 4F+PLW0CthEqxV1INI472E4+FL2C/zc5hhLi
+ oSK8ZNlXK9r57oCYNAWzba9GdtNWERAtQl9f
+ OdvvdRfn0rN2QXh4Up8CXvfeEWV5AMjCcXLl
+ 0IM7mpva55YbY0bAIKr2OOOFsXg= )
+d48.example.com. 86400 IN DNAME d49.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ graqGvlYjOAhU6i6T1nds4DyOiJAmbW0G2kh
+ oXE1f8rGZd5BoiVED27aIdqP5351Tb0PvAj9
+ IW7BhFVySiwHs9GZ5OAAj6vjW6DFYTlmdc4Y
+ GMTsEOlyKLGFCGTh/XepTLuWmIi4+LBuQeUK
+ c2kr0Ed/qKv1OJoCGnh0VUH6aho= )
+ 3600 NSEC test.non-terminal.d48.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ PxepWNMME35o1ChOQhQDt89YLWwLak8F3rq4
+ iRWnbIgmN/N/SRf8rksQdkCiV5w++OLqTtl0
+ yjK5Isv87r79WFwxInxCA0lKJagbO2udW05p
+ K61l3TE5Qtxtt+ByfZv5vBezFR0CKUBVGBfs
+ FV7K1R8LfAhzS0CbyfNwmEiuQlo= )
+d51.example.com. 86400 IN DNAME d52.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ob7SvAUXVsIZER3zbqaWDr+3cvglyQrshVTJ
+ e/zpf+qkxLrQ/xuspUrN4u69AIGBh8V8EQub
+ q1BwSIP8fSYweIdE5Vr8NZ2T4pT0H1qkZWgZ
+ ulB1N9eGlBw/93GZQ4ol7s3AINgVcWTo2UR/
+ zA0G/GgSJDn9W5DmS+JWM6+Mki4= )
+ 3600 NSEC test.non-terminal.d51.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ ukD2EC5ZSNIjqi+K27LKR5O3JFGK7Q192TxH
+ 8wQpCN8mBs5WbBdUcYoCbww3cF8MYEqF71/i
+ XKlAfZ90v0epp637aYIyCP3h4jo0cAUB3WSo
+ RlWpEEDoQKELaC54a7Igg9Fl5ppsK7JuA9KN
+ dgpolGwcDI8SGScSlBjKnEa9c+Q= )
+test.non-terminal.d51.example.com. 86400 IN DNAME d52.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ OHeLvB8FdcnIGHv10dwFvOygb/h950MtdpIZ
+ Qzd6UNAnuyhYfhcL430ve0B0NqDYUlyE+J8l
+ PFEcRbaXOHS9phNud49FqL2z2kxiRUzljH3L
+ YWYKKRCuIyiIVLcD/7N68dbhIh2w/DxoLDlT
+ Gl9Bt4sIqibKqLZvWCVlPhVUmkk= )
+ 3600 NSEC d52.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ l7KlwFlFAHjkfRS1xknFqZyFS5rQvZUfsAP6
+ 8SY6yHJ7uf8ObMwn5vnFNZmlU9lKuMoqrO3D
+ u6Rj+gTkYixs9h8+yWCuxa05CzpF4dBBqMQ5
+ acNoEPXdKjX9VNXNwQY1BQ6hj68nbTtB4sxm
+ nLuXPDhYJt1hRto4b5oNjEp8LTo= )
+d52.example.com. 86400 IN DNAME d53.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ pwTKX029yQ2Ds3kewJVumKJepAvBDCPg16In
+ xYY0c39EMn9M7KfnWcKwRIyi/6jwFH33ngRw
+ MldxPtyC0BrTxMU/3g8fbQwa4+td1RzXoqyh
+ P/TRyMYuZqg7pRvd3ZvFNE+BmLIK6Nl1tTu+
+ /5U55WiMTIFMLNBk4rszGI/yDek= )
+ 3600 NSEC test.non-terminal.d52.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ R3PUeC7YLueyfxokG213cfGUdOxH1GYv0Xtc
+ grLW9vGWWZPqbk+ihl2iFL4FnCGkxz844O9x
+ 9p40HblMFbP5m/ZMvfXkT7fRvO8I8JAlV8v1
+ xsE4pQZdfPm2qFjHBIweFekZ7xgcfGWK8m4s
+ qICF2jGxzTS60b9hVxl9hn2h+LY= )
+test.non-terminal.d52.example.com. 86400 IN DNAME d53.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ FKwgRwy3//wjuTOO38IJQzVX4QA7TKc7soDV
+ aGhLAzV/iJgvOqDR3aUWp2FkvXBZmFKz/uYr
+ 0ktN1L254Ld0yON8fGpb22nxiTjTjZ6VUn6f
+ OdPGHWQXWD0JkyVzopHbHZHvFFpn91ZiSb/9
+ 3OKrzWhd9amcnmYg4qAMtTbV7E8= )
+ 3600 NSEC d53.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ JtQfr/5qedGto/KadEIT956iI1MX6ThkTjMF
+ e09HJj/DDJx+N7oLUhdtpq+pDOobldHBGpvv
+ aiOmkQZrUt1eruLfhmbN1LnqerBXT+SzOKt8
+ 6SdXdyQv2FCRu6dzRxSgXbkdoxObWeZXrVMf
+ 4/jC6IiQF9V7EVQL0XoMULHOlYk= )
+d53.example.com. 86400 IN DNAME d54.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ wxWrxE4uGjPL1/qXJCoAf2Ya2ovHw0gDjEHZ
+ BnLwQ4FmCK0aa1OCg9WWLkWTaFHJWgo7+YeV
+ ksPSFfwJ14Lv9UG75mxgRyEs7HKYtsyfn5+x
+ 8Kl3F3/w7lTkr8kyh2iuPPPBIEjH0CdzcNV/
+ B/pyyJS/Tw9Ep9CrCJeXIJU3hLc= )
+ 3600 NSEC test.non-terminal.d53.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ tulemDQTMeBxeNlTmhl+iQ5277c+lmqlt1UA
+ HfvP0FYE/JIyDQ0EeYMZm6G4MiYAFipJkiSb
+ d1aE3nUqVqB5DyxAboUSVavr876HPaSaHmH1
+ tbHayHu+EOIlAoNp/TRysbtA088x9oEBNMzl
+ CMcFSwTqeIM/+sHYYFv20NIDrP8= )
+test.non-terminal.d53.example.com. 86400 IN DNAME d54.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Av/rhbzcoT5NMBy+W5gC42TGQACIfID+v2zR
+ 2AS34F0YctsV8nRqUweDc29neHertB5QavCR
+ cYOcjoq8FRU2Fv0nA2dq2sV29FOlbemSuIjk
+ mRurIyLQvYsM10X0XyIbspiE0WGUd/whY89R
+ xq5tV4d63bgScmPMThWkh2zrKT0= )
+ 3600 NSEC d54.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ jMjBoTAENoFkGGVC4ZYIwCB/B17yiFI/PGkP
+ PlAc6FqXAuJv5s4ANZ1ivmRC2S7xfoFhWcRa
+ Wi+rYsFA4HCXtd4XojYeF51mvwf1MJM3QKir
+ JIPxJmDriQzMlIPrA792GXbxy/eHm31JKOff
+ 4Lg6xBRrarbjGbLn52Wv6Qx9Upw= )
+d54.example.com. 86400 IN DNAME d55.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ tLcB809dF0SzNNkTDxQi2yUUvGKLLx80cylQ
+ 2Q+LQE0thrKWEtzoUhp7dojgyTLuZom0ZGOL
+ st91bI18DujPydi4UZuXI0n0z81uXrP9Jr6T
+ ks0MwWJWnavrZxqxMootxjB/2YMi8vCunKTe
+ YRotKTTfVh35OQATrmD/ixjfrzY= )
+ 3600 NSEC test.non-terminal.d54.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ R37llTMvvIym9/JyvIyndMruAaY7tYiAIADP
+ dx3xLb8tOd716299yCNWvjXb/1DROrRt9GD7
+ ZW23R7wVzsd+MIhmtM65z0JvhZaqSDwigW3V
+ r2MQmgwrw04PKpabMANu6eNoi6cv/lYbCyTh
+ FrLSwXkqcB1ITuC9r2cX1ab+yEY= )
+test.non-terminal.d54.example.com. 86400 IN DNAME d55.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ pJmpKXUwGwF9NVXTj7p0VCFgLBgPy4pFG7+R
+ Az7HiuVWRRhU3GOjE/WlP/4yYrDIHVkKUqIE
+ KqZcB6/H9INvWDDCl5QdFtNn0U+eR1POPLbL
+ QIzGYEBWMnBQyfbAPolXv5vXS+Y4lkpxgq3R
+ 6TvxrkY715Y+/XL/JbozYbTuTIE= )
+ 3600 NSEC d55.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ IlnT22tG+kP9KOvuD5BmA9340tK9Dg0SzS7u
+ 5hNa0OwkgHmlRkl8QQP+fszXqcV2w5iJXs2a
+ uPXgNYeUa1KPJVLMhjGuxhcQE7QO4htnZj/L
+ YGUvM+TcWSKvsxJJW+pWK8op2/Wryq4ZhxK9
+ rwrzpOpxWsrrM5qdbp3nSBojMKY= )
+d50.example.com. 86400 IN DNAME d51.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ K38j/kWSLO6dlsaj6zjwzAlTyzh+i/vBy9FF
+ 6piwjv5C/zTVMD1FJfib8lt59+3XXtHQLY4I
+ k75phzMefkNltxceDjajWizW23J/07pMfYFn
+ tkqaiMAPRAQ9n2IS9YGiE1pXGqUgDyNgguPo
+ u8W8FNXnDeLt4li38LFvA2iXJEg= )
+ 3600 NSEC test.non-terminal.d50.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ PPCtzDBJWAfEY8bjbhFkmMFdrPMcOdY5MIgq
+ xKd3inR1G8Dd51NiEAcjHp+Q7mMAyEUMf7n+
+ 4l8QjLnbRcBF73PPtOMr7TIX7jzYRYHRXHFT
+ /TJPhy92NmXqc6t2SrWWRB27vyDm91KDxACa
+ hdZFGjRPuHeWCE2OXVZ0F1ylBU0= )
+test.non-terminal.d50.example.com. 86400 IN DNAME d51.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ iBHaBt2XClcxB0nB4zKOinsruncTbD34bWTU
+ lXbKWRzY2muNaht8QrbkOcGp01GviPxexQar
+ oMpYD8ranSChSRDeBBBu6KcUSuAeWT36iRDV
+ 8trQAnMkf/pAg+Yto9eDsY1+Xae6Ji2sMWhJ
+ BjjmVKBLmNQVfRqpPABsmKmpnOA= )
+ 3600 NSEC d51.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ cVuYXx64JJdqXvauOdvMUuJqV3mKej/SSmIk
+ LZf1GRFEeRkcfTgsrNMJyJPYAaqSRLg9dX6I
+ rRrV0J47CcAMLmoknYmNYRqLLbDlV2rqfkre
+ msB9xYw1ip/r1kk3Aa7+7+oqcDyFh+UdkvsG
+ 8To9CaLe+Z4W/8XT5Ipef2FJP0o= )
+d56.example.com. 86400 IN DNAME d57.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ A5FrHt38MZ7HRVeOk65HFsY1//PGz+OAk17Z
+ g/LGytFcTEF04VHK/V6UthcWmgBr/bTfwwLz
+ MHPQThKv/a9BH1gi+xh0Z78s/Bqz5wYoyvwv
+ xKM4hP1ybXX5+ymVVwQ0mVKHml0KYMZKmSIq
+ o708k4fKUS8R8Da8gOAz3wNiLI8= )
+ 3600 NSEC test.non-terminal.d56.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Z/iyqtyhOATyFDbRwK/2vzwVG/s8wqT/XRC9
+ QPodJbFfR4veh+SNkJJH9vgl1plMpUPFrhr5
+ YZQZIltnh/XB4lknhH2hBbItDZzjntkv4RsZ
+ 1QDqveNMgCsJUgO+wcRggALI9evUzHT/6m/w
+ 9PyywulEFdV442oTHYRwU+GIabU= )
+test.non-terminal.d56.example.com. 86400 IN DNAME d57.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ FzVD7OlhI3F0ucWJpsiNcHSdjgESqsFDOIQl
+ fxMDdAx8dZwAjptkWMd0UFomLViP6XxnN5VR
+ zOa9kFm8tLsuGnThDWX/MJXEhCL3ZUIkQ4Qz
+ OI7rq/S7eJwCRsekvtNLz9t9o+Bdegp4aqAt
+ 8IS4j5pHN7UPFJGnWU3+izZ7tis= )
+ 3600 NSEC d57.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ KEtvHe8ziDkahA14gafEjTg3sunGX8ZiwSYy
+ ZzerUSPz5j1yYBhGNN3icENQlPSs8WVeGA7K
+ wDlA8uG0WsqxBeTzIwk+SWrnW7qrOek2zYr7
+ NGIVsnONs86+DarU4i+k2b4WTt356F9YHShN
+ 31QGI8dGYnuaSwQ3nvUWfHHCa98= )
+d57.example.com. 86400 IN DNAME d58.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ aepXpMXVir5N39tnsWWqa+TBIOL2O3wUGwxM
+ Gf0BKdCLffME8dSsjHDzePx2xqFRXmaO0Fqu
+ 02hHcUUS6gr/0QHvwdM3fag9c2JLZD4Ot2sD
+ g3dz2WlkeLDYIiJP7CX+WmtjBRGPIbDM/mMt
+ Gu435xsdUzey6hqAIeCnPRIbV4A= )
+ 3600 NSEC test.non-terminal.d57.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ XzgpBHxvhafDE3zHcgI5Lkm8gDmxqWk0IKGt
+ uiewi2Cd1CdflbCBKn+YW6KjWjCcGYc2LNva
+ lzOpL5XNc+2Dx2gp4jlb3VmNXh0oEetvqSRb
+ hjlo5REnuQAHiEQH9a2iQL5HCk4Er/PCIRs3
+ uoqpIU13gCCLdXONdEoxoQ8aQHQ= )
+test.non-terminal.d57.example.com. 86400 IN DNAME d58.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ wTn554M8BJc2A9xvTs0/f03p7Zi0wjetuFyN
+ Krlr1eqHxhaKP5xcBKxFBkO/DG2tDK1jlYOI
+ /hY4lxQnT0ucuYl8xkaXKOfitK8dN9iOxGpS
+ qA+EyXZpNQJVvyMYvVwOJoUKKlJRb0V8FcBY
+ oRiZXriPAVqx0+pmRHn7mWcAciA= )
+ 3600 NSEC d58.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ AZyh+Ul3lfZQS2sZZtd6QQZWYEPhyfm5ZKpw
+ iXpYww6dgGLsFfxo0NlXM9Ci1WZ5dOtNJN7T
+ F8ZwrDgMESlJ06f2wtfOWSK3CiK4TlTAvEYm
+ dbbvkjCxLOSPQa6NW/x3tHdKqX3oKxm6mQVz
+ CkcBIoXL+O7sWFYgoR5cHO4vAto= )
+d58.example.com. 86400 IN DNAME d59.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ g4KCW7ISDwBggwNDFTV1dQjGd6l2CMBOAMnD
+ B116Aa4VN6en5MO88tDH5KnaUehE40C5Fh5w
+ 7s3zrMSnDsIAEKS7Q5WVQzJYW7rCvH+l9uWN
+ ffG++8WK5AF8oS1jmx9Ai9B2r2TKkidaKOfF
+ WQohh582K3klHK8fVkeXfAO4XJA= )
+ 3600 NSEC test.non-terminal.d58.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Hzb6pV53Lgs3UeNl9PQSuK4vXH9Usif+AE3v
+ JUEXMot9sf/Yqj0YQ7bp2fi24CHtHkyJTBqL
+ xr0OWwghDiY1mzzc9f886O1UPRMaVw0yMhix
+ tGPtUrQAmlhxbCEkT+HL1FqS6X5VenRRStgr
+ hhejEInXnKcJvz/mu8X5iAybVyU= )
+test.non-terminal.d58.example.com. 86400 IN DNAME d59.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ RKs5QfnSUcAXKCRJ8cQwmzJGZQ4Fww9vwngJ
+ 6h4B1xDJ/XIchmupkI9ZyxOCcPNNwvI76Xmw
+ 9zJCiDzRc3vvleWmZ+WMntDzwn1LFGRgoS1z
+ qrol5HuJbnmLXFLHzDIi6xKdHhA9OH8pmMDf
+ VURAjLRl2vHd0uA+mG9VGWU192o= )
+ 3600 NSEC d59.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ pYVsbpS4N+m3AM2IjGVnEhaCyeW9PJKysg+g
+ CAar16agqOBZAFEQ9imO9u3zLYL68aS+X5B8
+ cjibKkux4jynHN979m0lZZKFXlxmuGRd2Hlk
+ EkbWW9a7nay8Rw9enNTk38vTX3MNcaNtufdc
+ FTHqnDIFzRm+DUvU74kfT7SC2y4= )
+d59.example.com. 86400 IN DNAME d60.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ HV77cJwpdWFAisXOgbIkS5iTQ/j0QFspwE8K
+ mpVT/VVgXFVJHcD4WQLnshNUFPtVE+TTEdWG
+ jj5iNZtNH7nnZMH8GxhXIAGdZLRIilTSlxuF
+ mQVoE2arlQ0MVjUKTWkJPTmlQtPAIPJMydJz
+ 2h4cW+gv8qbKjG8xe90lpN3ijVs= )
+ 3600 NSEC test.non-terminal.d59.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ wFxN38pgrGqoslSEI+p3oPvdMbP38YKekQSX
+ YCazFjuxyYQDpA+Q0EA1vZBvGEHy0VmlO0AS
+ IwHlN+DzuAWLqDi2sfIY/9vJkBHmRhs5KDV6
+ yQUELyH09q/0Nn3IHdz4PKLLN9NWKdwVdnvR
+ 3ZC/7OVPVyqMJk8ZxIVrUH9YSfE= )
+d55.example.com. 86400 IN DNAME d56.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ IiWLmG2Qz+QqrPwbd+PEXQcaupEmJSVE+AaI
+ sQFDdbAj9X6oQoKVwRMj7DFhWr2AlVotAfo+
+ UmQAH1hvTjqFS2gDi2Y3iKVQyQJ/2y8Htvze
+ xmvhdL6j5PjuZPEHQpJ2lZs7BDhuJj77Ydcw
+ oKPedUcHfIbX7LQY372oGiPpYZU= )
+ 3600 NSEC test.non-terminal.d55.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ xyV3SO1yg1FpRzB0l+tCOOlQo7JjJOIOFFyl
+ Ee1JIox27pDz7zRFjDg3/YWQ2KeKJx9noxOy
+ xaBdcpKrj4OUnmUFKU6NfH+fd6Kbq389+Xa+
+ fyPdGZRwjv7bVuP/neO5phkGUWwJ/kWHM7dx
+ XnkqZtKiMQNfWFRWiq6E2Jn+Iyc= )
+test.non-terminal.d55.example.com. 86400 IN DNAME d56.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ wTyWA8Zw2epn77Z/T/Oien5kGWld9yj4GnQX
+ uHDC5BbE6nwl/iOQIThVSfX44eUDQ6ZaFtP/
+ jdzQfAmjj00CZ9MNkMVO0OTvytIJn9m8n+Lz
+ Hllr3trcuRR5fXkLi/76f2bOW60Xa5vmBp4+
+ Y+G8r09NHh+8lsVrTlcodMyAF5I= )
+ 3600 NSEC d56.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ qlY1jFfkXadSPnSaFVGWH65YrjE2WkrVpt4u
+ taX76HBaJLXiZ6NZd1srPss7LI8m9nRW3gKS
+ s8BFjZfJ7tqYLXW2vLekYHBlKWd4A6VLTW/h
+ uxN1Pni8hPrSQPi88VVEymbsE+NqE2g4E6pi
+ euwOTylhs+oDvqMbNMNTjGpUtDI= )
+test.non-terminal.d70.example.com. 86400 IN DNAME dicarl.cnnic.cn.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ sJOusae4Tsw5x4toPAZaqoWO0l3LxNJUIoQc
+ cYeLVCdVNvqLMOzu0HpuV9H3RLHvp8vjBJBD
+ vB39ujHG5YBLRzV7Bllzy+2l69OrmmNhAo9g
+ sdz/7PhPCUkc/tedDrpCDUqmvA4LOE6OmF4i
+ zWUyC3701nIgp/8bDg5bQqCZSls= )
+ 3600 NSEC d72.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ HzbLK6MfElgWNL1klvwyL5Rezwx1JmKNfkBJ
+ tGT4xFwV/l1hNzx3MmcOgdI9vc8vw84RXPYq
+ vlCvfLt8HCtZmpULDxrTcK4+hzc9Rwd6sk2x
+ 2ZF7Z9oZFfND0o04qfBQummdTaU34YH/gzlS
+ xWiAFiy2LLe7YiSPEDLuBcHtjas= )
+d72.example.com. 86400 IN DNAME sub01.d72.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ oQsloDjou9E1NRfVb49eHUuKtsehFiUu/Uxw
+ AUoojfDtVWf1JgRpZv+Uh0EQyjky7U0+MmGK
+ Jio+9ws9Ek3oCJIdDVScdfg4EUHANcCt99Uu
+ ggRqr7VwBXiYz/vnAL2/7qJ9H+IajKwFZxUm
+ k3TUE8rItUT67+GbgjSvgacQTbs= )
+ 3600 NSEC test.non-terminal.d72.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ BzbybR57RmDRyn4oie1kfGuyX/AWGhCVqKLJ
+ iI09oGeR8qxaah1wnEt8J53nqTDA9u1nG2R5
+ XNEa8k9GPjuqHsVl7uI4+/2bAc4rSaU7KA54
+ EmeWw/5J/BZy2JBH3Fbao4eO6k70r+3DcxCV
+ 5Gg5Z4CSuEEnB0G+hHpYgiBojX8= )
+test.non-terminal.d72.example.com. 86400 IN DNAME sub01.d72.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ mTtEj+B0PvJH4o3sUvL9pWMXo7n5Kq1nTB99
+ 0trN+F1So91BjrGDznv/dZ7nkv+8QovFuyyL
+ QgKtl4hQFqaNaC73SpkChlAdKQO78JPE/N/s
+ vabju8PLRd2fYRk8C9qBGqF6bfxhvQgwtMa+
+ RpFDWytdzzbYeftcLK1O9Jfg8C0= )
+ 3600 NSEC NS2.demo.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ JUrLP2PHy3QHGcOTajs1lES2bVh1674iREdS
+ lWUPV77GIFycVPQnCrh69aEf3W/7Z3ih30sd
+ mAxRaKQRSVpofa9mOY8m6hIN8lF+fLFj+RNK
+ EKc2rn/jlYZAIIA0W0T5x2mKsNFMsCjxjwHJ
+ udsQ11DSbfrKhEiZZsd/fdAQHPU= )
+NS2.demo.example.com. 86400 IN A 218.241.108.15
+ 86400 RRSIG A 5 4 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ gEBkB1vm7CjYdB866+lGSyIRuwxiErInV4k4
+ Kpa7iJvyK4aJ2K9vqoG1wiralDK9HoeksKI7
+ 6/80+p1QSkqbeCz2ehrj5qxDvh+afQnWzh1p
+ Ah6/nZipnGT7ZZcxlYKXCVosStpwT+yvX90x
+ Qp1uKeXBGQIS3doSO5Lxnhc8I9E= )
+ 3600 NSEC AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 4 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ yhhP5MbObZRTlDKhvX85VX/1ca88R7yPUaJk
+ 6aiugigneW/uMi1ZjjVm/aoLufHhQYbcEJ+1
+ IeQW3xGDNl01dFfwZETizQoqOzpsQ/PtNYbE
+ Y3BaMYvGtUi2+JxPDkbTicRX1Xz1P++u3dQh
+ rPvHd0uHQtWMM1/cOAkd32/BxTM= )
+NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com. 86400 IN NS ns1.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com.
+ 3600 NSEC N.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com. NS RRSIG NSEC
+ 3600 RRSIG NSEC 5 7 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ efX0JgQvcHgH7Fh76nqS04/6f7i5Grz1p7lH
+ 6YeEyZyjl+v2r7ym7kdyp+kUg9Dbbv6RO2pl
+ juSFb3x2WNt4k4poUH3zveDY005N5teX93KT
+ Cklu2bw0fkJ8imo8hpNNvClp4TDXcCCDR/jP
+ 2hSBXnzA+AC1l8bkuMdyGsGnksM= )
+ns1.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com. 86400 IN A 192.168.2.2
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com. 86400 IN DNAME sub3.cnnic.cn.
+ 86400 RRSIG DNAME 5 7 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Pkj+vbKUhB3wDl/k2vnC63u3RCtPymJuy8PV
+ JLaNEQjf6svjS393FKrH4xvJjP78RnWeVYsd
+ um+wC5iOgYQEvyguZd3LwbzjkED8se2Sr29J
+ tGnHiKu6iNKCnjQBSz+G2IoL33vofuzAK9Dx
+ KXAIU7kDjcdpA7rOO/qouzG+Rtg= )
+ 3600 NSEC NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 7 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ xU36WQo+z1lj4DeMxU/kkzlVNvTEO56NiCrT
+ wI/eBiDTa/scpXsAgLjkDK98eR3sPDSbYJor
+ 22efGHKL3r7HE0JXgXg3QqaTtHYRx4aHVMvB
+ ui3BEgB7M6/fzq7M5a7NNsqm9bTzQ4kFkFWA
+ alN9kSbWRqZeTVY+WnH1cWHhKsk= )
+ns1.N.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com. 86400 IN A 192.168.2.3
+N.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com. 86400 IN NS ns1.N.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.G.H.I.J.K.L.M.example.com.
+ 3600 NSEC mail.example.com. NS RRSIG NSEC
+ 3600 RRSIG NSEC 5 121 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Cvi2ndHGWcwRYdPZTCehM7WmYht+7+ZRyFs6
+ qr51uwvfaSwaepBghslFUx6OaNPlFCx2SH3h
+ Dc3WGKG5L4Vf+diFCbbyQJd9T/ZJ9XLyamJx
+ s70tGx6HCtn5WLSuK8t+ER7mkazaW4wkhyT5
+ 5eec49Q+PrVErHs0sTh7QmR3bo0= )
+mail.example.com. 86400 IN A 10.4.0.1
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ E3BIWNPrCMlws0hchvEmK08cxmnk/Sthe6Ha
+ KMTkqSW1GvYqopHNkT306tpznyRPCquz/5Xg
+ fr4h0ME4Ii+Sgdc3cQ3y57FckehktyOSCUv4
+ /eq+4ZNCIU8CEpM3GPif/+6Jw6YJNdo10psi
+ P5BFduWTcf4D2wkArfyeKFX8zf0= )
+ 3600 NSEC test.non-terminal.mail.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Ex1fByxHWTiEUvk+SvIHtClMf1tpaLZbBkU0
+ Uvvb6cscstzgBviip+LxNQwvP75DkXIIUu5A
+ YWFIYcFZATQQiUPhXeQxjh50pTRhhyA16Krz
+ urrNuK5asKiAaZqJ9FZ1CfYfgfhAfsUr00SF
+ CV+oaAFLTuzycM56Bs6hhpbQEfg= )
+test.non-terminal.d59.example.com. 86400 IN DNAME d60.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ k2ouJMhtyyV4zQdY5tPxavUC4O/5bhzABxmd
+ 2HRsU5rAh9fwQ5dP3pSegTVXnUDaEOAWO3Rz
+ D2OfUsQcJcBwqJk4aM8vjimqFOKuIGy5A6SL
+ ByNjum882LA8c85utl7+dE/F8cdMcIS+mW4U
+ kRJ1M9N54GoYTNFVhNpn+O3lXkg= )
+ 3600 NSEC d70.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ WXa6gm/fCfzC+cCJ4FHLyuph6UmvtBoikli2
+ xDRHwcr7sfIHEPMBsWkSiwSh0ooibDcnxc9b
+ 1Q9KMiU63eyVwnYrWa08ZBiNfeGU5MFICoR6
+ t3flIl+7PDQw78pbYm7piiO4gxS2AC7fBn5Y
+ KftJNCWiBVnroQFC+aBZ6lVZQbE= )
+d70.example.com. 86400 IN DNAME dicarl.cnnic.cn.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ MEfjKN9rAmiv6FWIBmFRwpyUhlgej/s74oZE
+ nLuniJl5qdiOHLsCM7C7Koo8tYaUlU9k9wsX
+ ARFReBoUQ/E+u9eWUKMzkg2je1bR5mieNVYp
+ 3l/xGO79gcLK8sp3fsHxWMeVAw7ec6svCsjm
+ g1ysEcuNN1A8GbAjzyaTeKE1MYI= )
+ 3600 NSEC test.non-terminal.d70.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ vzGn9aTaLqQfvuZRf3NP2Q+2lROavGKDkeuc
+ Gzm4/2oGOtv537VRyNh1W5VMA3hooxFMoxf4
+ OCeTaxU0TowduMT33qpxEW5amPZ0H2KpWi94
+ Sks5qX+p17352SlXrUmn1PpUtlw4SiAcTykd
+ IGyk9SyB3EDkoxL19QKB9PwzlVw= )
+test.non-terminal.mail1.example.com. 86400 IN A 10.4.1.1
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ WgbJfoitNOZcm06k+IK+DiJEaSC5G3SYR+k5
+ kyWsovzaB9AKqqzTz4BZHNmEK8MuzBv7fo14
+ 6dB4LuR38KdiZsrM1rpE+CaeS/qc3JxihFwT
+ I0MkQTNaYzXaJlVwacFMOFix20e0X4tIe8as
+ iSEui2GAfwLt5zoEsC5Ul9J63Qs= )
+ 3600 NSEC mail10.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ vE0IY559z+QzSelb3Bj7d/jreDH6RpGzM0Yy
+ zJJrvc7apgzCRP7VDya8liZQEpsjRmAsarEm
+ GdZSv0uKrLz0HHBsykDdR7cp3bBg8v9QDtHB
+ RQG4ed2Rpi5B7ycdx6HXEa0n2ZLhZk90D6oZ
+ ytyx77Z+jG7Z/me3RI5TymxAH+E= )
+mail10.example.com. 86400 IN A 10.4.1.10
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ wv7NomK60P/RCdDsS1eUBrtI00kEsu/NJyhJ
+ VJM4HFVrYXOcKjEfl5lXM/fJBv4LlAMhuzFD
+ 2MziNeRr4crbzVt6vAYzQq/2CWBZDniiJIKd
+ EakNNliNu1BDVr64lm1gDDs3+a/2hWL2E7XK
+ XeZXphgYquug2L1dtTRbW27G1ZU= )
+ 3600 NSEC test.non-terminal.mail10.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Yc1N7vRqNUhxvcPwS0GnjofDzyGa1h+gHTZz
+ TkG/RYSeDeUz13jXtsVB1/ZhpA94cEn5DR5x
+ 3Pi31mr61Zsb1zKI4q3NsbM7vTor32gbzbj8
+ CZHVwMjkpd7AOiPAk14q9iREPjPYCC2+mMD0
+ OcNs6HssNzvC/DrhwRWPwz4nfGw= )
+test.non-terminal.mail10.example.com. 86400 IN A 10.4.1.10
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ oBEDA9mWjn4OiB0u1eSerGPp7A5g31dX7bod
+ ieAtEu4C3Lnnrpkcem6iS6XZN2d9D2M7TTO4
+ kYho5fytEHFu5kTfmtWuBirf1DoAV3rcpqY5
+ Sbc8NZE03uJ4jKuTHbNOdLuk8Gr78apm1sB4
+ MC1thf7n62h033OrfcYwwobwBRQ= )
+ 3600 NSEC mail11.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Um60SGEk2RxUQK5GNZ09duBxDi0iTuIslKXa
+ Wi55L5/oHvXR/MM7VqNWnyGmJNBPz7Oqe7V8
+ i7GA+Z/sbkj/Gs7Nnzimf712UlxxpF/rTv23
+ 3VZw6/MLw40eIXVFkymOLIeYzt9zb5+53RVh
+ bpPsYviQ1mOCmw2gQ25VNJ9rPHg= )
+mail11.example.com. 86400 IN A 10.4.1.11
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ XaQmxG9oM6lGDSbNNOG73U8NeV/3kdRHHsvF
+ 9Ja+sl6phtEY6M8ErMTsxzmWkqxfJ9GvjWdr
+ ivfv8/SjtWiLMZJQhigpruMU916sfK1X06LY
+ NVbUhtVJm5HNhQ2CVKyZFRWbidfXnm7SJFNL
+ 1hFGKNWTF/DRlPK0bsEDqG2DhnQ= )
+ 3600 NSEC test.non-terminal.mail11.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ repU5qTAzTYlOJwTesJkEzZrQ1qc4mWVqHf0
+ lNcjzKIoN7aGplQ6SW/MCuJMRfAWWK2HLFse
+ 2jXwU8tEDfvr7tuv2CZioN7WuDE7PpjNJzwn
+ sxdaiEBZSWDL9kSacQwyNVwOjL1WMxHSuuRm
+ DzNkWMhC4xNLCFHKrdoEPWk3gj8= )
+test.non-terminal.mail11.example.com. 86400 IN A 10.4.1.11
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ RM30OrGcHqGperkip7zqX/3bGYfdwRIkzgsG
+ Yn3ewcaG1eVmu8peZg86Z+9jljSy26oy8kan
+ 9//kn67W9tyBfglj223vVhU4QHlRl7AQxMc5
+ 7VDUQj9pHfXh0x3hLo07lwta4H8ednxLFHnN
+ l3GR6bJyPbE/AzXfhIFLiT+SKpI= )
+ 3600 NSEC mail12.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ BbVJnGuGXOC9hc7wIhcOGFz4eH2sfqK4fFhc
+ m4fEt6H6Gn8X1iXA+4czMkNqQv+Y4w744SSn
+ idOj9W7xj19zAp5uzmviFZUyUeQ08JOSxaE9
+ w1immavwVVInaqoDzBUF3k/EQO6ZlPRklZlc
+ stMHTIU1GmdqRcLUedHTUDGs+HI= )
+mail12.example.com. 86400 IN A 10.4.1.12
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ULkNZR9GJ3jNYk6tMgA4pL9mzqw8fuRSNUYd
+ bdRFNvbbosxxz/7A5k4Eyf9t3daJXQ2BOoI7
+ ior+ioy8HtsbkUhkhTwYJ/LYUmUif2J7jWMx
+ 67I1IFXDbWXEqv+iV10nnknKL8nJk8a1c7xt
+ 3285LqsQXhifGD4nvbXOIwTYNZE= )
+ 3600 NSEC test.non-terminal.mail12.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ kViShVqq51fKGo+KUhc460FRF1PmfDaXtSZo
+ JEe0ds7jP1jdoIMs6rt/M0NH1lg0gcmbhlsE
+ DEnClu8HEqCsp6lbz1TgNeqYNgkQDqmyZggk
+ mXyjEDtQ4xql8XfzbkX00dX5u9ACPZIkmvKV
+ 4iQS3bLGiCQg1w5WlPGfOZTl7KY= )
+test.non-terminal.mail12.example.com. 86400 IN A 10.4.1.12
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ lYsqWwKz6k0JDzTw8T95/MAnXbaODOB/Ab5M
+ zkmjC3jWKDgOnlvQrMlYf8BqE0HJb63vX0px
+ vAX2TKPGvQ+wEB1fed22nqw7DYjzfuNzOaOt
+ 0ELyEzlNAwQ7NDSkT85ng1l1qEzPKYIzlgc0
+ L97wHcnPv7Urj/igApMX/8NkQ74= )
+ 3600 NSEC mail13.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ PU/IhwdYZcKTwIIwSxlcuen+TWvG2gRp42QF
+ B/Fldaec83BSWKmq9GbXuccIFoHNKMTArnfX
+ 9uuIP4xLqkXkDUYD00iaNhpRw1Jffbeoh3Fy
+ JSH9aY0xW4Y3osobMQr08iavv987OhbNKiYy
+ v/fPjuz0iYm9/1HoJLupZUsfeZs= )
+test.non-terminal.mail.example.com. 86400 IN A 10.4.0.1
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ DnPjHjEhdqCWkgzAPRifXUGDx3g9V6yJt5j+
+ EdYQRCt++GDbEF2Ynl71Vd/zpTacCUWYps+K
+ WabrsUhsjMHIPoipa2xjobIfjrsqfo8pqrMx
+ RZtCt8fZyxK7NLczKU/Ie+apcoOYnDMW1AbN
+ lSL+h8c3HiZ1DHQdFPq8D2BA3LY= )
+ 3600 NSEC mail1.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ qNtQsc3FtsLw2H+d8zCCslSYohsV2ofLlmyK
+ 9dBVxWmMkVcPBpfx+4b1fjV8SL/+SHUtVPA0
+ 2Yx8S1BIRcOMBuM2ENJNKKExfCAHLf0dqOvd
+ mZZe7z1TTgMkeBCAG5lOZhsZDg5F2jAPYOeC
+ b/YHwPsRrPWh7p6Ai96af4BmuAQ= )
+mail1.example.com. 86400 IN A 10.4.1.1
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ eFsMrAmwRL0R5sbyaZrkr4vfQi9RvbkYObM6
+ rqNpfDjw6eOwvxLm8oZFr12oCGI3xBw6mgHT
+ pF1l7y24pZoCnGpg4Q1ag+TBVgvZuFfEcJAk
+ KALe8a9BhF20Uvs0Ec+BiKcu7ed7irAic71x
+ C7qanobK/CJ0P4PX+hiW9skHGV4= )
+ 3600 NSEC test.non-terminal.mail1.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ mRwBgE7mSYb9F5TixnzooW/4dvwLq8zEn1/C
+ HIGSimObo2B94n/Ef5NQ1kf1gqvfyn+ZP1HW
+ 8hvDLeo1paMJd8xmqknIjOcV+hcjntTid38T
+ BgHAurPSRjiy4MQARJGWc2EuHnxt0QqAjs+H
+ MhDoIi1II4LAxJZyWTSyWthL1YY= )
+mail14.example.com. 86400 IN A 10.4.1.14
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ H14+atsKW8ZWNd8JZ36TkuBFZhXdkJUThLzW
+ c8gqBsErgeeZMxcUC5ygm3//cI++yJVMD1Bx
+ HTu3b7fksTbJGvX3UdhVTzGdR9YvJYi9D2Xn
+ cafKBE+9ETotMM62piNdmCY/lVEURI00/yvk
+ 6mXZYH76sDNi6i/deK6SmMpHWv4= )
+ 3600 NSEC test.non-terminal.mail14.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ WxINgY9cqH55Isi0HHR23e9i4eKjvdw2lv4t
+ QYXV2NUS1sFTrEIpCGaBMKYwKh8xkSM8fuL7
+ bpMzvG3emC4Rg0n9tizYCNjsfTCQNoFNvpqj
+ 2fKuWxUqV6gjzTb6KjlKOSfx5SW9L5Sg0Tc2
+ GV5b2QHiW0e847ABx2dql++GF6E= )
+test.non-terminal.mail14.example.com. 86400 IN A 10.4.1.14
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ J//po6XMFoHBXbqh6v2zbK3aUovMyDggAYj/
+ o7eDkEihai8nWowXtpMbxys/1dm/l0WWyBCF
+ GaO7vLLxkRt6F7phAKbcAV2yJF4U64nnvIrV
+ aHFWF3fkdEN78t8CHtl+aYi37878in4t9Yvw
+ ALGkXfLx+UG2+0TxpzQ3mGfto74= )
+ 3600 NSEC mail15.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ coWx1XV3F2ZUu50ZiDU+5msSuqZwpZxvBBys
+ TSj8+jo/sPDSA/ilZSss+WxGOHrhppalbpcX
+ TnLxEhmPHPGUf6NLfB5+N0v0Pf1HyhXq8Z7g
+ ULFqYCVK16MQkSGuYxX7maN8/XtosxX+ZrF1
+ 7SeJqpHzWFTPIiO+gTrAAXSJHak= )
+mail15.example.com. 86400 IN A 10.4.1.15
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ YP2OUilH+GET+E3E5YWCCslWg0rCQi4xIIGi
+ FnagBLB+XNA4L6ciizUxKgFdZ/Z0C7Op7X4/
+ cLEMSsOtCGgYaNX0EqdT4oXk652+mL0yKs5y
+ T24PzldUd5Zh/iUCCZ6uhjoO1xTMSsKqZnyz
+ 6DIHk6OPqbjV4I3tEIaWgQ5qaAQ= )
+ 3600 NSEC test.non-terminal.mail15.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ tZ6C9X+QdFelME6sS9gz/aIUBM3qst73Km1f
+ 0qkA3/P09OA0hlFzFSebDDwq6mpFyVbi9eNZ
+ PtCdYo/yU8Ijn9Hnb3aazkcPUQk4CI6wkAzE
+ mQNpj7rwzvMdBmQbRAdwUGbdVoJTRbvjcZ8S
+ 7VYEHp2V78IC6+yDrxT93dKgXTA= )
+test.non-terminal.mail15.example.com. 86400 IN A 10.4.1.15
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ nWQkiRRvlHtXeb//24mAa2yBryERD0bvWw1l
+ 7oL39AemUF+MVHmHc9xAYqahzizK1SAmd9ax
+ Q26D2ipGfDDAvcLkZ7w80QiPWELBCC09O+2R
+ lUXr1n1XopyT0m7DTFYg4BH1c2xwrqMrAqyi
+ frjEhuKvdnPucunHpOzhJaLd6+c= )
+ 3600 NSEC mail16.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ czIMj3JdbgqmUC0i7Q5qxCO7YlYwjag6dPN8
+ eT3mQnWoM5njhxVh4WTonM3Lh29BhZdYMdkM
+ u3Ba8xG4JFASCO21EqArHjrOsEr8h0RVNT0g
+ BXY5NEUXH5IjCnqePI0DTrgFf374HoAAK5ve
+ cwAtWtWQl20AeRBrFB1auzqme/Q= )
+mail16.example.com. 86400 IN A 10.4.1.16
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ QKYCaMEqGKQmVHWNriSBcVRwuqtHkxGIfaC9
+ WQdNL/Mbt5zGxS/6v1W8MQtRpWHWrWOZBvNn
+ RjfNta+GNjI88yY3qe/Q1S9RQsWdLA3I4XKT
+ +Y50ekCI/+qtxi6yhhHFKTSQK4DvPskkX+9D
+ UkhYhnzrqlp7zUQl1RxV9aQJDrM= )
+ 3600 NSEC test.non-terminal.mail16.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ az7+23AJg24nymox8k5AB5VlGa0Zvplh9K9i
+ qkAYdvVR4qQK4Ugd7B9gUxVeWiPy4sN8S8IW
+ YgBnuskgy/Ly99DAQ1wqmkJdFpIzW0oC7P+Q
+ qFEViacIEwtmfGHczJvNtkeXQBHS2dgglxHj
+ 7Ih37xOkph2NdStunkIxNfReV78= )
+test.non-terminal.mail16.example.com. 86400 IN A 10.4.1.16
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ TJGi0Oa4+d7VIPH3n/oFToOXxLKISZaByKjA
+ s8gqdx8iw6D6ce6ces97fWQWY4QysYFmxKsU
+ zxefahGr2NW3NwOqN5kCyP5hzbN5bvzPia0Q
+ wPvZowOcotDzd8E2igxRdna3hAa5UxV6BaEt
+ G/qsalYLs5Sd5n63gaMk7zD26hw= )
+ 3600 NSEC mail17.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ V4WYG/l1Peo34Q5hfm69QwRtUszRoZ2JjYdq
+ d1wQqZBoXO79SXU3TssrH77iL4hTZNxug9AC
+ MVWbr14KIok8kVgOTiHcuoD8MEvXOYldVT2n
+ PHur9u98YWUl67tIHxFJaEDllUr+F/MSphIj
+ xDE+JoILqyqQxiBplivpYQpKd54= )
+mail17.example.com. 86400 IN A 10.4.1.17
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ tP5Kusdk8U8sk73CRE5h4OzGQA5xX867rStw
+ vnmLO7QxUwnUt86iHTA0WLZ9tUSU48e1QgA3
+ sXdfsE2s9lL3RkBCOeZBbtVaEoJra4lrsQqE
+ 33/lJ2dEXinmUp8V8ndMrDZjoM2akMWqil8B
+ retyu8+6sDBRDq7DwOo6DnhXZpU= )
+ 3600 NSEC test.non-terminal.mail17.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ rLomo4w8U3IUOk3Z/VM0cq8Wf/O2w7/ZWvVx
+ a6b9nWBEfkDPkhLNHMN+bb99SMepeWKZEbNB
+ aM73aSqbebSFwdc2LLBAqQ0tN8H3ay08FEK0
+ RkZUncMkUWuAwdWePDPyy/OWllJCuMJCOP1w
+ 9fVbQTm+N8pIQGE+8VbwKb6CR1Y= )
+mail13.example.com. 86400 IN A 10.4.1.13
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ekti6VPO+1/EmlifNgbhfoQK76lcypNefc6J
+ F2TLJvWqiVAObvbLGamIu2gXtyZjY3SZtkL0
+ DN4gkao68jLi57kmqQq8QB9TYUjV3Sk9q1KF
+ T6IVYs3hMXtVftUf5F48fvY8MiciDX2ef9hI
+ DDAzlju7Xm+28VThG+4HdLGOABw= )
+ 3600 NSEC test.non-terminal.mail13.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Ofh4YmlpGvaFnSfgxt40TCB2tXJj1ni4qE/i
+ ZKO58Yk6Yhewoh9PnFu+zY1iqqiOFdkwm1p8
+ 0ZnBv8zEnBgyWlaDGlyXYaekbopPR2RCNVCz
+ zBDfTUkV/ZH/9jHW52vxuCTEXJbfyDtoMpSH
+ bpywxlsz4qUl2LsTwUXln7XWj0M= )
+test.non-terminal.mail13.example.com. 86400 IN A 10.4.1.13
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ RF1mMLuNWPr/PlRrXFMfOa5OpOzrkmpPwACm
+ K5Q31EJxO0ypnWSR09YzAkltDVutHLM6tEvF
+ 2r4kPeAPMoNe6dh30QMQp51knE6Ltcqp/2AJ
+ g4g7w2V9I0cwRnVzIvpSvZESJKKWEpjFB1qe
+ T/QwCOMDUKEKyVeQmk3/qaJbS4c= )
+ 3600 NSEC mail14.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ DTP8SAm5AK+j54fkHzEuAkWdlJ9+y3nioDsU
+ Gc+UlH0iDZJ0sw5xRbzFK/p3eO2aYbOQqyCX
+ 2J15B3KMCcUNRRe22nkHS3zEeQqY7vfo3xW9
+ U5CBg7PY2EmuZ/nwj7oP8RE21SQj7NaIC5nn
+ pej4vBhEFWqPC1fsebCp4vxil1I= )
+test.non-terminal.mail18.example.com. 86400 IN A 10.4.1.18
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ cDlI0qI//7Wy1+EE5Cd0Xvbj/mtk55jC1ZIy
+ 0m9TwV8M7vMKv7Z88w35YzpkOlZAbvZrOk4Q
+ dTXjaT1KVVW4s3zn0ZGSHjMyiZu3XeDmFJGg
+ CM8FxsrzwemZmVrWgLRkvo/iM8h+rkMM0c6F
+ xSMHyQS+cETpPCiLe6XoBtrWEpU= )
+ 3600 NSEC mail19.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ SXveX1Oea1X38bLeIwQ8XqKyeCPhiJ7VMhnm
+ sj8VCrIVAl3opZt4TeV4HhbEO8OTHORb30o0
+ ruRxRgTiIPkjiPsyJ29jSklyL6BiN32HTJGz
+ dUzNOQLaa1BVQn4Z1/vafMfABamOv1nkpdwz
+ D2xC96J2A3dfgtYcOw6Z9uH471Q= )
+mail19.example.com. 86400 IN A 10.4.1.19
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ PsTOjwnxY1kNxu7MfaFZ68LjNqq5DzFBtU6/
+ 6L6VshN9VKaP9XmlvSbj2TxWNitcK7159y1w
+ Bo8O3E1xr710wOvjeEoEt7zRBxaZKhHO+WfO
+ xpsP2d24rwaQXt61hP2rREow5DjCT0lHXUm7
+ /stCotu0+AisTQSf4UrJSQcos/U= )
+ 3600 NSEC test.non-terminal.mail19.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ lnY1TXd46C+A9ScdyYn3AZNg0h5xUk3DvKUS
+ OG5uDVgM4Ox/i2aIlAcnrtb+YlYKDTi4dZQi
+ CIoCSsvahVnB3rtkyqmNYps/wD+31FukUN4w
+ KJY1IJSSBOd9TpuOyOMVOZk4a8ewJCReJLQ7
+ fdQdaxc8wz+VIWX5m6E8I47I76Q= )
+test.non-terminal.mail19.example.com. 86400 IN A 10.4.1.19
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ rhR31JvAxtmNDYbh+BEc/7r409c4+c+54nnm
+ tUlHy8mAT6m2N55avXOvjHXCifhlChCDoToC
+ fzehwMH/PaV5BqarR4f+IESsCeXWbpgreQGp
+ pDAvFKoQSMSG6tHb/oURZz5D9Nj27hi0yW7Q
+ +0Js3qKbkzAVPi4qCAg2qCA83Fs= )
+ 3600 NSEC mail2.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ SGOyr7LJYoaqqRT44xDjqwJ7l8+3SkVZylDG
+ b1Uk4D66k2QoEb0382hXIou8OylOYFu6tKha
+ CsSL9CjDq4gP7XVar9sCwqHnRiO84cEO4TKQ
+ rWKK7aofN+N7bGJojEPMIcfMjAQyVEj0DijZ
+ Bz+vfbMr/3sGHpjwYyDfi5QOXwY= )
+mail2.example.com. 86400 IN A 10.4.1.2
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ lxfw+OaPPVMAePmo+8Tj/037dN3b9bwL7Vrq
+ eUfzJQDfXX5lcxxfUvKWZWaBYu3+SGQcA/iv
+ MfX7dU85urSeHfDrtzmL70v/ltJVv2vShg8a
+ D/WbPtACA0i7AQloTV0iRUQElwP9H+VtaI/h
+ F169y8UouYvhrxVJpkIfGsKy5bE= )
+ 3600 NSEC test.non-terminal.mail2.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ yRRrHYIrXUzNO48M6ol/YKQDsDscuCXD/ZTc
+ qhd4SjVCRYwmPa4bFWSgrZUlOLEOIBZ1EvZm
+ HYh52Hq50xhzGZp6sH9oew7jp+obrEqFgE6P
+ pcZb4pSiM0r+Jtqzs8PeNjuoC/nKXvpbN6yK
+ AMjbhKymox3kOBqGd7Bd5nKR+SY= )
+test.non-terminal.mail2.example.com. 86400 IN A 10.4.1.2
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ M98D8qNyCROc7XGZZK0wXCaU61Gvgn19ABeX
+ 8OKXt9N27a0P5khTQ2I/ANqixKdjMPTXAEtn
+ RbCLhE9m/Ffrcslb+6q7gmIquehlrSeHgovM
+ 3Z/KdPtCmn12CASmXTV+SglLjPi3zpqSGXeU
+ NVEo+1BK4/VjaA7vwFJOEGNRvjM= )
+ 3600 NSEC mail20.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ UqXAJQ0qmNqF7Pi9ugPXdlAzYEextshmHi+0
+ d8Ex7jXeMis2X62F191k7f0h6OY/sAxjbugy
+ utGhqDS37xObZIf1pIYYQrI6q2DJtU0tLLBK
+ v9V8lOEcaAya2btbevjSFYk/p0R8lsY7kjLd
+ FtqLs/ca8d/R635/bh1kuaTXZO4= )
+mail20.example.com. 86400 IN A 10.4.1.20
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ k33/p4b8hiJjrapigEjN5y19hANXjA2jOZWM
+ QLJDh15G6kTxiPIWmj0sS9+77fF01LYEO+wX
+ KFr5+EHDvYiNMqr99sjpxkKymkDem996xKSZ
+ pbH6YDRvTppF7rkL/wmG7hmO1RDwGyginyJr
+ V549ykQ6+1As5qcM1aUFwGqUWHA= )
+ 3600 NSEC test.non-terminal.mail20.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ bNuO49PAGLOyE8ACYFdaYW6QJMfP2MbCd5ct
+ M+gBo4LzsSiSkni+T+hMro142fHJiJAmYA+L
+ 3rv2+nT+DZWXplwY+AMz9KW7vATRSFlRlFfF
+ PZ1fc/TNmsauPIHARZnMxc5g3P/c09wN8n0r
+ IKp1unynOUpKKtZBGsvK2zJMwms= )
+test.non-terminal.mail20.example.com. 86400 IN A 10.4.1.20
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ AduBecIyU6OkPrmM2rpdxLm0FKPDJzw90w4z
+ 70qUdGH/hJPHg3b0kmD0WVnlbI2FNWD6juZS
+ Zd+bAriXFxxg3jIOoWthGcX7ndWqJQ4MUk1W
+ 5PeKOgyA+o+DtUBymKKS1C2G1DQmOcitWOD1
+ pOo1jawNJ8PrL7r8psS6PEv9VSA= )
+ 3600 NSEC mail3.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ NFPdkaP+u974qr1A28LJKjF1kOTF/bN1pM+W
+ J8uSC2ZuRFGrJtKYC/IgbkiaZA4wG0OiQzbv
+ 9UWu87hETfGszTPfvHhSsgQPbw0D3SVFx2MR
+ xqX2Jejet+eS/IBitiTntqUJUKTkBIcVZ57b
+ N72RUhhR6LIzd2l6HCnZWKdtISY= )
+test.non-terminal.mail17.example.com. 86400 IN A 10.4.1.17
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ T8gcprOnmnCYj+oa2agvk5UxQ1SIN46p8GvE
+ PV2+d+oLaruxIRf50Q1aCVaa3qtXa5cKZglc
+ 6OPTDykCNYPZ8Qkaqbauq9XFWEu0jEg+Ko1F
+ FWpPHKesHQjRQu9/XsJq3198aDp1JF+Od4CP
+ /3c4EF4a76jD2WHltHU+2tYPDAw= )
+ 3600 NSEC mail18.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ xSnml2cHIp7DLeiODjrm4CckTM+eGhUketW4
+ qzlptyPht+BcBY6r7QGSsD2XMd71gLLC5zFX
+ 3Ih5suA24CsXJRRf1McMaAlsLj3W/043SIE6
+ QT1l1WVRkaAYZAwlK/ZVwWvWXCzAFaMAwl0l
+ NGg4GKljzo1VKej7mxQEepfkIKU= )
+mail18.example.com. 86400 IN A 10.4.1.18
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ pgMM8YR5BN5sS2FZqHQkJTUrXI5fwIDP1K6S
+ p30INrhtV7ZzuJftLPhU9iA0+pgR4uCLKYT8
+ bzVoQOg+ISgxWkMUr1pkmEA8fQIJqakIJAp3
+ laU2Jz5Egd0ILQk/ZwhVTBDsnHC91BWaFCTd
+ u0sw5DTlEC4Jw4rd53lrROJgtSI= )
+ 3600 NSEC test.non-terminal.mail18.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ OvjZKUeOwOhFF29tGvHGlad1j7tnED8t2EFX
+ uwKw2u5e8ALW6/gI6qD5/cXlHxV/fV3Mmydo
+ Do9XZRpbRzj/+GSc01nda3hS89X/c1vdxEqh
+ KOqzmukIahWcb6OdSxabzQBxUMX3Zn6xpNeH
+ 7ZF0cuPuBbyg1+D6V12h72xtIoU= )
+mail4.example.com. 86400 IN A 10.4.1.4
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ PiMqiEaBWJ+5AX50Vx/Nt+8ZW8aX9qJ/2yTo
+ TFUWr/x+UBN4JuTIdslKRCMFUaaKRsRE6zx3
+ pN/oAwGUU4tavBiPoL4oPK7gX/DotyELK219
+ PCG2coShjUsrjEXoWs0G0Tcczf/zH1SnRkL1
+ /s3HvjZqmgPkmqGx2kjpnp4QMcY= )
+ 3600 NSEC test.non-terminal.mail4.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ zcsX93LwEFg9doHsO0wAS5EWhvQ+FoFiFRju
+ dNmipFXcoZ337K+5Q13gSW0XR8xBCMtZhNjI
+ WVxem1NbB87UOTwynp8ybHv3FqwtCzhOaCXy
+ qpVWZnrxJCyFsmWjmL654YAvO5yc/hm9NVTo
+ mLQRJmkeGXHYHdtoILaN8ZYpn44= )
+test.non-terminal.mail4.example.com. 86400 IN A 10.4.1.4
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ qI6qajPmldqEI8dtJ2aylv/j5PoTb+UD/xsw
+ lpcYvpBbk1ZJQXraWTAKvytlTf1tOswha+c+
+ +uQcUzsGKdlIO7pFIoNTbf2UciUkYzlw87bj
+ 0IqdLws0Orq7beDMRD57bxYAcocqsg7JcK+b
+ QOkx7ThU+vzO3PlqhpCPrIm+XV0= )
+ 3600 NSEC mail5.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ hyyqXEMBD0wr1EiuayMANDLRjWuWqOCJi2uD
+ s+NNfwvPzCArxkjhVG1n8Gxv7ByDiCsGj3k7
+ q0Y8cSX3XItqdmF2ltKkvpjMLmBN5OB71ahs
+ rM3nFS/hp6Kq3NQpiy3qnl+TYxKVflqHroIb
+ yMeD7xDseHZg1WelNYq5SKIOlkY= )
+mail5.example.com. 86400 IN A 10.4.1.5
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ BwjGy/pQq8wwq2oWeo8IjkZWnXmxU9F4Aiui
+ V7enbF3/e5Ls0/aNFFH1/C+BiX6ehulxCLkF
+ 8Npgg98+UMVgHEkuqcph7zHivTuArO94M4XN
+ zC0ANphLEac93//GtrZcI/QC1mIxQrC9+Kuw
+ RK5swzlI8jEWvn4Gncw0KVVsZMw= )
+ 3600 NSEC test.non-terminal.mail5.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ kFxxuuQI4s4rutL6doGHWWzh3xa56l6Q8b3n
+ OMMeh6QcAkc2POP1iK2/GAgWyymgaF5J16+f
+ qptFjboQl7XeusiFt539Xxjqd7kyE2H2m5dZ
+ WZqeoYI6jeQy4RYEaQkji+wm4w/kJ5tT2jil
+ 5pwyqkQx10xnvnx+wT52xv2Hifw= )
+test.non-terminal.mail5.example.com. 86400 IN A 10.4.1.5
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ nQ1Rf5Lhe1y+CE91nOrgW9KqRf5k5wZmp2jk
+ DuvgTJ+p3BPRXOhyVVxGwi90N4grlutXkIu3
+ I/FgibSaUSmoPQvZrmsxfwB+tckPXCYp+AP1
+ U9iLrA36lJ6vpkniV2aGfxxkxyFt8xGVWy6z
+ xlMN3OrofQ4417d0wyCMyJIsi94= )
+ 3600 NSEC mail6.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ XWNdpdSMLhscZuHwpjTOQly+5b+EeQLGkxU4
+ kB2+UmdB8t+bk0kPCC0Fn/iUDu+iVxasow2i
+ F5GDBJTtqCdfXZI4uAb0Difwsf2O8q5H2ljc
+ jSQpv56iYt36XxobLWiVe02dgixVEIoTZGQR
+ qr/nSkPSGkQZV3t92CO7bXvFtZ0= )
+mail6.example.com. 86400 IN A 10.4.1.6
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ U311WSJmd9YNtNw9VMXSApDFH2VnzY0fc09B
+ NAiQYo7Gj0NCvIAK/0bL74pZjBEg1oqSoNG2
+ QcaHA+1HY+8jvkEii1kRVAxP03BZvW2U0RoP
+ eExCk/WyNhyPf+E6rujFEjkM7BiD+43dNeX9
+ Uuw9enf9Lge1jO7oqE3c6Uc0cGw= )
+ 3600 NSEC test.non-terminal.mail6.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ oFMJmWpaSlpIZrkmW5PKp/ZEsmVtDrNYSAbr
+ gGHDUUt0+ZmVfHAX0tqTDdFg+kaKQ5J3BN1j
+ 8yAIBtMocPzefaRvI+vU628lXQvve6wezZMd
+ 0gQPURodkeSf/s85hMFhkxVx8jkapiHisxPe
+ veVdhCswAvCDhWcLToLTQyDVKoo= )
+test.non-terminal.mail6.example.com. 86400 IN A 10.4.1.6
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ hlxRsoZQnLv7Vd84GLMWWbvpeXI+WmNU/vGM
+ k7vMqloFdK5VbNiV1UU55RAszL+mfxFQiLLH
+ /bxRSxqpwaQ9n2HdnB7EfpmSk7CthVhjOMv9
+ 5C5zB4V1Zq8t3EUkoEgrXULoMRCJ5kjkHbUv
+ BuwCwvcwO8dzhy/kkfR6CU9E2OA= )
+ 3600 NSEC mail7.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ iTb4Nb6g7r6GgAMmgr7+b5eYscku9X4mhjIz
+ XjEj69qxTYjk3hDtX/eEC3SFGP7MDqqwbFH5
+ rNEO1Z4dE9o4P0D8CcYHi1hjCnQcBaYKpy8k
+ d82DaVXkxeuKPSTHBNApBL6IItcfsR5F7IYV
+ 3MIZd1DqdNNbWroZD0r9S0UZ5xI= )
+mail7.example.com. 86400 IN A 10.4.1.7
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ xq5d9fK4T5Byh0DDoU9ng3S3ODGKagJFHuGk
+ AZ7F4gaHRuu65To0mqLR7EYcpIMHT1N0qIfw
+ pig2GWtcfG8hnhM2Sg4Ecec1Spq8MnRcfEVP
+ /taJWtTc+uFPz26Ib9PBZcFEusQO0uObH1rz
+ Y/+3sI9150q2hVo/9VYQdG7Dw4M= )
+ 3600 NSEC test.non-terminal.mail7.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ eVx1EXYO0mkWdvbBM0bsPC7cyA3XmDzMSR9j
+ c+ljmJjE2tGyPJlJHoyqSrxPe+ZCLPKxMvMe
+ svILL090OysHffdCnNUHbJv5P36XrDMtP2AY
+ RhLFmQfY4z3bu1WWIEZmt9gNSwSDD0wJlS/P
+ gx9IyKwYzbYgI8RMq3A78iCnzd0= )
+test.non-terminal.mail7.example.com. 86400 IN A 10.4.1.7
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ IJUdllCwxGAuk5DFWKXf2UeJyoSU4UezntSQ
+ bKFHK2MCx6DgvfQMGkHVsQpobQGiThyW+Up5
+ UZULM85mtYzNaW2FcZ5inBgNk07+ulhBRYSV
+ yfoJMh22hvL2B9kUrq9ahdxQEXwAi2EXsx9Z
+ IqY3x1GT6sTiRr075QFGKsm533E= )
+ 3600 NSEC mail8.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ H9QW8lTHfPL9+68q1K2UDhxB+GBfvf3OHSVT
+ XGrc/ZeLRozSTdv0kgEw+fMtZEnNKjQ6FkSK
+ IT4KRjpzfgPPerFDSdPnpRLB92oexIqW+4HL
+ VyUqvndhJHQhtuI+6oUds5kKP7wtx0SJNtlM
+ FFApNNKJbNXqGSgdcMH0VX6X3xw= )
+mail3.example.com. 86400 IN A 10.4.1.3
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ HY2Bj1RiTEJ50RwIVeFIHtltIqSe0Ag8+hRV
+ 2nkT0XJyfyYJgUzmyhQZXyifwmVbA9GB+ikr
+ 48W8YExB0tz/0LKQ/7ZnAJIVeIqZNxTHH+8E
+ lTtdY0BgVb1jbGwVjMPPP2PMtHIV6js3SOKf
+ etKGzHGtwPKLJ5zJQ9JIqGLAcb0= )
+ 3600 NSEC test.non-terminal.mail3.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ dDmzEDtwj1sY+OIx45+eR//i/eXqgDTK0BvV
+ wXTGD7twq/4wJm98L0zwVf9nBWvPBVuPxbes
+ RRkYQMUDsrzbRe6nwlMOzBq8A+vkgjPg0jPf
+ XGXjf+VuBhLkfthA/zGtIbydaZD6+9GQZcuT
+ Z2PmsUcE7tLSUETrGUfzWLUx+N0= )
+test.non-terminal.mail3.example.com. 86400 IN A 10.4.1.3
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ WK8oxOqRtx+WvmlPwLqzk9cg4i/edlo9Q18m
+ c3WSKOcl8k9PIsGLdmR15S/0FwPE8RAwZj5V
+ ivNvD1jZPji4FUd5uqDOIIEf8I7h/oJXtICt
+ fuHXBta451J9hHax2CQsbdRAmgOPePKumLjE
+ DjWcuIDlwd5fUUWlSNJXGgaGqB4= )
+ 3600 NSEC mail4.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ JlLVYj66GZBi5EzNOj+dNuNbhAzNfETJEM+v
+ HEKoxHIMQycdQIE+qIRnslLTOiS+mGcNqvuM
+ QwDh/pX0T/aJfv2iNavo6Kteo1mFqjC6WdlA
+ dwKHgGQUaFpITdhlLdoloBljJ+s8qf0XQu51
+ prk95kMz7J+P5HDfISD1NUiM2CQ= )
+mail9.example.com. 86400 IN A 10.4.1.9
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ilueEhVsd566HjAQ9OKBjKllUmR34IIsUt+d
+ gDJHfac3bkLpoFTaONpUWHFzzGdMk+1qJz1h
+ 1JUlaJhUBZaAVS1YmTtnVhtEe0ir8RlJWDti
+ Jps6xnL0NnWUJn9OHVHyi+4bpewBuCRCL1oN
+ G53y0Ie2BtaQ0gKfxOv81i7PZoA= )
+ 3600 NSEC test.non-terminal.mail9.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ lOFQtp7jpHPGoWz9OzviTsXFBHUQs244Gvh4
+ p4DTipDsg0RaQiyK9klKigNabJh2j5hrt+lx
+ q/bvlur6lfUijW593HWEAgCck8wQrHPgg3HE
+ vxwBrxIbueuxuxOT6QOYH6lBzzG4HDf6LSFM
+ +zCoYsYXXtbEI7rxqcd/WieiFqM= )
+test.non-terminal.mail9.example.com. 86400 IN A 10.4.1.9
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ FVGEqnhagAxIPSX0Nim/AR7ePP5GwBEnvCsu
+ oRpgnIgpCcBKJrKPqlF/aOF4/dFtysXvbQCW
+ Tt0MSFEfQsDFcVOj6bQ/jZq4oAvZW/TBG7na
+ z0J4pTckegIPzuzVso+PtLHbONxu5Ts+IyyO
+ Ble0Jz86L58IamZKXRHlYkV4LOk= )
+ 3600 NSEC minittl.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Tj6rYC12mMnPrPa2wDOp09e9cuByqjvO+y1i
+ riH51BzIMDEUEeaDYCQfPWnJtReNPOoxjeA1
+ 4RQbyLtv40A8Wylwq5U3lHrHO5iB4hWTm/Qe
+ vhbzdmrxPYKWhZUgamb22UMY/mPk5O9iMeQA
+ adOVtlQr+vxZHLfP9st/M5FD464= )
+minittl.example.com. 100 IN A 10.10.1.1
+ 100 RRSIG A 5 3 100 20110408061651 (
+ 20110309061651 27366 example.com.
+ Mf8cekGVYrEFZW3FXsta3pbcxONMxlfpxveF
+ yvKxd4RmEa3a+LW7AANsVSCLXdret4r1hFcE
+ +Cz6XSw+HcIcF+3tvcQRqpHbtu+ZBZVafIHc
+ jlvCpw8vaGr9TL9+KiFSPxZvh7f65vd7oUlR
+ 0IJfkPLsRiKLq0TFwooxXX48w0s= )
+ 3600 NSEC test.non-terminal.minittl.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ sbqJlXF9wG3NiLwQFhKu+LCEx5E/S567MXwU
+ l4845BDvl2xUr+UK35xIVrAzbKk8baYtc6Ad
+ YFEx7CAE6wc++U8WdON4ZZtMaFt9v4ckBAAm
+ /El1zvZ6LhzhkzQTtt8fx++g7iUfNRfJeuBw
+ iVe00wSg1cRchlP4B0QtaNw37nI= )
+test.non-terminal.minittl.example.com. 100 IN A 10.10.1.1
+ 100 RRSIG A 5 5 100 20110408061651 (
+ 20110309061651 27366 example.com.
+ LwMLnZZyz34kJ8AstokH/TxMvAGtaOO04D3J
+ 2k5HauNrJCtluPYbDELm+b7ngEc/wOsHhu5c
+ PPdWRWrmhUCz/sEcw0W0MD6lgEVd57GTEAim
+ W7gEDp6EcQt9f2g9LUxuGS4g9YoeHucENI+n
+ swfDBEypCOzz22ajeqic37jgiqA= )
+ 3600 NSEC multiple-type-a-record.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Fy9D+zHXn4q5C3Ypa+oYjOwAD/SyI7PW/AWC
+ WlbPBIYIeDMWfToE2FNahRpV448K+D9QGsyK
+ rpQCGqyZoK4VGw+SMvmwIowQ0kP3vfS9kjLM
+ bGtd2ZZo894qa1AwHMjfC6HJKSfg117l+Rfb
+ rQR1g2B9dSq8vTRMHPBFhEMqZRE= )
+multiple-type-a-record.example.com. 86400 IN A 192.168.2.1
+ 86400 IN A 192.168.2.2
+ 86400 IN A 192.168.2.3
+ 86400 IN A 192.168.2.4
+ 86400 IN A 192.168.2.5
+ 86400 IN A 192.168.2.6
+ 86400 IN A 192.168.2.7
+ 86400 IN A 192.168.2.8
+ 86400 IN A 192.168.2.9
+ 86400 IN A 192.168.2.10
+ 86400 IN A 192.168.2.11
+ 86400 IN A 192.168.2.12
+ 86400 IN A 192.168.2.13
+ 86400 IN A 192.168.2.14
+ 86400 IN A 192.168.2.15
+ 86400 IN A 192.168.2.16
+ 86400 IN A 192.168.2.17
+ 86400 IN A 192.168.2.18
+ 86400 IN A 192.168.2.19
+ 86400 IN A 192.168.2.20
+ 86400 IN A 192.168.2.21
+ 86400 IN A 192.168.2.22
+ 86400 IN A 192.168.2.23
+ 86400 IN A 192.168.2.24
+ 86400 IN A 192.168.2.25
+ 86400 IN A 192.168.2.26
+ 86400 IN A 192.168.2.27
+ 86400 IN A 192.168.2.28
+ 86400 IN A 192.168.2.29
+ 86400 IN A 192.168.2.30
+ 86400 IN A 192.168.2.31
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ YT07B2OmuwkPtDW5FUPj4iy7ucdoTKahQKWT
+ 8KGn9vGwdbVLZXQ0L0rtvv7SO6SiDsYbDmEX
+ MxCp5Ecs58A0FCpIPhP9yKhPOLHA2UKMAch3
+ FfdujPf7SK0P/TxE2l1zQ2wF7pD/dUuV3DLf
+ uCuIetcVYC7AYJy8xQKi951ubYg= )
+ 3600 NSEC test.non-terminal.multiple-type-a-record.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ d6wmda+yHwrLO6zvpOxegvusUWmIwW/QqPwi
+ dLc6+IcunjlFPd6/fiHa9SThJv+Dh9rssiFQ
+ iLpi7tO7sPZlDdmJoXJMXlV0zapSs9ACxdiY
+ vWDbZfFcGdCeN2/FqjqCQ6WnCnFf+R4ahGTx
+ KJkcsKfPVAYSbDuDItUZ13yxPq4= )
+test.non-terminal.multiple-type-a-record.example.com. 86400 IN A 192.168.2.1
+ 86400 IN A 192.168.2.2
+ 86400 IN A 192.168.2.3
+ 86400 IN A 192.168.2.4
+ 86400 IN A 192.168.2.5
+ 86400 IN A 192.168.2.6
+ 86400 IN A 192.168.2.7
+ 86400 IN A 192.168.2.8
+ 86400 IN A 192.168.2.9
+ 86400 IN A 192.168.2.10
+ 86400 IN A 192.168.2.11
+ 86400 IN A 192.168.2.12
+ 86400 IN A 192.168.2.13
+ 86400 IN A 192.168.2.14
+ 86400 IN A 192.168.2.15
+ 86400 IN A 192.168.2.16
+ 86400 IN A 192.168.2.17
+ 86400 IN A 192.168.2.18
+ 86400 IN A 192.168.2.19
+ 86400 IN A 192.168.2.20
+ 86400 IN A 192.168.2.21
+ 86400 IN A 192.168.2.22
+ 86400 IN A 192.168.2.23
+ 86400 IN A 192.168.2.24
+ 86400 IN A 192.168.2.25
+ 86400 IN A 192.168.2.26
+ 86400 IN A 192.168.2.27
+ 86400 IN A 192.168.2.28
+ 86400 IN A 192.168.2.29
+ 86400 IN A 192.168.2.30
+ 86400 IN A 192.168.2.31
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ iYwlsJGf+LDo9qHyfQbjh6+fEqliq7qTyiFB
+ aUdSRT/RHoJ2dWXxrkxxJ2uo5xMnVyKxlfHT
+ mXXwUSrUU1Swfi3ROh7n4+9P+T8Z9wvmTLSp
+ VBDIyn98uKqacA4Oyj5TOLUuu1hHRfqJ5lfM
+ O1Aml9wQ/MMtPr9XjiK99+dHnPg= )
+ 3600 NSEC multiple-type-aaaa-record.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ a6PDPkS1fCfOwM5+GDtEdRK9MfR9bgd4NMj0
+ Hnb5ZdIX+aRUrEykFGvhdKHmoae4nvagOLfC
+ rd0fMCZJqia4JLOh6b4Z52qf/yDyraceadFF
+ A7bcrLFCdKcumJqUZMs1XEkBuIdThtcHG/S9
+ VtRbtoeVIGCrHj1cA2seUFM1RZ0= )
+multiple-type-aaaa-record.example.com. 86400 IN AAAA aaaa::1
+ 86400 IN AAAA aaaa::2
+ 86400 IN AAAA aaaa::3
+ 86400 IN AAAA aaaa::4
+ 86400 IN AAAA aaaa::5
+ 86400 IN AAAA aaaa::6
+ 86400 IN AAAA aaaa::7
+ 86400 IN AAAA aaaa::8
+ 86400 IN AAAA aaaa::9
+ 86400 IN AAAA aaaa::10
+ 86400 IN AAAA aaaa::11
+ 86400 IN AAAA aaaa::12
+ 86400 IN AAAA aaaa::13
+ 86400 IN AAAA aaaa::14
+ 86400 IN AAAA aaaa::15
+ 86400 IN AAAA aaaa::16
+ 86400 IN AAAA aaaa::17
+ 86400 IN AAAA aaaa::18
+ 86400 IN AAAA aaaa::19
+ 86400 IN AAAA aaaa::20
+ 86400 IN AAAA aaaa::21
+ 86400 IN AAAA aaaa::22
+ 86400 IN AAAA aaaa::23
+ 86400 IN AAAA aaaa::24
+ 86400 IN AAAA aaaa::25
+ 86400 IN AAAA aaaa::26
+ 86400 IN AAAA aaaa::27
+ 86400 IN AAAA aaaa::28
+ 86400 IN AAAA aaaa::29
+ 86400 IN AAAA aaaa::30
+ 86400 IN AAAA aaaa::31
+ 86400 RRSIG AAAA 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ x8x8CvwAHG+JZBh9u2x/0DaiiP94QpQZGhXM
+ jTbtwBMphbStQ7nic1hj6PbAf9lD+aS5hYuo
+ vUc4Ef1wCUdvQ7bzzgFPChzWJ1cMQf+qD8OX
+ pUYUbMklFReEIOA6M/0qeD4XaWuq4NedHTVF
+ Kks4D2u5QBHksqAFguW+k23I8uE= )
+ 3600 NSEC test.non-terminal.multiple-type-aaaa-record.example.com. AAAA RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ hnQieVeyJETv0V8rau8a8CXJuALihgviFDAj
+ EctzT7D+97HFGkmQJuwoTIIZMl65AlV4p/tP
+ dDoT0FnjahNeA/+ifEGqpSb5nM4J46SipBEl
+ yc7gXwYvF3VzthY1JtXFIwz6/EhIj9Ypoh3H
+ dtRVcSwNe9Ts1LQen4MJGXojlis= )
+test.non-terminal.multiple-type-aaaa-record.example.com. 86400 IN AAAA aaaa::1
+ 86400 IN AAAA aaaa::2
+ 86400 IN AAAA aaaa::3
+ 86400 IN AAAA aaaa::4
+ 86400 IN AAAA aaaa::5
+ 86400 IN AAAA aaaa::6
+ 86400 IN AAAA aaaa::7
+ 86400 IN AAAA aaaa::8
+ 86400 IN AAAA aaaa::9
+ 86400 IN AAAA aaaa::10
+ 86400 IN AAAA aaaa::11
+ 86400 IN AAAA aaaa::12
+ 86400 IN AAAA aaaa::13
+ 86400 IN AAAA aaaa::14
+ 86400 IN AAAA aaaa::15
+ 86400 IN AAAA aaaa::16
+ 86400 IN AAAA aaaa::17
+ 86400 IN AAAA aaaa::18
+ 86400 IN AAAA aaaa::19
+ 86400 IN AAAA aaaa::20
+ 86400 IN AAAA aaaa::21
+ 86400 IN AAAA aaaa::22
+ 86400 IN AAAA aaaa::23
+ 86400 IN AAAA aaaa::24
+ 86400 IN AAAA aaaa::25
+ 86400 IN AAAA aaaa::26
+ 86400 IN AAAA aaaa::27
+ 86400 IN AAAA aaaa::28
+ 86400 IN AAAA aaaa::29
+ 86400 IN AAAA aaaa::30
+ 86400 IN AAAA aaaa::31
+ 86400 RRSIG AAAA 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ szXkzygL5I0RgjQ9CEDYm0yeFS0bpwk1EgO7
+ cYnf2i9PxX5npLk9WRZVLsFLsHmj2eRgXvsb
+ AADYxOzSwdEIZeqBgsy7Eoee8qYntoSpXOM0
+ QqyVKp/4pVP3JAW9a+j6SyjboR2dXJFY/9XG
+ Ox8BkwtrlAsW5/2tLT8mD9jfjYk= )
+ 3600 NSEC multiple-type-apl-record.example.com. AAAA RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ DjrQLbKKPBy1hB8LWgIO8BlFonO2LktUIigM
+ wTmzaWMgAgiKaw3BHZwYsbKzP0BqAb5FcS92
+ 9R6rx7yvd1x3ykx0AjctR1Teno8yqenlErNr
+ eY/0aU1PLgnnnqGMJEc/Kv0vuQfqWrzJDXDH
+ Vz0SIfn56DArnR/+k/BD/OfD3yg= )
+mail8.example.com. 86400 IN A 10.4.1.8
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ oOjuOj5G4jOjwHN2NRy7j+jpbsDF+bw5sVjn
+ UKSi+WENwlhZODEtJENVVoRB1ri7B47w3JV4
+ GagwVUWVeYmbalk38hRtKD1Ak9C2o1cAyv+p
+ BR3YazWd7mYqxhdvu/n2E4D7IB5+MUSZ2LUT
+ YPnrsCta+BfEobMTA8kUplMj6XA= )
+ 3600 NSEC test.non-terminal.mail8.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ yCUwmQfvAQblEsFsNiPxnhCVKhjjZTeBFLPN
+ cES2e3gA6cy23sHmujvkSsUhl04ZceNmqZE8
+ UzZL5YFNemGy+GHAOnvY03L+4d52pAIsH/33
+ OC8KmOAPXySnMvAkIF4nv32gJIuCjB5Qn1LU
+ IlxyAMSe7ZOaxfcc9d5AvkA0xKA= )
+test.non-terminal.mail8.example.com. 86400 IN A 10.4.1.8
+ 86400 RRSIG A 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ MuQ87eA65dY5dqnUVkoau0U0AMOYEPDciAmm
+ oULk/zlvcHpPuxEcHh0oIB3jvzYIc4BqXDyJ
+ fc7JKXhwWJnVI9A4AkUNebdfPA+WkR/LtOpE
+ 8lmu2gPxRWdkWuDflmE/e6xuPoK5oTz0see4
+ vGgzEEYtuK2PNJo2WoJQL4jVfps= )
+ 3600 NSEC mail9.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ z0uNfSHz4i4TJhgEkY8QWkDu+Nqa/ngzQ3rZ
+ 7SWCVhMQxROnFSF6l9U67oxzariVRobR0Bt4
+ sv/ufpa3j68bm28B1JjbZEUkIVteAtu2mSgN
+ s9jfb2c/NVdacVX/qtr8d/9boq426im3OvEq
+ bEsv4DbQTyzTjQIBFJmfztZ7AIE= )
+multiple-type-cert-record.example.com. 86400 IN CERT 65534 65535 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45Ik
+ skceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVf
+ SCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY= )
+ 86400 IN CERT 65533 65534 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45Ik
+ skceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVf
+ SCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY= )
+ 86400 IN CERT 65532 65533 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45Ik
+ skceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVf
+ SCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY= )
+ 86400 IN CERT 65531 65532 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45Ik
+ skceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVf
+ SCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY= )
+ 86400 IN CERT 65530 65531 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45Ik
+ skceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVf
+ SCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY= )
+ 86400 RRSIG CERT 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ xQoPoH5wwAOIiZspU1WKMKdO4QoVyW0BolZX
+ 5Tdi7VW/tD/HYBJsJ6qm+YsfMEs4gMFjkUTy
+ oxIMi14lDk4CvKV7g6jpn1XReRJQHM4sFrcN
+ /Rnr7xLWQicNi3jCIdHwQiGAon/ld9+dhQwi
+ P/DffsAYU8XHCnrB+vBUF1bGSlA= )
+ 3600 NSEC test.non-terminal.multiple-type-cert-record.example.com. CERT RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Q6w8eQLJK6pucvBPhxcDis2mEmPfH0cvzFvy
+ Rq37BEk3qPqE9VzvnMlWOfcR0D/f1zychP0y
+ PwSxRSX56+IiBnAUczMMI1ESiy0CMpWFI8Cw
+ kuJeVcDlBbqzgK6T6aRs08dwfos9V2WcQczt
+ um4kDs1iP6hu95xoIXOD5AaF23c= )
+test.non-terminal.multiple-type-cert-record.example.com. 86400 IN CERT 65534 65535 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45Ik
+ skceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVf
+ SCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY= )
+ 86400 IN CERT 65533 65534 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45Ik
+ skceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVf
+ SCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY= )
+ 86400 IN CERT 65532 65533 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45Ik
+ skceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVf
+ SCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY= )
+ 86400 IN CERT 65531 65532 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45Ik
+ skceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVf
+ SCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY= )
+ 86400 IN CERT 65530 65531 PRIVATEOID (
+ MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45Ik
+ skceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVf
+ SCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY= )
+ 86400 RRSIG CERT 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ OZzzum87ApOeJSFbd7wDbmr4iY/3iAKbh8RQ
+ UNJDBPwHj8KbJlttJ9gmjToSyk3P7SrbS7U7
+ Pk/2i38keOWZVW9ivcksmt0ghx2x3I1FP63g
+ aH8ch+7Xhuh5Jchg0Kce1+5mXpVu+j22qfPQ
+ 1cPyG/Ret1LaUhrSkXMzsPAMbCc= )
+ 3600 NSEC multiple-type-dhcid-record.example.com. CERT RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ WopCB1qLP8JM8dH0Rg7ahT5KpZ1PrkjfzQTd
+ okVyD/OeeIuA0AtUZ92DqMvUu3HfHHp/Dt52
+ ROAavolZh7nXqULc770QeysyeHvBOjY+H0F5
+ yZCjUKFm8GH0+1LPQsYnZTE8yrCNWflb6qTk
+ saBhkD1SnfAaLkY9fjCQRcXvZ3g= )
+multiple-type-dhcid-record.example.com. 3600 IN NSEC test.non-terminal.multiple-type-dhcid-record.example.com. RRSIG NSEC DHCID
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ IrzmyShLB6KgAaQAz5AOWMBW8Ww74oWgoWry
+ DP89FY0GOn8RdXfeBGTNVCLuZO46l6JBdcYz
+ 0YRoSp2EsfUzcEP7ykNpKdUiIt8cu3bO//w6
+ jViWdm7O5yWvGVgk03o6xJMqsSsqqXXx07PC
+ s8oICZkWDc/ivWe9EZm+N/nCoxo= )
+ 86400 DHCID ( AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( BBIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( CCIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( DDIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( EEIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( FFIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( GGIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( HHIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( IIIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( JJIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( KKIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 48755 116 32
+ 86400 RRSIG DHCID 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ zFbrcp3DpDdFEj6IWGhneuND7eyaKu+2Sx15
+ ol0dEX8HlFqmojg95PruZxCO/iUlT0azgfEc
+ zAmztRiVU/YSCW6JjMb/7t8BU9XvISyMQvqB
+ IQjKKtmkHBz4QkZOp+KhDG2u1lCXNsx6SNNd
+ TI0MBFI2uOs6r8SvwVVyaKT+9Yc= )
+test.non-terminal.multiple-type-dhcid-record.example.com. 3600 IN NSEC multiple-type-hinfo-record.example.com. RRSIG NSEC DHCID
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ D2ft8CG25E0aXD1OQUvZAyQwEUwrgQwubVc2
+ 745OhUK74XrisajtydaXeo1lcY63/YugBhMn
+ fbOO7dAdmsA5PcUmEUN6YPF64F9fci4SBmoh
+ I2VMWpQl5wDGMJJlCTh//UsYMgVxitsf2ilr
+ /d4c5zpd4KWZiCMx7va4MgAUyoo= )
+ 86400 DHCID ( AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( BBIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( CCIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( DDIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( EEIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( FFIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( GGIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( HHIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( IIIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( JJIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 35 0 32
+ 86400 DHCID ( KKIBY2/AuCccgoJbsaxcQc9TUapptP69lOjx
+ fNuVAA2kjEA= ) ; 48712 84 32
+ 86400 RRSIG DHCID 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ lY/VinZHODSBjAKVarQgP6t/W6ELmy8nrFN5
+ MmAxDY7hEniet6b9Pj2ZfJwXuxCLCaBjK533
+ CflXFrMUhRQ2ou0cROzx5SYFaHvXjkTYBNjx
+ 1Wyq9/oymCoR7wvWC8oVnQ19C+xn6bQaJqMK
+ ALeaYlArNpnRFCba8Y6rXBLWFOw= )
+multiple-type-hinfo-record.example.com. 86400 IN HINFO "DG MV-4000" "1990"
+ 86400 IN HINFO "DG MV-4001" "1990"
+ 86400 IN HINFO "DG MV-4002" "1990"
+ 86400 IN HINFO "DG MV-4003" "1990"
+ 86400 IN HINFO "DG MV-4004" "1990"
+ 86400 IN HINFO "DG MV-4005" "1990"
+ 86400 IN HINFO "DG MV-4006" "1990"
+ 86400 IN HINFO "DG MV-4007" "1990"
+ 86400 IN HINFO "DG MV-4008" "1990"
+ 86400 IN HINFO "DG MV-4009" "1990"
+ 86400 IN HINFO "DG MV-4010" "1990"
+ 86400 IN HINFO "DG MV-4011" "1990"
+ 86400 IN HINFO "DG MV-4012" "1990"
+ 86400 IN HINFO "DG MV-4013" "1990"
+ 86400 IN HINFO "DG MV-4014" "1990"
+ 86400 IN HINFO "DG MV-4015" "1990"
+ 86400 IN HINFO "DG MV-4016" "1990"
+ 86400 RRSIG HINFO 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ UwdNrH/kb403kMqM5iogfB1a0VrA77U/kqEc
+ kyUgUuOmX4rDM7n4de2ZF8/llmqqM0bGBoiS
+ CjqpxIiflbq56pLEgWC4GmzoSIKtWZOKepZJ
+ QYS9zjAEr8r65S64XtrBSCC9bSEHrPVVl0GD
+ lHJLWBBs/8bPkOFevrbIHOjrmAg= )
+ 3600 NSEC test.non-terminal.multiple-type-hinfo-record.example.com. HINFO RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ wxkUhmJ2GrC/KbBuuaLO2AcSm7dprDcbbBmJ
+ BmxuJ4QDjp1PCWbhzo3GlAcQ83zDbi2JQMWS
+ XFq3/6SJCJ0U0K5F6mSVd0MKJgcbHBvMUdIj
+ 46FxLCRjDjN3+Ej2A6egcmVcVxbmolVcbwS7
+ jLm/xMZHUHFs8sIuCJGDyZLFBns= )
+test.non-terminal.multiple-type-hinfo-record.example.com. 86400 IN HINFO "DG MV-4000" "1990"
+ 86400 IN HINFO "DG MV-4001" "1990"
+ 86400 IN HINFO "DG MV-4002" "1990"
+ 86400 IN HINFO "DG MV-4003" "1990"
+ 86400 IN HINFO "DG MV-4004" "1990"
+ 86400 IN HINFO "DG MV-4005" "1990"
+ 86400 IN HINFO "DG MV-4006" "1990"
+ 86400 IN HINFO "DG MV-4007" "1990"
+ 86400 IN HINFO "DG MV-4008" "1990"
+ 86400 IN HINFO "DG MV-4009" "1990"
+ 86400 IN HINFO "DG MV-4010" "1990"
+ 86400 IN HINFO "DG MV-4011" "1990"
+ 86400 IN HINFO "DG MV-4012" "1990"
+ 86400 IN HINFO "DG MV-4013" "1990"
+ 86400 IN HINFO "DG MV-4014" "1990"
+ 86400 IN HINFO "DG MV-4015" "1990"
+ 86400 IN HINFO "DG MV-4016" "1990"
+ 86400 RRSIG HINFO 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ gkoYkqVsjRqPFQx8a5B42+kidExzFOoEup9r
+ 9q/V/xtxx+z8T84u/qXxwLwKgLoYGBafVH2+
+ 0XJLQ/Gg7pJK2CeWAoDElKmeLSl7VB3epvxe
+ KfFIWl9kOjakoDkaEPc6XE3Yo/JouelyGj3Z
+ LqHYXHA7xozZYw+K5cLqsMTNqjU= )
+ 3600 NSEC multiple-type-ipseckey-record.example.com. HINFO RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ z+pbp+klFIu5h22skrEjRFJV3P9c8cmUx2aX
+ fLRUl4XMUf6hYrQl+4zuMp2OwS3UnMY+mGQC
+ G/PnbvoFbc7kemyaYtJXbs4CpE1mk3FlSzbW
+ Rowb7K7gy9S+P88b/SLgJvV5V0cZLbp1UqV7
+ mewGUQFsc7JSXZZzko8QyDsW8D4= )
+multiple-type-apl-record.example.com. 86400 IN APL 1:192.168.32.15/14 !1:192.168.38.0/28
+ 86400 IN APL 1:192.168.33.15/15 !1:192.168.39.0/28
+ 86400 IN APL 1:192.168.34.15/16 !1:192.168.40.0/28
+ 86400 IN APL 1:192.168.35.15/17 !1:192.168.41.0/28
+ 86400 IN APL 1:192.168.36.15/18 !1:192.168.42.0/28
+ 86400 IN APL 1:192.168.37.15/19 !1:192.168.43.0/28
+ 86400 IN APL 1:192.168.38.15/20 !1:192.168.44.0/28
+ 86400 IN APL 1:192.168.39.15/21 !1:192.168.45.0/28
+ 86400 IN APL 1:192.168.40.15/22 !1:192.168.46.0/28
+ 86400 IN APL 1:192.168.41.15/23 !1:192.168.47.0/28
+ 86400 IN APL 1:192.168.42.15/24 !1:192.168.48.0/28
+ 86400 IN APL 1:192.168.43.15/25 !1:192.168.49.0/28
+ 86400 IN APL 1:192.168.44.15/26 !1:192.168.50.0/28
+ 86400 IN APL 1:192.168.45.15/27 !1:192.168.51.0/28
+ 86400 IN APL 1:192.168.46.15/28 !1:192.168.52.0/28
+ 86400 IN APL 1:192.168.47.15/29 !1:192.168.53.0/28
+ 86400 IN APL 1:192.168.48.15/20 !1:192.168.54.0/28
+ 86400 IN APL 1:224.0.0.0/4 2:ff00::aaaa:cccc:bbbb:9999:5555/48
+ 86400 IN APL 1:224.0.0.5/8 2:ff00::/8
+ 86400 IN APL 1:224.0.0.9/12 2:ff00::888f:0/24
+ 86400 IN APL 1:224.0.0.13/16 2:ff00::7777/72
+ 86400 RRSIG APL 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ aehwZTTprSc6w/FDMjLqB574zGk5TMjPpKw9
+ XPUJ2oH2BvAffjSolgd96+3DdrcRnn/8WxUB
+ NxJGGfRldaWrpz1/2Kyh+H1/b/neo30pAHn7
+ yM6Ikl9INciVjk7iubLA0jC42szWDywaGPRm
+ GxaxZrcdNU1m+76HG+X5z7/qtyw= )
+ 3600 NSEC test.non-terminal.multiple-type-apl-record.example.com. APL RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ BLMYiCErePflhN13zVr9H4ixeq1EC/m1/Igd
+ c3cthuQ3YRbYylhmJ1Y/3FBJyjnExM3JVPlg
+ 0v+kgG34F1i9QzItAKk03VNWwSSxuW3wLRhD
+ 7m0KlyactIGcn4PzzQUbcLNuvApiphJb5EDT
+ Wd9TAvhZchgdsbcB2SJTEypHPYM= )
+test.non-terminal.multiple-type-apl-record.example.com. 86400 IN APL 1:192.168.32.15/14 !1:192.168.38.0/28
+ 86400 IN APL 1:192.168.33.15/15 !1:192.168.39.0/28
+ 86400 IN APL 1:192.168.34.15/16 !1:192.168.40.0/28
+ 86400 IN APL 1:192.168.35.15/17 !1:192.168.41.0/28
+ 86400 IN APL 1:192.168.36.15/18 !1:192.168.42.0/28
+ 86400 IN APL 1:192.168.37.15/19 !1:192.168.43.0/28
+ 86400 IN APL 1:192.168.38.15/20 !1:192.168.44.0/28
+ 86400 IN APL 1:192.168.39.15/21 !1:192.168.45.0/28
+ 86400 IN APL 1:192.168.40.15/22 !1:192.168.46.0/28
+ 86400 IN APL 1:192.168.41.15/23 !1:192.168.47.0/28
+ 86400 IN APL 1:192.168.42.15/24 !1:192.168.48.0/28
+ 86400 IN APL 1:192.168.43.15/25 !1:192.168.49.0/28
+ 86400 IN APL 1:192.168.44.15/26 !1:192.168.50.0/28
+ 86400 IN APL 1:192.168.45.15/27 !1:192.168.51.0/28
+ 86400 IN APL 1:192.168.46.15/28 !1:192.168.52.0/28
+ 86400 IN APL 1:192.168.47.15/29 !1:192.168.53.0/28
+ 86400 IN APL 1:192.168.48.15/20 !1:192.168.54.0/28
+ 86400 IN APL 1:224.0.0.0/4 2:ff00::aaaa:cccc:bbbb:9999:5555/48
+ 86400 IN APL 1:224.0.0.5/8 2:ff00::/8
+ 86400 IN APL 1:224.0.0.9/12 2:ff00::888f:0/24
+ 86400 IN APL 1:224.0.0.13/16 2:ff00::7777/72
+ 86400 RRSIG APL 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ FDKSvC1Hlgc+duM5wdWPDAjmqdJgxv22pHhI
+ xxHh6Y75VPi1zockKHZ/L9vMVm6kTtp697/h
+ UjnY2zzD104gUX1NPJPfuUJg8bdhd+r56zpc
+ uRPeBZI9EXvSXkmdmnIHtCv9Pq0wR5mOZSp1
+ zUeyUnR8GJUH+cKraS45IW7mwJ0= )
+ 3600 NSEC multiple-type-cert-record.example.com. APL RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ wlrUQ2kurNfp9VNdmGudUuLx0GbQAgEHyh9V
+ ABZDLzzEZy6ioJ71evrUa3ra3XoCb5/dg/MP
+ aOpGeD++ohZ6XOTmDEygH6ZxQflVLAuevBGN
+ XfmrZxpEAX4Ff9EecEnW8Esw3a1bUs7Pt8e4
+ DPUEN++I9FnGZ/6AETeoQJbqKns= )
+test.non-terminal.multiple-type-kx-record.example.com. 86400 IN KX 0 kaku0.google.com.
+ 86400 IN KX 10 kaku1.google.com.
+ 86400 IN KX 20 kaku2.google.com.
+ 86400 IN KX 30 kaku3.google.com.
+ 86400 IN KX 40 kaku4.google.com.
+ 86400 IN KX 50 kaku5.google.com.
+ 86400 IN KX 60 kaku6.google.com.
+ 86400 IN KX 70 kaku7.google.com.
+ 86400 IN KX 80 kaku8.google.com.
+ 86400 IN KX 90 kaku9.google.com.
+ 86400 IN KX 100 kaku10.google.com.
+ 86400 IN KX 110 kaku11.google.com.
+ 86400 IN KX 120 kaku12.google.com.
+ 86400 IN KX 130 kaku13.google.com.
+ 86400 RRSIG KX 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ K4xlhr1K6jZ/6wNo4FFN/gKSMRWeC5UD1J3T
+ inMvzeTwWLHM7u4HvNB/uqqM6d6w/clp2bx4
+ YG51yG8w9yZSMWLeqdbgMrvi3PojR4GQRGAM
+ y+27D8ocGPuZfd0tGIJ6rKHxoFlwTKJ/0lKQ
+ Hy+mN8DszNIexzJGGD9j5VYgGAM= )
+ 3600 NSEC multiple-type-loc-record.example.com. KX RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ nGn1/sPftbUJ8K4cmAW4yzVfjphyNDt9rQWT
+ B4Ut+I0llInvc+4qShem8orJzmTbH6gi4mJj
+ Kn96n5X9yvbrFby5ptwHcOVokn/rzlYpV+0m
+ 4G2wlMVAfzFts0gJ54nKUmUmIzYETLduYPX+
+ Aq0HzuZbMIRCMOLK6wBI+f9QTWQ= )
+multiple-type-loc-record.example.com. 86400 IN LOC 32 7 19.000 S 116 2 25.000 E 10.00m 1m 10000m 10m
+ 86400 IN LOC 33 7 19.000 S 117 2 25.000 E 11.00m 1m 10000m 10m
+ 86400 IN LOC 34 7 19.000 S 118 2 25.000 E 12.00m 1m 10000m 10m
+ 86400 IN LOC 35 7 19.000 S 119 2 25.000 E 13.00m 1m 10000m 10m
+ 86400 IN LOC 36 7 19.000 S 120 2 25.000 E 14.00m 1m 10000m 10m
+ 86400 IN LOC 37 7 19.000 S 121 2 25.000 E 15.00m 1m 10000m 10m
+ 86400 IN LOC 38 7 19.000 S 122 2 25.000 E 16.00m 1m 10000m 10m
+ 86400 IN LOC 39 7 19.000 S 123 2 25.000 E 17.00m 1m 10000m 10m
+ 86400 IN LOC 40 7 19.000 S 124 2 25.000 E 18.00m 1m 10000m 10m
+ 86400 IN LOC 41 7 19.000 S 125 2 25.000 E 19.00m 1m 10000m 10m
+ 86400 IN LOC 42 7 19.000 S 126 2 25.000 E 20.00m 1m 10000m 10m
+ 86400 IN LOC 43 7 19.000 S 127 2 25.000 E 21.00m 1m 10000m 10m
+ 86400 IN LOC 44 7 19.000 S 128 2 25.000 E 22.00m 1m 10000m 10m
+ 86400 IN LOC 45 7 19.000 S 129 2 25.000 E 23.00m 1m 10000m 10m
+ 86400 IN LOC 46 7 19.000 S 130 2 25.000 E 24.00m 1m 10000m 10m
+ 86400 IN LOC 47 7 19.000 S 131 2 25.000 E 25.00m 1m 10000m 10m
+ 86400 IN LOC 89 59 59.999 S 179 59 59.999 E 1000000.00m 1000m 1000m 10m
+ 86400 IN LOC 90 0 0.000 S 180 0 0.000 E 0.01m 0.01m 0.01m 10m
+ 86400 RRSIG LOC 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ulzhF8Ko+Sr8OHUBwmFjaJv55/L91bpQvg5e
+ 24Wm5wtvHh/HKDJpokvbj9DHhCpqK1E/9AW8
+ kc3qZjSnzt4FoLM3RqXrUVyWF2T1AnQgOCmu
+ kPaH3oxyrPSg8H8XYSFNckLA8OXicG1xTNeh
+ FdDhgNMEDr4PnBvgmUby7u3cQqo= )
+ 3600 NSEC test.non-terminal.multiple-type-loc-record.example.com. LOC RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ mwez/8p0PQ3c/jv4785yYIeWJmj1UCE40Sbn
+ rBGW/cPQDluXBSrKkcHiakmwm2Ox6VTNZPG9
+ m7sh0w8bxemoH0ELEePVXmbmICJxRC92n91f
+ laQKNMB3x0BWLviyWWzP3CIftWMRf4VBUIcQ
+ VhZHkXOcNfFF8gBeeOm4msZdX0M= )
+test.non-terminal.multiple-type-loc-record.example.com. 86400 IN LOC 32 7 19.000 S 116 2 25.000 E 10.00m 1m 10000m 10m
+ 86400 IN LOC 33 7 19.000 S 117 2 25.000 E 11.00m 1m 10000m 10m
+ 86400 IN LOC 34 7 19.000 S 118 2 25.000 E 12.00m 1m 10000m 10m
+ 86400 IN LOC 35 7 19.000 S 119 2 25.000 E 13.00m 1m 10000m 10m
+ 86400 IN LOC 36 7 19.000 S 120 2 25.000 E 14.00m 1m 10000m 10m
+ 86400 IN LOC 37 7 19.000 S 121 2 25.000 E 15.00m 1m 10000m 10m
+ 86400 IN LOC 38 7 19.000 S 122 2 25.000 E 16.00m 1m 10000m 10m
+ 86400 IN LOC 39 7 19.000 S 123 2 25.000 E 17.00m 1m 10000m 10m
+ 86400 IN LOC 40 7 19.000 S 124 2 25.000 E 18.00m 1m 10000m 10m
+ 86400 IN LOC 41 7 19.000 S 125 2 25.000 E 19.00m 1m 10000m 10m
+ 86400 IN LOC 42 7 19.000 S 126 2 25.000 E 20.00m 1m 10000m 10m
+ 86400 IN LOC 43 7 19.000 S 127 2 25.000 E 21.00m 1m 10000m 10m
+ 86400 IN LOC 44 7 19.000 S 128 2 25.000 E 22.00m 1m 10000m 10m
+ 86400 IN LOC 45 7 19.000 S 129 2 25.000 E 23.00m 1m 10000m 10m
+ 86400 IN LOC 46 7 19.000 S 130 2 25.000 E 24.00m 1m 10000m 10m
+ 86400 IN LOC 47 7 19.000 S 131 2 25.000 E 25.00m 1m 10000m 10m
+ 86400 IN LOC 89 59 59.999 S 179 59 59.999 E 1000000.00m 1000m 1000m 10m
+ 86400 IN LOC 90 0 0.000 S 180 0 0.000 E 0.01m 0.01m 0.01m 10m
+ 86400 RRSIG LOC 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Hhg2/gJ4NSn/PBU6SaFXqd35wqDXOKJ84O5Y
+ Z0SfMJjZQ+S/jmGfPEDADp0p0d7J7N9ttari
+ yw9HMZ5AGOjl44aKfwVLivXcjITsar5RKyIT
+ E5Q22bb/SrDJUb9orcGJZmhFPlJXP7VGBbO8
+ frk9fsvG2g1wsO3Wh45M9us3jLI= )
+ 3600 NSEC multiple-type-minfo-record.example.com. LOC RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ y84udhTt/XPPoB6q1IjRXiC3DCBklB1h8rHY
+ fmjpmoqGc6eFViSWr3K3gx6XnuaOhIsXRjFe
+ knQlkYIRGy42YHvY7IZ1BiLDtM3k3twqsasP
+ 9sUVavv3Km+IdvOfvTtbU5UYcaLiTIr8M7/p
+ vIPoqkTWeXntb0KR8rYYylVGPZA= )
+multiple-type-minfo-record.example.com. 86400 IN MINFO sun00.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 IN MINFO sun01.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 IN MINFO sun02.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 IN MINFO sun03.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.dn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.en. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.fn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.gn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 RRSIG MINFO 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ZI7hm5Zn9qwBl7HVkMLFLOrv5E4ggMSNsJ4w
+ MRJCIv+69fdhS43NYA/W2hQfUzOXa/JhFk/i
+ z+guQ6N6XhllidIaT53W6AIHZu/19QKpL2VS
+ VMQ4pDrtJYzQNsemeTcvfGzZdBmbPc/5QryR
+ zbswkajykzSDL9Au08M8TncCB3c= )
+ 3600 NSEC test.non-terminal.multiple-type-minfo-record.example.com. MINFO RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ fUwTo84k6vpmBltSZR1MZNtTDdZSGQ1/+ICZ
+ ummg0tYmImI6Ml3VBSgZepkIDT+7fa4JDWPT
+ EpYp7GK19OPgv8ijQhmPU3OVNAeqhvMpjK3g
+ w9IoR+OEQRYhgezsA0hyKIhd5OJ9bW/FBJTf
+ 04Q8bo9tHqfUSpM2yK9c9xoaL70= )
+test.non-terminal.multiple-type-minfo-record.example.com. 86400 IN MINFO sun00.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 IN MINFO sun01.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 IN MINFO sun02.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 IN MINFO sun03.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.cn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.dn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.en. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.fn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 IN MINFO sun04.abc.foo.bar.liu.zhang.han.cnnic.gn. pipo.fifo.lilo.tito.gigo.bibo.riro.siso.vivo.cico.cn.
+ 86400 RRSIG MINFO 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ eUJ2HVgSxrFxBXmoEMHybIZ4CScum7nVIjJB
+ TRXwAZBoqTFWAxZ8v3tt2e0aIDk5lu3H/lLa
+ Z2ThcXpFYmibcBid4eWhwhsUoj+/vsProK6Q
+ spXz2+GPg9C/1aVmblO3JfUUQ/XHbDGLSOyJ
+ TtWC0MqUDbKu86jDlKOTTo5tyiw= )
+ 3600 NSEC multiple-type-mx-record.example.com. MINFO RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ YK9blDNd/KdWiDYEFQi3wPAiY3r7+kk44aRl
+ IV0E/Mwpg6Uy/n6h8fS8Jxv35lWusKFjR76F
+ 6ekvGjn+a9aXZ1INxR2F2aLMxI/pM9qnntty
+ PdBx+X8OQlrT3ZU4Egt+Edl+TYZEmLYVCCaV
+ nvQw5+tXChTPlgjxCs0MsJe+LSo= )
+multiple-type-mx-record.example.com. 86400 IN MX 10 mail1.example.com.
+ 86400 IN MX 10 mail2.example.com.
+ 86400 IN MX 10 mail3.example.com.
+ 86400 IN MX 10 mail4.example.com.
+ 86400 IN MX 10 mail5.example.com.
+ 86400 IN MX 10 mail6.example.com.
+ 86400 IN MX 10 mail7.example.com.
+ 86400 IN MX 10 mail8.example.com.
+ 86400 IN MX 10 mail9.example.com.
+ 86400 IN MX 10 mail10.example.com.
+ 86400 IN MX 10 mail11.example.com.
+ 86400 IN MX 10 mail12.example.com.
+ 86400 IN MX 20 mail13.example.com.
+ 86400 IN MX 30 mail14.example.com.
+ 86400 IN MX 40 mail15.example.com.
+ 86400 IN MX 50 mail16.example.com.
+ 86400 IN MX 60 mail17.example.com.
+ 86400 IN MX 70 mail18.example.com.
+ 86400 IN MX 80 mail19.example.com.
+ 86400 IN MX 90 mail20.example.com.
+ 86400 RRSIG MX 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ n1a9V6z3XbAXFUwAtE0eqS1Cq5wJhIG8AZkF
+ rABGfxtLR7zatMeRwIjx4aUoUI5vLWN0E+di
+ 1xOAEDiFOjw+jFYklcR6dqREdrEbJFYAOPwY
+ MCCKeOPay3VcJxW/Z5yjLSBPnq7jq9ERawcB
+ MJnOnKS49WaibS3VYiWuXOXfn2s= )
+ 3600 NSEC test.non-terminal.multiple-type-mx-record.example.com. MX RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ U1f48jBPDKboX/uyoNm8/bRKVV9ro2iNx28C
+ 0QAyRv/Wm3oc+u9dJQZF0lobbf12XiYOP+G1
+ UTSBTx8TKPNqkeQWYE/jjpiB7DOF24Ua6Mh+
+ d+bofYik7Eissuyqm6jJbkfJjSjNluAixbKc
+ iRmt1BNYGIMfXj4FsJHYESRMW1M= )
+test.non-terminal.multiple-type-mx-record.example.com. 86400 IN MX 10 mail1.example.com.
+ 86400 IN MX 10 mail2.example.com.
+ 86400 IN MX 10 mail3.example.com.
+ 86400 IN MX 10 mail4.example.com.
+ 86400 IN MX 10 mail5.example.com.
+ 86400 IN MX 10 mail6.example.com.
+ 86400 IN MX 10 mail7.example.com.
+ 86400 IN MX 10 mail8.example.com.
+ 86400 IN MX 10 mail9.example.com.
+ 86400 IN MX 10 mail10.example.com.
+ 86400 IN MX 10 mail11.example.com.
+ 86400 IN MX 10 mail12.example.com.
+ 86400 IN MX 20 mail13.example.com.
+ 86400 IN MX 30 mail14.example.com.
+ 86400 IN MX 40 mail15.example.com.
+ 86400 IN MX 50 mail16.example.com.
+ 86400 IN MX 60 mail17.example.com.
+ 86400 IN MX 70 mail18.example.com.
+ 86400 IN MX 80 mail19.example.com.
+ 86400 IN MX 90 mail20.example.com.
+ 86400 RRSIG MX 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ BTXMAaDMhKrLa9vsctT6XkutEC4KRzmyFej5
+ PbCv/n+q81Nyevyp5VZjH2/u30JNA2BDjhcA
+ EvMUncGljMiz9hKvqW04R295Z+aSq0xINM8H
+ wE0S+Ef1KQDNJv4z9jHwnvtJx8BXT+RXeXMK
+ t9+uv7X/ejrWFrgLGEVnrP9MexE= )
+ 3600 NSEC multiple-type-ns-record.example.com. MX RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ AFH7WPdHrW3IhPeWuE7kBvgLWqa/Qd7zm+69
+ RzH0tC7ZEB/LH7noLvHr3a4L0gYR0CD7p6Oh
+ q6e7KNGcA/bUoS8YXxlQiyl/ByGQxSe5jUVi
+ zbAk/6p7QlRg/6q3HlphjK0r+C23FlUsElcu
+ kV0WEzGZ8rONYzxbduUVluTW0Co= )
+dns1.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.1
+test.non-terminal.dns1.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.1
+dns10.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.10
+test.non-terminal.dns10.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.10
+dns11.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.11
+test.non-terminal.dns11.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.11
+dns12.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.12
+test.non-terminal.dns12.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.12
+dns13.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.13
+test.non-terminal.dns13.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.13
+dns14.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.14
+test.non-terminal.dns14.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.14
+dns15.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.15
+test.non-terminal.dns15.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.15
+dns16.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.16
+test.non-terminal.dns16.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.16
+dns17.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.17
+test.non-terminal.dns17.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.17
+dns18.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.18
+test.non-terminal.dns18.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.18
+dns19.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.19
+test.non-terminal.dns19.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.19
+dns2.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.2
+test.non-terminal.dns2.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.2
+dns20.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.20
+test.non-terminal.dns20.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.20
+dns21.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.21
+test.non-terminal.dns21.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.21
+dns22.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.22
+test.non-terminal.dns22.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.22
+dns23.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.23
+test.non-terminal.dns23.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.23
+dns24.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.24
+test.non-terminal.dns24.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.24
+dns25.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.25
+test.non-terminal.dns25.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.25
+dns3.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.3
+test.non-terminal.dns3.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.3
+dns4.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.4
+test.non-terminal.dns4.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.4
+dns5.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.5
+test.non-terminal.dns5.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.5
+dns6.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.6
+test.non-terminal.dns6.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.6
+dns7.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.7
+test.non-terminal.dns7.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.7
+dns8.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.8
+test.non-terminal.dns8.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.8
+dns9.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.9
+test.non-terminal.dns9.multiple-type-ns-record.example.com. 86400 IN A 192.168.4.9
+test.non-terminal.multiple-type-ns-record.example.com. 86400 IN NS dns1.multiple-type-ns-record.example.com.
+ 86400 IN NS dns2.multiple-type-ns-record.example.com.
+ 86400 IN NS dns3.multiple-type-ns-record.example.com.
+ 86400 IN NS dns4.multiple-type-ns-record.example.com.
+ 86400 IN NS dns5.multiple-type-ns-record.example.com.
+ 86400 IN NS dns6.multiple-type-ns-record.example.com.
+ 86400 IN NS dns7.multiple-type-ns-record.example.com.
+ 86400 IN NS dns8.multiple-type-ns-record.example.com.
+ 86400 IN NS dns9.multiple-type-ns-record.example.com.
+ 86400 IN NS dns10.multiple-type-ns-record.example.com.
+ 86400 IN NS dns11.multiple-type-ns-record.example.com.
+ 86400 IN NS dns12.multiple-type-ns-record.example.com.
+ 86400 IN NS dns13.multiple-type-ns-record.example.com.
+ 86400 IN NS dns14.multiple-type-ns-record.example.com.
+ 86400 IN NS dns15.multiple-type-ns-record.example.com.
+ 86400 IN NS dns16.multiple-type-ns-record.example.com.
+ 86400 IN NS dns17.multiple-type-ns-record.example.com.
+ 86400 IN NS dns18.multiple-type-ns-record.example.com.
+ 86400 IN NS dns19.multiple-type-ns-record.example.com.
+ 86400 IN NS dns20.multiple-type-ns-record.example.com.
+ 86400 IN NS dns21.multiple-type-ns-record.example.com.
+ 86400 IN NS dns22.multiple-type-ns-record.example.com.
+ 86400 IN NS dns23.multiple-type-ns-record.example.com.
+ 86400 IN NS dns24.multiple-type-ns-record.example.com.
+multiple-type-ipseckey-record.example.com. 86400 IN IPSECKEY ( 10 1 2 192.0.2.3
+ AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6
+ BSGvgqt4AQ== )
+ 86400 IN IPSECKEY ( 10 1 2 192.0.2.4
+ BQNRU3mG7TVTO2BkR47usntb102uFJtugbo6
+ BSGvgqt4AQ== )
+ 86400 IN IPSECKEY ( 10 1 2 192.0.2.5
+ CQNRU3mG7TVTO2BkR47usntb102uFJtugbo6
+ BSGvgqt4AQ== )
+ 86400 IN IPSECKEY ( 10 1 2 192.0.2.6
+ DQNRU3mG7TVTO2BkR47usntb102uFJtugbo6
+ BSGvgqt4AQ== )
+ 86400 IN IPSECKEY ( 10 1 2 192.0.2.7
+ EQNRU3mG7TVTO2BkR47usntb102uFJtugbo6
+ BSGvgqt4AQ== )
+ 86400 IN IPSECKEY ( 10 1 2 192.0.2.8
+ FQNRU3mG7TVTO2BkR47usntb102uFJtugbo6
+ BSGvgqt4AQ== )
+ 86400 IN IPSECKEY ( 10 1 2 192.0.2.9
+ GQNRU3mG7TVTO2BkR47usntb102uFJtugbo6
+ BSGvgqt4AQ== )
+ 86400 IN IPSECKEY ( 10 1 2 192.0.2.10
+ HQNRU3mG7TVTO2BkR47usntb102uFJtugbo6
+ BSGvgqt4AQ== )
+ 86400 IN IPSECKEY ( 10 1 2 192.0.2.11
+ IQNRU3mG7TVTO2BkR47usntb102uFJtugbo6
+ BSGvgqt4AQ== )
+ 86400 RRSIG IPSECKEY 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ S6gFr+bJMrovfOsEzphLuH/peRKpC1e9CIw1
+ MxMQNN4YRW0XtJ0P6R4crYkRIE6hOtMsAHlD
+ PTAOZmnInmM/8Nhv0f7917NFJLrXpmpcqFLu
+ lolxyYPgTvDEaD4hE6ufCbHoxcQVPvDoNNUW
+ MA6sat8kv+I+6/uR2yTNUuDn5xk= )
+ 3600 NSEC multiple-type-kx-record.example.com. IPSECKEY RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ bSTvLRt+Gky6rF00U36bSN5SkSqtMQYlYvvP
+ GpulB8CBePWtgvyfgoqqldDTxfrqNzNMJ9ty
+ VZuHP96F9g15C/hOcuatoYri+HxTaNnUnk6c
+ bKGx7EKkkw7DCwO/WeD+DxAYiGVv1ugJ1hVz
+ MV4zSmYKajwf1TicojyQsxedD5o= )
+multiple-type-ns-record.example.com. 86400 IN NS dns1.multiple-type-ns-record.example.com.
+ 86400 IN NS dns2.multiple-type-ns-record.example.com.
+ 86400 IN NS dns3.multiple-type-ns-record.example.com.
+ 86400 IN NS dns4.multiple-type-ns-record.example.com.
+ 86400 IN NS dns5.multiple-type-ns-record.example.com.
+ 86400 IN NS dns6.multiple-type-ns-record.example.com.
+ 86400 IN NS dns7.multiple-type-ns-record.example.com.
+ 86400 IN NS dns8.multiple-type-ns-record.example.com.
+ 86400 IN NS dns9.multiple-type-ns-record.example.com.
+ 86400 IN NS dns10.multiple-type-ns-record.example.com.
+ 86400 IN NS dns11.multiple-type-ns-record.example.com.
+ 86400 IN NS dns12.multiple-type-ns-record.example.com.
+ 86400 IN NS dns13.multiple-type-ns-record.example.com.
+ 86400 IN NS dns14.multiple-type-ns-record.example.com.
+ 86400 IN NS dns15.multiple-type-ns-record.example.com.
+ 86400 IN NS dns16.multiple-type-ns-record.example.com.
+ 86400 IN NS dns17.multiple-type-ns-record.example.com.
+ 86400 IN NS dns18.multiple-type-ns-record.example.com.
+ 86400 IN NS dns19.multiple-type-ns-record.example.com.
+ 86400 IN NS dns20.multiple-type-ns-record.example.com.
+ 86400 IN NS dns21.multiple-type-ns-record.example.com.
+ 86400 IN NS dns22.multiple-type-ns-record.example.com.
+ 86400 IN NS dns23.multiple-type-ns-record.example.com.
+ 86400 IN NS dns24.multiple-type-ns-record.example.com.
+ 3600 NSEC multiple-type-nsap-record.example.com. NS RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ hGy291VFD0/csxNnK9OwnL52ufIRnZpta7MK
+ 1KQ2XboDyUTxJEKbKFiQpD33eUDDkNgcbueF
+ 57BBm2A+O1Kmn5I58ri22hQOZGVEqXlFJsFe
+ tQ76bHy0LUUt1fy2vXdjeC2Z1KonDbxJiRpa
+ 772s419tSBNU500199iHoH368yc= )
+multiple-type-kx-record.example.com. 86400 IN KX 0 kaku0.google.com.
+ 86400 IN KX 10 kaku1.google.com.
+ 86400 IN KX 20 kaku2.google.com.
+ 86400 IN KX 30 kaku3.google.com.
+ 86400 IN KX 40 kaku4.google.com.
+ 86400 IN KX 50 kaku5.google.com.
+ 86400 IN KX 60 kaku6.google.com.
+ 86400 IN KX 70 kaku7.google.com.
+ 86400 IN KX 80 kaku8.google.com.
+ 86400 IN KX 90 kaku9.google.com.
+ 86400 IN KX 100 kaku10.google.com.
+ 86400 IN KX 110 kaku11.google.com.
+ 86400 IN KX 120 kaku12.google.com.
+ 86400 IN KX 130 kaku13.google.com.
+ 86400 RRSIG KX 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ tdUveI5DwuJTkM1NOfp+IGWO5bYyLjb4ujTo
+ 1IlGJoVzyzA4NxGQGhX7zHVMx3uI0Mj85eQ0
+ pLsUi9SLP/gXNcvz7KHnEc7UnBFraochDS0n
+ Uop0Y1Kh7PjL8/jrTjgnDsz0Tg9yAJPNrIMa
+ JHQWasZjphbO9iRFrlJ4XTKHx6A= )
+ 3600 NSEC test.non-terminal.multiple-type-kx-record.example.com. KX RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ TuV2ESpHYIOFm2VZ10JCEPLLmoIRg36FVY5O
+ qj6vfg/6vzIxLviMtBlq+SYoiADw+w+EeQaq
+ cYhuv44V6G5LlfViSLNFWPXntBnEkIUQlLVc
+ B9vbD2bYb7pPBW40qn8m22d5xDaz+VFi7/hV
+ JeRNhEjb0nl1De7Vw4yrYXguF9E= )
+multiple-type-nsap-record.example.com. 86400 IN NSAP 0x47000580ffff0000003210999911112222333345
+ 86400 IN NSAP 0x47000580ffff0000003210999911112222333346
+ 86400 IN NSAP 0x47000580ffff0000003210999911112222333347
+ 86400 IN NSAP 0x47000580ffff0000003210999911112222333348
+ 86400 IN NSAP 0x47000580ffff0000003210999911112222333349
+ 86400 IN NSAP 0x47000580ffff000000321099991111222233334a
+ 86400 IN NSAP 0x47000580ffff000000321099991111222233334b
+ 86400 IN NSAP 0x47000580ffff000000321099991111222233334c
+ 86400 IN NSAP 0x47000580ffff000000321099991111222233334d
+ 86400 IN NSAP 0x47000580ffff000000321099991111222233334e
+ 86400 IN NSAP 0x47000580ffff000000321099991111222233334f
+ 86400 IN NSAP 0x47000580ffff0000003210999911112222333350
+ 86400 IN NSAP 0x47000580ffff0000003210999911112222333351
+ 86400 IN NSAP 0x47000580ffff0000003210999911112222333352
+ 86400 IN NSAP 0x47000580ffff0000003210999911112222333353
+ 86400 RRSIG NSAP 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ VjjJ7wbuhaC7NRGHCYvLvMcfIq4NfrqsKPUT
+ R3MGYS2BDQV4zRH2+O3ZZvJPowMYjRLtirZb
+ zCufgAeMSffttz0NYaeFc3aPt3P99Svdyj8A
+ 8a/A31rCsT2pX3JKpjqsbWvZ71ezJtlysizF
+ chSvSB+zfVzrZkYUIaAxj4GQOmw= )
+ 3600 NSEC test.non-terminal.multiple-type-nsap-record.example.com. NSAP RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Wtp2ageuXKacRJvnPgqHi7DFetvEH2Sti3Y3
+ ofcZlCu0k9vH60DHGPQVZZDXMsJqNbm6rEP/
+ 8gl/FULxmdbYt54xrXzhTVj5LLQ/zIwHfYcC
+ ybz6EnBR5ECIm7AYDssaGYS30q378ILGaIJp
+ pE9ekJ7BMuvlnfcY16cpIrTla2I= )
+multiple-type-ptr-record.example.com. 86400 IN PTR 192.168.2.1.example.com.
+ 86400 IN PTR 192.168.2.2.example.com.
+ 86400 IN PTR 192.168.2.3.example.com.
+ 86400 IN PTR 192.168.2.4.example.com.
+ 86400 IN PTR 192.168.2.5.example.com.
+ 86400 IN PTR 192.168.2.6.example.com.
+ 86400 IN PTR 192.168.2.7.example.com.
+ 86400 IN PTR 192.168.2.8.example.com.
+ 86400 IN PTR 192.168.2.9.example.com.
+ 86400 IN PTR 192.168.2.10.example.com.
+ 86400 IN PTR 192.168.2.11.example.com.
+ 86400 IN PTR 192.168.2.12.example.com.
+ 86400 IN PTR 192.168.2.13.example.com.
+ 86400 IN PTR 192.168.2.14.example.com.
+ 86400 IN PTR 192.168.2.15.example.com.
+ 86400 IN PTR 192.168.2.16.example.com.
+ 86400 IN PTR 192.168.2.17.example.com.
+ 86400 IN PTR 192.168.2.18.example.com.
+ 86400 RRSIG PTR 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ xMr0GjsJdcM49li65zthlitnHMAn9TnOvwZG
+ BCq3Z6tfwLqMjQ+OE5GJL4LxRwbzeD4He5F/
+ E0jgt53h5UBXIDGQW5JVn37eymOpq85AZXXj
+ DPwzSE52wPSiglYdE8yD1YCiuMsUzwuxIuB4
+ vmsQ6Uhk290QA1B+HSF2lOozQ6M= )
+ 3600 NSEC test.non-terminal.multiple-type-ptr-record.example.com. PTR RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ Ycs286OBLxSiSUj//nyrgzM4A/RjGADr3q5B
+ zyX8lCk2DyBIOVzJi6s17uy/FhuPiIHDXUMG
+ TN/30+tlo0PNaK+sA7wSY01djNVdcGuWv5id
+ LNaeHi5poNlOw2LwulKvDH7jbctBTDd7yaPu
+ S00drnPpXu0BhF5+QrBnRS7G3pw= )
+test.non-terminal.multiple-type-ptr-record.example.com. 86400 IN PTR 192.168.2.1.example.com.
+ 86400 IN PTR 192.168.2.2.example.com.
+ 86400 IN PTR 192.168.2.3.example.com.
+ 86400 IN PTR 192.168.2.4.example.com.
+ 86400 IN PTR 192.168.2.5.example.com.
+ 86400 IN PTR 192.168.2.6.example.com.
+ 86400 IN PTR 192.168.2.7.example.com.
+ 86400 IN PTR 192.168.2.8.example.com.
+ 86400 IN PTR 192.168.2.9.example.com.
+ 86400 IN PTR 192.168.2.10.example.com.
+ 86400 IN PTR 192.168.2.11.example.com.
+ 86400 IN PTR 192.168.2.12.example.com.
+ 86400 IN PTR 192.168.2.13.example.com.
+ 86400 IN PTR 192.168.2.14.example.com.
+ 86400 IN PTR 192.168.2.15.example.com.
+ 86400 IN PTR 192.168.2.16.example.com.
+ 86400 IN PTR 192.168.2.17.example.com.
+ 86400 IN PTR 192.168.2.18.example.com.
+ 86400 RRSIG PTR 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ cU6aFz/ZFM8Sn/3faM0jdOxmQ7l3H4yBNIkz
+ dDK/NhisaScsEVifPDSkDOye3Z0mCB22eJSt
+ Z5OKiLEfG7LIskHVFckYcjtX1/z9Atiimqfk
+ EKyqefWHDmYgcN79Ik212BR77qrAaFc9VbE3
+ pt0fImdLIoavysX2H6ja+LddfWg= )
+ 3600 NSEC multiple-type-px-record.example.com. PTR RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ iW85YGRBWXGMMKOCwy9jObygUotQ+qtYXvGV
+ DhlNSa92e9fr3lkJXmkTobs8yM8Y6y26sgMO
+ N7Pu7ecOoj8lrGqnhvh0qDQ6XHi0yZndTsAy
+ nx6oe2MjTmn5Qfo+a1JLCf6DPTPvq8WJTdPl
+ WOuYYdxULuYWZxwhMfpO6udbeWI= )
+multiple-type-px-record.example.com. 86400 IN PX 10 ab.net2.it. O-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 11 ab.net3.it. 1-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 12 ab.net4.it. 2-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 13 ab.net5.it. 3-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 14 ab.net6.it. 4-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 15 ab.net7.it. 5-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 16 ab.net8.it. 6-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 10 ab.net9.it. 7-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 10 ab.net2.it. 8-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 10 ab.net2.it. 9-ab.PRMD-net2.ADMDb.C-it.
+ 86400 RRSIG PX 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ L4Ei+7bRt5fC/yz5QKoLdjDQHf4tFRfueF0H
+ RXKm5ytymaaNfbA+MWVB1YAo/4volienHpz8
+ 6xwW2UEdBmSlAN+F2ITp9vy3Rbt4WRrvW1f+
+ EHfZUAW9b9wNfjY2gYNiBkD+/Ilw4le/gG3M
+ 4bD2RDndIcDrS0ibgPhfJV9yBnM= )
+ 3600 NSEC test.non-terminal.multiple-type-px-record.example.com. PX RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ n8tYwZFxp04RsaevVGSgiJYIUB4yQ8PUH13r
+ CtOMhyZ9pOKRapoZo+oSCP7XKPrjpAu/IoJw
+ Z0ZIEgDEGvMmrd7eJF5kvx/Ac7Ev11M3LsfP
+ Vqo5yyMbTEzdeQjx8Jy+4PXs8Q7L4t2p1Qal
+ Yju8Lbn5PW0trhw2mQd9lr3UIXQ= )
+test.non-terminal.multiple-type-px-record.example.com. 86400 IN PX 10 ab.net2.it. O-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 11 ab.net3.it. 1-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 12 ab.net4.it. 2-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 13 ab.net5.it. 3-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 14 ab.net6.it. 4-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 15 ab.net7.it. 5-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 16 ab.net8.it. 6-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 10 ab.net9.it. 7-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 10 ab.net2.it. 8-ab.PRMD-net2.ADMDb.C-it.
+ 86400 IN PX 10 ab.net2.it. 9-ab.PRMD-net2.ADMDb.C-it.
+ 86400 RRSIG PX 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ T5eoO8YYxMN4gJ7wbonQn2qt+QuMXk9OqErP
+ HhqWbrCGhdgj+n4KvbqzAgutcBWcNDEM72F1
+ J5kcNJyw7KB5Cgc5LPOYFLr0e0GLdKMDKR8D
+ 2HictxvGwxNa044z54ArZB57K8dj0ZvVKD4x
+ gBddOCAdo6xjA0yDPSqThASjEPA= )
+ 3600 NSEC multiple-type-spf-record.example.com. PX RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ W7UWZhx9H+WRE4rWvaqN6WNA+sjnAwOJ9qvr
+ oacCBklJMCu/8Dq8OyK4TbJ/jQdcZHWPA7Ck
+ Sm1A3dw3WjD6xrNXFdk53UzHBV4hpIgFMMaU
+ cPRnGoH2oYMeysqHxxG8kaP/snz3FFM795E+
+ 7p7SutdJRwxtAgYEJ0lTMbnYYQY= )
+multiple-type-spf-record.example.com. 3600 IN NSEC test.non-terminal.multiple-type-spf-record.example.com. RRSIG NSEC SPF
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ fYl7qQa5MF35eatIE/SXaNuHSStpVPruc2nJ
+ TP6A6uwDz3mG52EJg55ppiy4geSS6PO/LHU9
+ hnf0PP7uL8tgyDcxlLEvtCkaSaZ9H6yrsCn2
+ 28j916U/OTEzOxtz6jvHZL2dXr4jizTJIaAp
+ hCrRq/7pzZfzcubwzwfqpZW/TbY= )
+ 86400 SPF "'asdfghjkl"
+ 86400 SPF "'sina"
+ 86400 SPF "'cnnic"
+ 86400 SPF "'jkl"
+ 86400 SPF "'c=sina.com.'"
+ 86400 SPF "'fw=fff.com.cn.'"
+ 86400 SPF "'ibm"
+ 86400 SPF "'dell"
+ 86400 SPF "'google" "-" "microsoft"
+ 86400 SPF "'leibusi"
+ 86400 SPF "v=spf1"
+ 86400 SPF "v=spf1 -all"
+ 86400 SPF "v=spf1 ip4:192.168.0.1/16 -all "
+ 86400 SPF "v=spf1 ip4:192.168.1.1/16 -all "
+ 86400 SPF "v=spf1 ip4:192.168.2.1/16 -all "
+ 86400 SPF "v=spf1 ip4:192.168.3.1/16 -all "
+ 86400 SPF "v=spf1 ip4:192.168.4.1/16 -all "
+ 86400 SPF "v=spf1 ip4:192.168.5.1/16 -all "
+ 86400 SPF "v=spf1 ip4:192.168.6.1/16 -all "
+ 86400 SPF "v=spf1 ip4:192.168.7.1/16 -all "
+ 86400 RRSIG SPF 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ nIzHN3gjvwLEZxKGRIX8ip+LLb6F4zPkkV7P
+ zgNkeUwrT9/1kTxn6dM7JLUs4B2e3TTOVVau
+ /8keis6ERbW51w0dR6glneGUqvWOk+GH1Qdo
+ nJLOkpPnXqUKr50pcLxS8YCf0bqYXZOy3uWh
+ 4eEKEKlgnYGys/+oZdiB8ulPR2E= )
+test.non-terminal.multiple-type-spf-record.example.com. 3600 IN NSEC multiple-type-srv-record.example.com. RRSIG NSEC SPF
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ uCohn+WaZg8LrCFrvjz0xRgKR2FTGMh2Vmw1
+ HrHnzdFj4RhAKIlcD+CMsaWlNYetu4AvK0Xw
+ uB+Q1GjkOjMhm843RxjNSXacCzvIoixYrt8w
+ XR8fLeRzv6fZbYocyKjgXuOmiIlBQp47yOW3
+ ptItmoFKrcxoZ5KW3dIvAYLNISM= )
+ 86400 SPF "'asdfghjkl"
+ 86400 SPF "'sina"
+ 86400 SPF "'cnnic"
+ 86400 SPF "'jkl"
+ 86400 SPF "'c=sina.com.'"
+ 86400 SPF "'fw=fff.com.cn.'"
+ 86400 SPF "'ibm"
+ 86400 SPF "'dell"
+ 86400 SPF "'google" "-" "microsoft"
+ 86400 SPF "'leibusi"
+ 86400 SPF "v=spf1"
+ 86400 SPF "v=spf1 -all"
+ 86400 SPF "v=spf1 ip4:192.168.0.1/16 -all "
+ 86400 SPF "v=spf1 ip4:192.168.1.1/16 -all "
+ 86400 SPF "v=spf1 ip4:192.168.2.1/16 -all "
+ 86400 SPF "v=spf1 ip4:192.168.3.1/16 -all "
+ 86400 SPF "v=spf1 ip4:192.168.4.1/16 -all "
+ 86400 SPF "v=spf1 ip4:192.168.5.1/16 -all "
+ 86400 SPF "v=spf1 ip4:192.168.6.1/16 -all "
+ 86400 SPF "v=spf1 ip4:192.168.7.1/16 -all "
+ 86400 RRSIG SPF 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ CjyonA4FOC7BYfeSGe7dJNO3sn/8SkVAH3+m
+ P1pxkJzMRzemMu65sFI6vlnRtbCvh6Q5wPRH
+ s2YDpiO0+QXDn6+75vS2Fzgmn5nOppt2SSsA
+ oRBM+uDaAmsBreYFJkXtx4PdpbaL/pGE3hx5
+ qmOAHRxS6snoZtewleyWvCnr6GY= )
+test.non-terminal.multiple-type-nsap-record.example.com. 86400 IN NSAP 0x47000580ffff0000003210999911112222333345
+ 86400 IN NSAP 0x47000580ffff0000003210999911112222333346
+ 86400 IN NSAP 0x47000580ffff0000003210999911112222333347
+ 86400 IN NSAP 0x47000580ffff0000003210999911112222333348
+ 86400 IN NSAP 0x47000580ffff0000003210999911112222333349
+ 86400 IN NSAP 0x47000580ffff000000321099991111222233334a
+ 86400 IN NSAP 0x47000580ffff000000321099991111222233334b
+ 86400 IN NSAP 0x47000580ffff000000321099991111222233334c
+ 86400 IN NSAP 0x47000580ffff000000321099991111222233334d
+ 86400 IN NSAP 0x47000580ffff000000321099991111222233334e
+ 86400 IN NSAP 0x47000580ffff000000321099991111222233334f
+ 86400 IN NSAP 0x47000580ffff0000003210999911112222333350
+ 86400 IN NSAP 0x47000580ffff0000003210999911112222333351
+ 86400 IN NSAP 0x47000580ffff0000003210999911112222333352
+ 86400 IN NSAP 0x47000580ffff0000003210999911112222333353
+ 86400 RRSIG NSAP 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ xefH7sd6NOTwJzREvnIXvOnh6bokfP6rjCJ6
+ Nwdl/Sgh6bDsbrrJTVIPIgZOXsjU7/OKakFG
+ ICOdpVWYmYnLnYcLyu/VP1zgXDnRydEFyYVp
+ WT/67H1dsCcYWM7KHq6eVdhMZe2pKturt1cD
+ W20lTJ0jdg7AK3Cg8v7K74Fpqzs= )
+ 3600 NSEC multiple-type-ptr-record.example.com. NSAP RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ w51cGk1/OFOq2wQoOUaswnmf7SAQkj3hc1xI
+ 9SRdhrG+FwVn+4wxP43/kBdvWI/auSUgWqtT
+ OGGg4vBCq3AXw3ujRIZlVmfJjUtIOSZQp9mC
+ 8pwU9tRhQJxMNBp3YpI2wlDZH4oUvZ//OAqI
+ 1zHHt8nwM49xbnZN/RVZwsITVKI= )
+multiple-type-sshfp-record.example.com. 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7890 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7891 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7892 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7893 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7894 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7895 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7896 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7897 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7898 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7899 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 789A )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 789B )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 789C )
+ 86400 RRSIG SSHFP 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ diDyrgPWHOROwzvA4uiTMlMt7TecCExaW4Di
+ lAcbaKw0L9FFm7Y/NFzbMnbTHil7oQXsj7YL
+ ACSfB2VEYOcbz+634MAP6OhWAbYi2tH4NtCo
+ NN7s0FO+X1OY4loW+YUzqv07GQx3rGU1Rp8H
+ ZN9SBxByLYX6/iv70IOoiOzwGIo= )
+ 3600 NSEC test.non-terminal.multiple-type-sshfp-record.example.com. SSHFP RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ jdMPQ4Z+FZBsMN8U8avHc2rz+YgCM9QzMK+J
+ DSP23aELopJTeocG1unpLkEjw5btJqPW1Hxj
+ bA2G/rojgP8hUOi4qLis1AiA6rjHILnW/sD+
+ 6H7DAxB7/tvD0SSJn55D3vmqU5WMrti9ofuA
+ n2LYz/MooVo2OMZATdrHvHGvuz4= )
+test.non-terminal.multiple-type-sshfp-record.example.com. 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7890 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7891 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7892 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7893 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7894 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7895 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7896 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7897 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7898 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 7899 )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 789A )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 789B )
+ 86400 IN SSHFP 2 1 (
+ 123456789ABCDEF67890123456789ABCDEF6
+ 789C )
+ 86400 RRSIG SSHFP 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ AZgsDXFCkg8QK/kb+myeB2S7Rt2/UoDtLZbi
+ iNmCi+ichEtXHsrYFW+cwnRC1ooUGGmR8RWq
+ IY+i/K1DdifcNu1eR7Fw8jvSGSwdP5PjcmZv
+ D7qsy9a1z6S4w8DV76yPSscc87feqJeFteDT
+ DDOY5qfTjDlU8NK0BeYyhIuAvIM= )
+ 3600 NSEC multiple-type-txt-record.example.com. SSHFP RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ gD4t70ymAKs3OGFjmOapjt4j7Sj4jxZoqtHP
+ /appQRN8b+sfq7I8bPuldmjGY/3dqoOuqXI5
+ i13PMeixxs6tWkfNAIc34/5V2jCnXH0ew+4f
+ NYP5Jf3En5XgXL/tMrMbduQvQL/WSqRj/eDX
+ JMX4Q7kAzPeey5dRrOhugnnVU5Y= )
+test.non-terminal.multiple-type-srv-record.example.com. 86400 IN SRV 0 2 80 www.movie0.edu.
+ 86400 IN SRV 1 3 80 www.movie1.edu.
+ 86400 IN SRV 2 4 80 www.movie2.edu.
+ 86400 IN SRV 3 5 80 www.movie3.edu.
+ 86400 IN SRV 4 6 80 www.movie4.edu.
+ 86400 IN SRV 5 7 80 www.movie5.edu.
+ 86400 IN SRV 6 8 80 www.movie6.edu.
+ 86400 IN SRV 7 9 80 www.movie7.edu.
+ 86400 IN SRV 8 10 80 www.movie8.edu.
+ 86400 IN SRV 9 11 80 www.movie9.edu.
+ 86400 IN SRV 10 12 80 www.movie10.edu.
+ 86400 IN SRV 11 13 80 www.movie11.edu.
+ 86400 IN SRV 12 14 80 www.movie12.edu.
+ 86400 RRSIG SRV 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ogZhDnKwKdVkMzz5tdzNb4g6Ob/eLvTYKDrl
+ V+lI1twfv79u2wRrrDGMPopIoqt2R0L8TCFd
+ 6R9ZRGuqaXnH7RJO7dcMrPtfqaOXrfHobxI3
+ /869BlqTZTHh5KXAnxc2ecLPMz5ZKRvbu0Z3
+ a3+Ltis8XxQbA06VsgpPhxsEWc8= )
+ 3600 NSEC multiple-type-sshfp-record.example.com. SRV RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ lMSeqTve8vhMIrgCXFvFTvmV2oGxXtTTowxB
+ WK7WSwXBc3HI/sXPLlH4WkM9lYG0A7W/RtnI
+ Q1rmeiTCGr/Jnedn6fDfi3drLPxN1PktZyiT
+ aqguYMh5O8o7Bn3WDF7jsHoTCO2RBWVu/C5P
+ mOCIbM/bI0+Pz+Fr+bQXufBk/wU= )
+test.non-terminal.multiple-type-txt-record.example.com. 86400 IN TXT "txt::1"
+ 86400 IN TXT "txt::2"
+ 86400 IN TXT "txt::3"
+ 86400 IN TXT "txt::4"
+ 86400 IN TXT "txt::5"
+ 86400 IN TXT "txt::6"
+ 86400 IN TXT "txt::7"
+ 86400 IN TXT "txt::8"
+ 86400 IN TXT "txt::9"
+ 86400 IN TXT "txt::10"
+ 86400 IN TXT "txt::11"
+ 86400 IN TXT "txt::12"
+ 86400 IN TXT "txt::13"
+ 86400 IN TXT "txt::14"
+ 86400 IN TXT "txt::15"
+ 86400 IN TXT "txt::16"
+ 86400 IN TXT "txt::17"
+ 86400 IN TXT "txt::18"
+ 86400 IN TXT "txt::19"
+ 86400 IN TXT "txt::20"
+ 86400 IN TXT "txt::21"
+ 86400 IN TXT "txt::22"
+ 86400 IN TXT "txt::23"
+ 86400 IN TXT "txt::24"
+ 86400 IN TXT "txt::25"
+ 86400 IN TXT "txt::26"
+ 86400 IN TXT "txt::27"
+ 86400 IN TXT "txt::28"
+ 86400 IN TXT "txt::29"
+ 86400 IN TXT "txt::30"
+ 86400 IN TXT "txt::31"
+ 86400 RRSIG TXT 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ EI+oudyovCkHhDkHJO5B8KWQkKBowxFVXgPE
+ Aglas+tpdVlsyJ0DrOc3z1vOoJXnKW85SUH0
+ 6DkPd+0PAmSraPNKHZmiL1onBgWjZgoXl4vD
+ NTTnBpnRrHTmxXF50x/j0tBwnSrIwqWW/exk
+ kkjthFL9jS/fJfSve2PHGqrZhtY= )
+ 3600 NSEC multiple-type-wks-record.example.com. TXT RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ jSP0eD1wN/KI8yAI4a3JqUXdN2a3zZVxdYgg
+ XaheCG4ftdh5RLMfZzZygx4OAFM3i9xKH2ul
+ sS8fyDcNx2yC8K5apt9CvQERQK6DgzvOMLv0
+ w/SJ9r7VsPwGQYGFkgL5lM/8j+OT8yWfqHKV
+ UqRVp2N9llMBgsZvONbcOAgk5y0= )
+multiple-type-wks-record.example.com. 86400 IN WKS 218.241.109.1 6 23 25 53
+ 86400 IN WKS 218.241.109.2 6 23 25 53
+ 86400 IN WKS 218.241.109.3 6 23 25 53
+ 86400 IN WKS 218.241.109.4 6 23 25 53
+ 86400 IN WKS 218.241.109.5 6 23 25 53
+ 86400 IN WKS 218.241.109.6 6 23 25 53 6000
+ 86400 IN WKS 218.241.109.7 6 23 25 53
+ 86400 IN WKS 218.241.109.8 6 23 25 53
+ 86400 IN WKS 218.241.109.9 6 23 25 53
+ 86400 IN WKS 218.241.109.10 6 23 25 53
+ 86400 IN WKS 218.241.109.11 6 22 23 25 53
+ 86400 IN WKS 218.241.109.12 6 22 23 25 53
+ 86400 IN WKS 218.241.109.13 6 22 23 25 53
+ 86400 IN WKS 218.241.109.14 6 22 25 53 80
+ 86400 IN WKS 218.241.109.15 6 22 25 53 80
+ 86400 IN WKS 218.241.109.16 6 22 25 80 6000
+ 86400 RRSIG WKS 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Ay1hCHvEA0uCa7qBEi0ABZrf3qsAswLbfBoA
+ ep+6yXF1e0Yv7nQPVlc9qJT1yoDLgVdNzpO4
+ O7DyZ/sWVjoXxFu5pAiKY+X4rgr4XT48NuBm
+ 0uzE2u9fNS/fi8lZoozhpWgGCLvP9+96pjGk
+ T9WMZOh9qPCLs8O0f3VFK+KLlzE= )
+ 3600 NSEC test.non-terminal.multiple-type-wks-record.example.com. WKS RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ pzOvjfjiGC9VJvwetU61b5K90963EiKAeTou
+ zgBFZslR6OYGDUeoYNuYi9CDJPyOctb8NAsq
+ 6EZXU8WBAGAPJbXp3+VtWYVX8Cu7nI1fMdbA
+ KUA5GCiUBlNp/w75unen6fQ9imtblyjg+/XC
+ kIlek+vR08q1TBM3WUbKVYUinGU= )
+test.non-terminal.multiple-type-wks-record.example.com. 86400 IN WKS 218.241.109.1 6 23 25 53
+ 86400 IN WKS 218.241.109.2 6 23 25 53
+ 86400 IN WKS 218.241.109.3 6 23 25 53
+ 86400 IN WKS 218.241.109.4 6 23 25 53
+ 86400 IN WKS 218.241.109.5 6 23 25 53
+ 86400 IN WKS 218.241.109.6 6 23 25 53 6000
+ 86400 IN WKS 218.241.109.7 6 23 25 53
+ 86400 IN WKS 218.241.109.8 6 23 25 53
+ 86400 IN WKS 218.241.109.9 6 23 25 53
+ 86400 IN WKS 218.241.109.10 6 23 25 53
+ 86400 IN WKS 218.241.109.11 6 22 23 25 53
+ 86400 IN WKS 218.241.109.12 6 22 23 25 53
+ 86400 IN WKS 218.241.109.13 6 22 23 25 53
+ 86400 IN WKS 218.241.109.14 6 22 25 53 80
+ 86400 IN WKS 218.241.109.15 6 22 25 53 80
+ 86400 IN WKS 218.241.109.16 6 22 25 80 6000
+ 86400 RRSIG WKS 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ bh+t72rURTD/AUNkK7tSgUNR1MfoksE2IlYg
+ bVwdLtmoIZdG6bH1ZjGI+EqxFL+jDGLiCHC/
+ HvZ8zc2QEjDYCvIvWU1NdaeVFAxqLNSDuz8g
+ d4l47H99JPTQgzRTGynfuJndDzFo76QEwSph
+ PhPQMgp2LQekn9IJ09ld+afY+4Q= )
+ 3600 NSEC NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com. WKS RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ O50OPaNiLok8UClIxTBPo9b/yE/3Ouyp2u4j
+ 6WPPzEMBqYb2fZ5YTlF89hJBcR9COabqc6Zz
+ yJnU0cyKBWu8r7kIcuIDCb0BrPN3/50b18CQ
+ qp1oarmS8E1SU+yPkPLbJzQcDiZpSb89Uqpe
+ pNDtVGe8SkYkcgzHI0jOnRCrSSQ= )
+test.non-terminal.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com. 86400 IN NS ns1.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com.
+ns1.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com. 86400 IN A 192.168.2.1
+test.non-terminal.ns1.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com. 86400 IN A 192.168.2.1
+NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com. 86400 IN NS ns1.NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.example.com.
+ 3600 NSEC testa.no-type.example.com. NS RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ AeXmrYa9++hgSgWI8qLyDG/6HnVUFGjv/Jh3
+ Sbcbz/Wkt35RywxgixoMPizs3rxlEmy/jBCC
+ C3p4cBQIhSZ2vOsExbEpadNbbCNaI0Qilwx3
+ LSOScfgBKeUdpDjaUnJPEgd8EbMDjBGDhySf
+ KOki0VRaVnqP6EFLtlFyf1wQXto= )
+multiple-type-txt-record.example.com. 86400 IN TXT "txt::1"
+ 86400 IN TXT "txt::2"
+ 86400 IN TXT "txt::3"
+ 86400 IN TXT "txt::4"
+ 86400 IN TXT "txt::5"
+ 86400 IN TXT "txt::6"
+ 86400 IN TXT "txt::7"
+ 86400 IN TXT "txt::8"
+ 86400 IN TXT "txt::9"
+ 86400 IN TXT "txt::10"
+ 86400 IN TXT "txt::11"
+ 86400 IN TXT "txt::12"
+ 86400 IN TXT "txt::13"
+ 86400 IN TXT "txt::14"
+ 86400 IN TXT "txt::15"
+ 86400 IN TXT "txt::16"
+ 86400 IN TXT "txt::17"
+ 86400 IN TXT "txt::18"
+ 86400 IN TXT "txt::19"
+ 86400 IN TXT "txt::20"
+ 86400 IN TXT "txt::21"
+ 86400 IN TXT "txt::22"
+ 86400 IN TXT "txt::23"
+ 86400 IN TXT "txt::24"
+ 86400 IN TXT "txt::25"
+ 86400 IN TXT "txt::26"
+ 86400 IN TXT "txt::27"
+ 86400 IN TXT "txt::28"
+ 86400 IN TXT "txt::29"
+ 86400 IN TXT "txt::30"
+ 86400 IN TXT "txt::31"
+ 86400 RRSIG TXT 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ e7Y+piYf7OeKFSgsjUABzPA8Io5ZRNv+euzl
+ 4LY8VvzcVsORAITUUVGhBM21MK7qGOvJ3uIZ
+ 9rh6Ojv4XAx8fzBLMaGLJLTUeZjC2CASD1t2
+ Sahbxvmmv1/Ikj7lSe8hsLjp85yuZ4KWKryl
+ a6kK+QW2/3IvVHF0P5T35EiZ158= )
+ 3600 NSEC test.non-terminal.multiple-type-txt-record.example.com. TXT RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ nN89y/xJwgkmhrr6HsGqcKubezzlYDVQJI2J
+ 7vXUFik+AVZwsJfoVmfnX87osJX+wrYzVG1w
+ RtUN0L57zc7O+SYfvb00OjeQjIwYfEQprhm+
+ orrbmStdGehT+qSeGoDitupTJYgY0rUpVb9o
+ ap0qcZWH7QBNdTQrqdwTXj0oaQ4= )
+multiple-type-srv-record.example.com. 86400 IN SRV 0 2 80 www.movie0.edu.
+ 86400 IN SRV 1 3 80 www.movie1.edu.
+ 86400 IN SRV 2 4 80 www.movie2.edu.
+ 86400 IN SRV 3 5 80 www.movie3.edu.
+ 86400 IN SRV 4 6 80 www.movie4.edu.
+ 86400 IN SRV 5 7 80 www.movie5.edu.
+ 86400 IN SRV 6 8 80 www.movie6.edu.
+ 86400 IN SRV 7 9 80 www.movie7.edu.
+ 86400 IN SRV 8 10 80 www.movie8.edu.
+ 86400 IN SRV 9 11 80 www.movie9.edu.
+ 86400 IN SRV 10 12 80 www.movie10.edu.
+ 86400 IN SRV 11 13 80 www.movie11.edu.
+ 86400 IN SRV 12 14 80 www.movie12.edu.
+ 86400 RRSIG SRV 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ LYlM0amkBW4UvNWm/dJEqk4BDDNndLZ11kdF
+ 03qFuHTgXsRqmwWFuZo5y4pQ3r7Zvt1T9EgS
+ 1JtftwnqmtdD1017G8Te8nOtNIWZZfEL6Jbr
+ FFbPoNY6o5ATSCZ+qMVsyPAcPZy4LUZlbnQc
+ In0YPMUnSuIqmawOwkHuhwN4okI= )
+ 3600 NSEC test.non-terminal.multiple-type-srv-record.example.com. SRV RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ tcZ7KwV1sX6P/tBKrok6DTp5AqJDHvXioeO5
+ RIXmfQyPQLWsc2i3EokkABiFz80PGARx2BEd
+ RxZUxkHXhCaoVXkQHDih+90uOVEOJIBMmd6e
+ KLfF2k0cmaUwP2ZrUcXV1i6hz04ouyWj3ZS8
+ 9+vibTjaKHRQfFHd8pj2+KbMuE8= )
+NS1.example.com. 86400 IN A 218.241.111.236
+ 86400 RRSIG A 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ rqS35MJl9mPPZiLU3qRIxBLxO7vGn2lDoh3f
+ 4YXB8t0bKCxvldlINNermQ+3filYHqxst4DF
+ 2baZCXU8bTpBEr2gtrhRp5/kDan85kTicZzj
+ tnJgdtDEdK7QBIcxtC6W/UoZAskINhfHEAvp
+ VrfeGxq9FB/ZM7mA4RAJbd9nTIw= )
+ 3600 NSEC sub.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ kvH+Drx2kDQXzhuXyscBF12sQ4BXeuUwPb4t
+ oj6rddztnktJV8Y6w3JLBDwIIUxw/ciqrRt0
+ M5AO14ujKlGgeG90Q8By9em2T7X16zYfLKIt
+ Dvi1VWmggPHVec1TeX9IqNs6ub/bK2MZ4T+x
+ +ehCHwojKTP8Y/b4nvx/kvskhBM= )
+test.non-terminal.sub.example.com. 86400 IN NS ns.sub.example.com.
+ns.sub.example.com. 86400 IN A 218.241.111.236
+test.non-terminal.ns.sub.example.com. 86400 IN A 218.241.111.236
+sub.example.com. 86400 IN NS ns.sub.example.com.
+ 86400 DS 49822 5 1 (
+ 6E7920E917F926300EDFB5F61B57588CF2B5
+ 2ADF )
+ 86400 DS 49822 5 2 (
+ 3EBE7AF8D46657F51149714AC5FF2FB23F5A
+ D8258A2229764475C86C9B5B1A59 )
+ 86400 RRSIG DS 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ ZAl7uANYIwncfhAV+AGiunvt4BflavkEnJGV
+ KXPgcLdpPCsBTLCXPbyAqxiu0ywgXQ5JuIHm
+ AF0AKzeE+abEiPipP816jCi3rQiSljI/zwHI
+ t4fcB5zvM0yBE0HkAVP+ftNbda2RvF8cjXH4
+ aPieL89lCcC3zuVwEo1NZIommKo= )
+ 3600 NSEC sub-cname.example.com. NS DS RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ nesi4ehQeGU8evobfldaepUGLmgw57nf2Wg/
+ F/A6TkmbgnVoWWPkT4KBSMgTmuym2TfFq7AT
+ b6hIsVh+OYToioFZha6UwrIgtiqxcplgCKwY
+ WB/tLtprKcTMlcaPWTtgzA18m/j0p7enlL13
+ kAJxjJIbKD4YKIRC0yWzlgk/Grc= )
+sub-cname.example.com. 86400 IN CNAME www.sub.example.com.
+ 86400 RRSIG CNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ aWW0K9aKL99ZOTnljdoWQxANkr5aZ0velC9D
+ WOob680cmhJ+8OlQVMNbcMZyPU7Zr8nHKAXe
+ 0On+72WLqsiGEQfZvBvMbm70R5jrEbtfFlL2
+ 8UdkIzkMPJf+IJlg+qFdZk7w1i6xlRxDOOKB
+ vZgZQwD3eTO2971kyypBYK+/4aU= )
+ 3600 NSEC test.non-terminal.sub-cname.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ AtfbnSLONvl0EB0k7oNBEqQCL3WIrsCmf+sn
+ CB5AOLE7/lWzsLm0vCN8lbrOXhqzg6iswPVO
+ vEvxkqpuA9I0iObyiqwYNfo7Dr1DWKSTjq+b
+ WdjKHw3LZ9s1BbrJX6Z5qtAU0I/22Tia4OMi
+ UFn0sPWOka7LNW8imGdsjV6K9cI= )
+test.non-terminal.sub-cname.example.com. 86400 IN CNAME www.sub.example.com.
+ 86400 RRSIG CNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ cvcWxilQsUeAl698HIsB+hfZO4jeYJHTSGCC
+ Y1eX8QEPox2LLmzkuB+j4VnBq9p0bOyg5S1r
+ FS4I8piCKJNt0xmCnXsVgaeEZ1uwBmkXN5nH
+ C9ba32o3aYUwEyAX/TJyNnKjc5Hl1Z8ePFOX
+ 05KRjn0Q9DjYsICA7F7ZMVYIWhE= )
+ 3600 NSEC sub-dname.example.com. CNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ M4TFyUkhvxkTPg+VD6v04O3DN5W15itA7iXI
+ 2JbePRx0MSK1nMx+tgKpYu/6hmsMMTcKpwph
+ TtZf+ieQTD04YoCk8hOm/QgHHVbt1HM47wMZ
+ AjdMosBvTAzv3Os1I1tENctPLPSCEdWUbmuI
+ JvmKiUG8Zn/9dvadT8rWu58kBOE= )
+sub-dname.example.com. 86400 IN DNAME sub.example.com.
+ 86400 RRSIG DNAME 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ pI27ntzYV8TIzdk9ObcvyV5VTdiMjCb0IPFi
+ gt/R2MXT9p5c/am6PyO6npjbxFIvibIyUvZg
+ YTtcN1vkE/d4RpgIZtaT0Zc7SNobXz1xVWXM
+ kOA7sK4cdI+7hv7bcQbrowXyF2TJnxO8FK8d
+ JWngaQS23xT4dSujJ5Q+gHP6kGQ= )
+ 3600 NSEC test.non-terminal.sub-dname.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ z67GTr+8vik0Gc+xI2efUrBZD4xw/4bb9dzP
+ AmnqEtIcuUdZnpBybe4KlwvbtYpalkF0lil2
+ P7zFNhMHc8Afb5aPLTUBQQFGo5XRZABzo83g
+ EnCmK+wCnGT50EZMJq5DZBRleCsynVd+ltKV
+ ZD+dVp/NwpQXJC7GRiotTK1oh8U= )
+test.non-terminal.sub-dname.example.com. 86400 IN DNAME sub.example.com.
+ 86400 RRSIG DNAME 5 5 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ hn45X0G/jwq6aqv1b6R9gS+la+mJUE8tpnQa
+ Sn1dk4Bcrf6tkI5GHvL/CeQTVAMfaKlnbnNE
+ wduF99rru4LzAz0kI4I70Oo0R2TfqzHjCD7P
+ BgtOg5rym8Z0gVA8DYlXQ6fwZvxzsoY9nuGR
+ P2yDYo4nY1kTTttzi41KIoYI0+Q= )
+ 3600 NSEC www.a.b.sub2.example.com. DNAME RRSIG NSEC
+ 3600 RRSIG NSEC 5 5 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ RejBSjNeUjtNBFw0N7tnaDXlDddCiAQIBlKS
+ swczhQxS72crD1vnPh4lg2TAxSytTlXUeF60
+ VRKmD5C+7zXMcKhukzTipWjLFaaQr83VfAUa
+ htA2UN1fNu2AGBWdZKJvKc18AtDNsgdwbxKY
+ 6zGg9r8lDdDv8yrt1EhVlETC9eo= )
+www.a.b.sub2.example.com. 86400 IN A 192.168.1.1
+ 86400 RRSIG A 5 6 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ OI3kSrtJbMMv+qbScv46yi9qfSupzUvD7R28
+ T5KBKUe1lIBM/nP3w4W/WUoYnG+ao5XNdSN3
+ BuxYOd1gDOMr3ZSSbzAMOvoOviVSxbW1X3Lm
+ P0/nEHkQF9L0UygDn4qnZg56Tmt5VGHDWk7U
+ HZdJ3Sa6jMX+aPKPbchyaG5wiqA= )
+ 3600 NSEC test.non-terminal.www.a.b.sub2.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 6 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ wTJX72cgdq0aNwh5MpKg5N3lpMxdI7oaEqjQ
+ 7LFdlNhge9pKGFdq4qpvKrqqt8H8evlW5CMB
+ ZtDV58SY8mLuNoJZt/ZEkc5BAe6SEF/Jshzs
+ AcZenhh2LhXRC9yTbIc2uUCFOf/t7w5DZRSF
+ qgLA4tfw0I8tlndjbZsd7owPixI= )
+subdn.example.com. 86400 IN NS ns.subdn.cn.
+ 3600 NSEC subdomain.example.com. NS RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ nCfBP/IRCUZJEG8TyfP13MrhQZOXPsEPZWdd
+ qLQkrXgywK4whngLQBC7RvmHJrmjZFhfNIGM
+ ATfsP6Ii5BgqQDrjxqvftMNta81rTxnXZEOb
+ I6wgo+F4OlFZ0esMgdNTtsVawXV5rxMCL3hA
+ a0HXZrxSp+ZOrn11rOYGKZ3XvXs= )
+test.non-terminal.subdn.example.com. 86400 IN NS ns.subdn.cn.
+testa.no-type.example.com. 86400 IN A 10.1.1.1
+ 86400 RRSIG A 5 4 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ aKBQAJSupqxsqeTrD/UTweU75M2V2wv8bLlw
+ yqp3FnzpLUeakTS4a6Y0qtvzumUohe1gmHj0
+ DQTtLMRaxNQcIleU02fCUI+d9BUh4xLhYevD
+ vP/t2k/zKoLWuVttDkQBarpifVq8axpguPEl
+ TT/kRyzk+VCpCzQSK4rik59OXA4= )
+ 3600 NSEC testaaaa.no-type.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 4 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ mSGRBg1l+w1qsFWrIt3tFNT2S4OOHteoiaJH
+ K+T2Smsy7o3OdJ9mL3F1Hh35QXDy4HDTjib3
+ 3odGn+4dot3ZY3aaafUW9y5Hdqprf+T0u8Ir
+ m3THIDMQB23pviwXZq9yhykzTKFFVPUL1Gat
+ sLleGYbv4X3R+cZ8aafVs1bGxaE= )
+test.non-terminal.subdomain.example.com. 86400 IN NS ns1.mysub.com.
+ 86400 IN NS ns2.mysub.com.
+test.non-terminal.www.a.b.sub2.example.com. 86400 IN A 192.168.1.1
+ 86400 RRSIG A 5 8 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ d//X2Ycwf5qOjOPnuGu3VHqIwDgnrTFqVMBq
+ jZn3CtYDE0hOTjwZNYyisTV+XJjUQZWko6XG
+ Usu3xKvyoxjLYQygY9nhzBMZG9lYvobJAHLw
+ 2Cd7QmfgwSzLKDVPh3ONyZy/BbZmnMubaWwO
+ k9EO1hGarRPIfvSi14bTa+VfEqM= )
+ 3600 NSEC subdn.example.com. A RRSIG NSEC
+ 3600 RRSIG NSEC 5 8 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ CTVkFc2+9gdljg6aNEN7dhDqqtZJXz8juhOt
+ lM4iNhwMWUy2kTEfGC3koSp8aVG/h+NIMH5m
+ lPDX+4BYM2XtLC23SWyyc/s/nnUDqTPw7PAL
+ R0/+0Rcv8YGk0N35G5jX+LBN2tXGNNMuvThM
+ p7jCSbFwO8Y7H8rlMf5i6Ygunu4= )
+testaaaa.no-type.example.com. 86400 IN AAAA ::1
+ 86400 RRSIG AAAA 5 4 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ eWUKLr3s4Flt/t8FjM5q7cRykBc/FNFXwTN9
+ pq2ucDrkuiLfe5ej4MNe6aNO6EOncRWVH6Qa
+ uJ0nisOuUZg2OVFIEHqLP1YF58O/xlhK3dDb
+ 1kAIr1+cM3CEkN0l3AJ6yd92j6P0611Yu26s
+ 7JlF3ZuX52VZpve9N+uk5uEsW/o= )
+ 3600 NSEC NS1.example.com. AAAA RRSIG NSEC
+ 3600 RRSIG NSEC 5 4 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ FcHjt3C/q4MU3ZV2q7FoF4zsop0qJDf9sR7y
+ Bx5pqk6AvXKcrpqUMIHlGlRxE5MJHi4bGBTi
+ Hyr3tZ6eXBVPqM+PBm3iT4HqnHGkwJm1dFWj
+ bayBY/7q4LTyQhReP3PaZjApkhqv3ehIlG1J
+ zuIHw5Axvw3bTzexBUlNgmDvrNQ= )
+subdomain.example.com. 86400 IN NS ns1.mysub.com.
+ 86400 IN NS ns2.mysub.com.
+ 86400 DS 6613 5 1 (
+ 39A2A64CA73B103E983EE77305E8DAEE9DBB
+ B0FA )
+ 86400 DS 6613 5 2 (
+ 8D24DBE7FEA6E40A27382B45EE189FBF30B0
+ 0FB38734BA5415A7695B11FE5CE7 )
+ 86400 RRSIG DS 5 3 86400 20110408061651 (
+ 20110309061651 27366 example.com.
+ Hsr5Z2S0t6NrbGYI69ynQyZYZfujoq4ANuCs
+ NqRkWa4fVQQ8uJr04pCAe2Dzm3R1dWyAHOAM
+ kUtt7GwYYH/hjjs4K3E0h63cqAkbxmyOdss/
+ lelPaVAGt66D5QSE1SRXKGKnQUD+sJM0plkz
+ 1KrMV/PNibJ1R3U8hYYtwh+gAyE= )
+ 3600 NSEC example.com. NS DS RRSIG NSEC
+ 3600 RRSIG NSEC 5 3 3600 20110408061651 (
+ 20110309061651 27366 example.com.
+ EWh5yFAxcePS0iGoif8JFj4ZvWuPtXSguzrB
+ OEl8BNMY4KeMVmwBLoGRpECKpybqqTXB/His
+ 7mejU9HzP47cNrD0eV73UvbHe0qc2DlgfL5Q
+ hXVMdQJZ0GJO4r6HNwoyMxOPZMXnDSijjVkG
+ UikyHQsGCfF7um8cw2KBmp73xqI= )
diff --git a/tools/system_messages.py b/tools/system_messages.py
new file mode 100644
index 0000000..7b0d60c
--- /dev/null
+++ b/tools/system_messages.py
@@ -0,0 +1,419 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# Produce System Messages Manual
+#
+# This tool reads all the .mes files in the directory tree whose root is given
+# on the command line and interprets them as BIND 10 message files. It pulls
+# all the messages and description out, sorts them by message ID, and writes
+# them out as a single (formatted) file.
+#
+# Invocation:
+# The code is invoked using the command line:
+#
+# python system_messages.py [-o <output-file>] <top-source-directory>
+#
+# If no output file is specified, output is written to stdout.
+
+import re
+import os
+import sys
+from optparse import OptionParser
+
+# Main dictionary holding all the messages. The messages are accumulated here
+# before being printed in alphabetical order.
+dictionary = {}
+
+# The structure of the output page is:
+#
+# header
+# message
+# separator
+# message
+# separator
+# :
+# separator
+# message
+# trailer
+#
+# (Indentation is not relevant - it has only been added to the above
+# illustration to make the structure clearer.) The text of these section is:
+
+# Header - this is output before anything else.
+SEC_HEADER="""<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+<!ENTITY mdash "—" >
+<!ENTITY % version SYSTEM "version.ent">
+%version;
+]>
+<!--
+ This XML document is generated using the system_messages.py tool
+ based on the .mes message files.
+
+ Do not edit this file.
+-->
+<book>
+ <?xml-stylesheet href="bind10-guide.css" type="text/css"?>
+
+ <bookinfo>
+ <title>BIND 10 Messages Manual</title>
+
+ <copyright>
+ <year>2011</year><holder>Internet Systems Consortium, Inc.</holder>
+ </copyright>
+
+ <abstract>
+ <para>BIND 10 is a Domain Name System (DNS) suite managed by
+ Internet Systems Consortium (ISC). It includes DNS libraries
+ and modular components for controlling authoritative and
+ recursive DNS servers.
+ </para>
+ <para>
+ This is the messages manual for BIND 10 version &__VERSION__;.
+ The most up-to-date version of this document, along with
+ other documents for BIND 10, can be found at
+ <ulink url="http://bind10.isc.org/docs"/>.
+ </para>
+ </abstract>
+
+ <releaseinfo>This is the messages manual for BIND 10 version
+ &__VERSION__;.</releaseinfo>
+ </bookinfo>
+
+ <chapter id="intro">
+ <title>Introduction</title>
+ <para>
+ This document lists each message that can be logged by the
+ programs in the BIND 10 package. Each entry in this manual
+ is of the form:
+ <screen>IDENTIFICATION message-text</screen>
+ ... where "IDENTIFICATION" is the message identification included
+ in each message logged and "message-text" is the accompanying
+ message text. The "message-text" may include placeholders of the
+ form "%1", "%2" etc.; these parameters are replaced by relevant
+ values when the message is logged.
+ </para>
+ <para>
+ Each entry is also accompanied by a description giving more
+ information about the circumstances that result in the message
+ being logged.
+ </para>
+ <para>
+ For information on configuring and using BIND 10 logging,
+ refer to the <ulink url="bind10-guide.html">BIND 10 Guide</ulink>.
+ </para>
+ </chapter>
+
+ <chapter id="messages">
+ <title>BIND 10 Messages</title>
+ <para>
+ <variablelist>
+"""
+
+# This is output once for each message. The string contains substitution
+# tokens: $I is replaced by the message identification, $T by the message text,
+# and $D by the message description.
+SEC_MESSAGE = """<varlistentry id="$I">
+<term>$I $T</term>
+<listitem><para>
+$D
+</para></listitem>
+</varlistentry>"""
+
+# A description may contain blank lines intended to separate paragraphs. If so,
+# each blank line is replaced by the following.
+SEC_BLANK = "</para><para>"
+
+# The separator is copied to the output verbatim after each message except
+# the last.
+SEC_SEPARATOR = ""
+
+# The trailier is copied to the output verbatim after the last message.
+SEC_TRAILER = """ </variablelist>
+ </para>
+ </chapter>
+</book>"""
+
+
+def reportError(filename, what):
+ """Report an error and exit"""
+ print("*** ERROR in ", filename, file=sys.stderr)
+ print("*** REASON: ", what, file=sys.stderr)
+ print("*** System message generator terminating", file=sys.stderr)
+ sys.exit(1)
+
+
+
+def replaceTag(string):
+ """Replaces the '<' and '>' in text about to be inserted into the template
+ sections above with < and > to avoid problems with message text
+ being interpreted as XML text.
+ """
+ string1 = string.replace("<", "<")
+ string2 = string1.replace(">", ">")
+ return string2
+
+
+
+def replaceBlankLines(lines):
+ """Replaces blank lines in an array with the contents of the 'blank'
+ section.
+ """
+ result = []
+ for l in lines:
+ if len(l) == 0:
+ result.append(SEC_BLANK)
+ else:
+ result.append(l)
+
+ return result
+
+
+
+# Printing functions
+def printHeader():
+ print(SEC_HEADER)
+
+def printSeparator():
+ print(SEC_SEPARATOR)
+
+def printMessage(msgid):
+ # In the message ID, replace "<" and ">" with XML-safe versions and
+ # substitute into the data.
+ m1 = SEC_MESSAGE.replace("$I", replaceTag(msgid))
+
+ # Do the same for the message text.
+ m2 = m1.replace("$T", replaceTag(dictionary[msgid]['text']))
+
+ # Do the same for the description then replace blank lines with the
+ # specified separator. (We do this in that order to avoid replacing
+ # the "<" and ">" in the XML tags in the separator.)
+ desc1 = [replaceTag(l) for l in dictionary[msgid]['description']]
+ desc2 = replaceBlankLines(desc1)
+
+ # Join the lines together to form a single string and insert into
+ # current text.
+ m3 = m2.replace("$D", "\n".join(desc2))
+
+ print(m3)
+
+def printTrailer():
+ print(SEC_TRAILER)
+
+
+
+def removeEmptyLeadingTrailing(lines):
+ """Removes leading and trailing empty lines.
+
+ A list of strings is passed as argument, some of which may be empty.
+ This function removes from the start and end of list a contiguous
+ sequence of empty lines and returns the result. Embedded sequence of
+ empty lines are not touched.
+
+ Parameters:
+ lines List of strings to be modified.
+
+ Return:
+ Input list of strings with leading/trailing blank line sequences
+ removed.
+ """
+
+ retlines = []
+
+ # Dispose of degenerate case of empty array
+ if len(lines) == 0:
+ return retlines
+
+ # Search for first non-blank line
+ start = 0
+ while start < len(lines):
+ if len(lines[start]) > 0:
+ break
+ start = start + 1
+
+ # Handle case when entire list is empty
+ if start >= len(lines):
+ return retlines
+
+ # Search for last non-blank line
+ finish = len(lines) - 1
+ while finish >= 0:
+ if len(lines[finish]) > 0:
+ break
+ finish = finish - 1
+
+ retlines = lines[start:finish + 1]
+ return retlines
+
+
+
+def addToDictionary(msgid, msgtext, desc, filename):
+ """Add the current message ID and associated information to the global
+ dictionary. If a message with that ID already exists, loop appending
+ suffixes of the form "(n)" to it until one is found that doesn't.
+
+ Parameters:
+ msgid Message ID
+ msgtext Message text
+ desc Message description
+ filename File from which the message came. Currently this is
+ not used, but a future enhancement may wish to include the
+ name of the message file in the messages manual.
+ """
+
+ # If the ID is in the dictionary, append a "(n)" to the name - this wil
+ # flag that there are multiple instances. (However, this is an error -
+ # each ID should be unique in BIND-10.)
+ if msgid in dictionary:
+ i = 1
+ while msgid + " (" + str(i) + ")" in dictionary:
+ i = i + 1
+ msgid = msgid + " (" + str(i) + ")"
+
+ # Remove leading and trailing blank lines in the description, then
+ # add everything into a subdictionary which is then added to the main
+ # one.
+ details = {}
+ details['text'] = msgtext
+ details['description'] = removeEmptyLeadingTrailing(desc)
+ details['filename'] = filename
+ dictionary[msgid] = details
+
+
+
+def processFileContent(filename, lines):
+ """Processes file content. Messages and descriptions are identified and
+ added to a dictionary (keyed by message ID). If the key already exists,
+ a numeric suffix is added to it.
+
+ Parameters:
+ filename Name of the message file being processed
+ lines Lines read from the file
+ """
+
+ prefix = "" # Last prefix encountered
+ msgid = "" # Last message ID encountered
+ msgtext = "" # Text of the message
+ description = [] # Description
+
+ for l in lines:
+ if l.startswith("$"):
+ # Starts with "$". Ignore anything other than $PREFIX
+ words = re.split("\s+", l)
+ if words[0].upper() == "$PREFIX":
+ if len(words) == 1:
+ prefix = ""
+ else:
+ prefix = words[1]
+
+ elif l.startswith("%"):
+ # Start of a message. Add the message we were processing to the
+ # dictionary and clear everything apart from the file name.
+ if msgid != "":
+ addToDictionary(msgid, msgtext, description, filename)
+
+ msgid = ""
+ msgtext = ""
+ description = []
+
+ # Start of a message
+ l = l[1:].strip() # Remove "%" and trim leading spaces
+ if len(l) == 0:
+ printError(filename, "Line with single % found")
+ next
+
+ # Split into words. The first word is the message ID
+ words = re.split("\s+", l)
+ msgid = (prefix + words[0]).upper()
+ msgtext = l[len(words[0]):].strip()
+
+ else:
+ # Part of a description, so add to the current description array
+ description.append(l)
+
+ # All done, add the last message to the global dictionaty.
+ if msgid != "":
+ addToDictionary(msgid, msgtext, description, filename)
+
+
+
+def processFile(filename):
+ """Processes a file by reading it in and stripping out all comments and
+ and directives. Leading and trailing blank lines in the file are removed
+ and the remainder passed for message processing.
+
+ Parameters:
+ filename Name of the message file to process
+ """
+ lines = open(filename).readlines();
+
+ # Trim leading and trailing spaces from each line, and remove comments.
+ lines = [l.strip() for l in lines]
+ lines = [l for l in lines if not l.startswith("#")]
+
+ # Remove leading/trailing empty line sequences from the result
+ lines = removeEmptyLeadingTrailing(lines)
+
+ # Interpret content
+ processFileContent(filename, lines)
+
+
+
+def processAllFiles(root):
+ """Iterates through all files in the tree starting at the given root and
+ calls processFile for all .mes files found.
+
+ Parameters:
+ root Directory that is the root of the BIND-10 source tree
+ """
+ for (path, dirs, files) in os.walk(root):
+
+ # Identify message files
+ mes_files = [f for f in files if f.endswith(".mes")]
+
+ # ... and process each file in the list
+ for m in mes_files:
+ processFile(path + os.sep + m)
+
+
+# Main program
+if __name__ == "__main__":
+ parser = OptionParser(usage="Usage: %prog [--help | options] root")
+ parser.add_option("-o", "--output", dest="output", default=None,
+ metavar="FILE",
+ help="output file name (default to stdout)")
+ (options, args) = parser.parse_args()
+
+ if len(args) == 0:
+ parser.error("Must supply directory at which to begin search")
+ elif len(args) > 1:
+ parser.error("Only a single root directory can be given")
+
+ # Redirect output if specified (errors are written to stderr)
+ if options.output is not None:
+ sys.stdout = open(options.output, 'w')
+
+ # Read the files and load the data
+ processAllFiles(args[0])
+
+ # Now just print out everything we've read (in alphabetical order).
+ count = 1
+ printHeader()
+ for msgid in sorted(dictionary):
+ if count > 1:
+ printSeparator()
+ count = count + 1
+ printMessage(msgid)
+ printTrailer()
More information about the bind10-changes
mailing list